Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"zhwu95.riscv",
"mathematic.vscode-pdf",
"CraigMaslowski.erb",
"HowerLimited.udb-extension-pack-vscode"
"HowerLimited.udb-extension-pack-vscode",
"sorbet.sorbet-vscode-extension"
]
}
},
Expand Down
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ gem "pygments.rb"
gem "rake", "~> 13.0"
gem "rouge"
gem "ruby-progressbar", "~> 1.13"
gem "sorbet-runtime"
gem "treetop", "1.6.12"
gem "ttfunk", "1.7" # needed to avoid having asciidoctor-pdf dependencies pulling in a buggy version of ttunk (1.8)
gem "webrick"
Expand All @@ -31,4 +32,7 @@ group :development do
gem "ruby-prof"
gem "ruby-prof-flamegraph", git: "https://github.com/oozou/ruby-prof-flamegraph.git", ref: "fc3c437", require: false
gem "solargraph"
gem "sorbet"
gem "spoom"
gem "tapioca", require: false
end
38 changes: 38 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ GEM
reline (>= 0.3.8)
diff-lcs (1.6.0)
drb (2.2.1)
erubi (1.13.1)
hana (1.3.7)
hashery (2.1.2)
i18n (1.14.7)
Expand All @@ -89,6 +90,7 @@ GEM
logger (1.6.6)
matrix (0.4.2)
minitest (5.25.5)
netrc (0.11.0)
nkf (0.2.0)
nokogiri (1.18.6-aarch64-linux-gnu)
racc (~> 1.4)
Expand Down Expand Up @@ -126,6 +128,7 @@ GEM
pdf-reader (~> 2.0)
prawn (~> 2.2)
prettyprint (0.2.0)
prism (1.4.0)
psych (5.2.3)
date
stringio
Expand All @@ -134,6 +137,10 @@ GEM
racc (1.8.1)
rainbow (3.1.1)
rake (13.2.1)
rbi (0.3.1)
prism (~> 1.0)
rbs (>= 3.4.4)
sorbet-runtime (>= 0.5.9204)
rbs (3.9.1)
logger
rdbg (0.1.0)
Expand Down Expand Up @@ -189,7 +196,31 @@ GEM
tilt (~> 2.0)
yard (~> 0.9, >= 0.9.24)
yard-solargraph (~> 0.1)
sorbet (0.5.11966)
sorbet-static (= 0.5.11966)
sorbet-runtime (0.5.11966)
sorbet-static (0.5.11966-aarch64-linux)
sorbet-static (0.5.11966-x86_64-linux)
sorbet-static-and-runtime (0.5.11966)
sorbet (= 0.5.11966)
sorbet-runtime (= 0.5.11966)
spoom (1.6.1)
erubi (>= 1.10.0)
prism (>= 0.28.0)
rbi (>= 0.2.3)
sorbet-static-and-runtime (>= 0.5.10187)
thor (>= 0.19.2)
stringio (3.1.5)
tapioca (0.16.11)
benchmark
bundler (>= 2.2.25)
netrc (>= 0.11.0)
parallel (>= 1.21.0)
rbi (~> 0.2)
sorbet-static-and-runtime (>= 0.5.11087)
spoom (>= 1.2.0)
thor (>= 1.2.0)
yard-sorbet
thor (1.3.2)
tilt (2.6.0)
treetop (1.6.12)
Expand All @@ -208,6 +239,9 @@ GEM
yard (0.9.37)
yard-solargraph (0.1.0)
yard (~> 0.9)
yard-sorbet (0.9.0)
sorbet-runtime
yard

PLATFORMS
aarch64-linux-gnu
Expand All @@ -234,6 +268,10 @@ DEPENDENCIES
ruby-prof-flamegraph!
ruby-progressbar (~> 1.13)
solargraph
sorbet
sorbet-runtime
spoom
tapioca
treetop (= 1.6.12)
ttfunk (= 1.7)
webrick
Expand Down
41 changes: 27 additions & 14 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# frozen_string_literal: true
# typed: true

require "sorbet-runtime"
T.bind(self, T.all(Rake::DSL, Object))
extend T::Sig

Encoding.default_external = "UTF-8"

Expand Down Expand Up @@ -30,8 +35,8 @@ directory "#{$root}/.stamps"

# @param config_locator [String or Pathname]
# @return [ConfiguredArchitecture]
sig { params(config_locator: T.any(String, Pathname)).returns(ConfiguredArchitecture) }
def cfg_arch_for(config_locator)
raise ArgumentError, "expecting String or Pathname" unless config_locator.is_a?(String) || config_locator.is_a?(Pathname)
config_locator = config_locator.to_s

$cfg_archs ||= {}
Expand Down Expand Up @@ -156,21 +161,28 @@ namespace :serve do
end
end

sig { params(test_files: T::Array[String]).returns(String) }
def make_test_cmd(test_files)
"-Ilib:test -w -e 'require \"minitest/autorun\"; #{test_files.map{ |f| "require \"#{f}\""}.join("; ")}' --"
end

namespace :test do
# "Run the IDL compiler test suite"
task :idl_compiler do
t = Minitest::TestTask.new(:lib_test)
t.test_globs = ["#{$root}/lib/idl/tests/test_*.rb"]
t.process_env
ruby t.make_test_cmd
test_files = Dir["#{$root}/lib/idl/tests/test_*.rb"]
ruby make_test_cmd(test_files)
end

# "Run the Ruby library test suite"
task :lib do
t = Minitest::TestTask.new(:lib_test)
t.test_globs = ["#{$root}/lib/test/test_*.rb"]
t.process_env
ruby t.make_test_cmd
test_files = Dir["#{$root}/lib/test/test_*.rb"]

ruby make_test_cmd(test_files)
end

desc "Type-check the Ruby library"
task :sorbet do
sh "srb tc"
end
end

Expand All @@ -192,13 +204,13 @@ namespace :test do

cfg_arch = cfg_arch_for("_")
insts = cfg_arch.instructions
failed = false
failed = T.let(false, T::Boolean)
insts.each_with_index do |inst, idx|
[32, 64].each do |xlen|
next unless inst.defined_in_base?(xlen)

(idx...insts.size).each do |other_idx|
other_inst = insts[other_idx]
other_inst = T.must(insts[other_idx])
next unless other_inst.defined_in_base?(xlen)
next if other_inst == inst

Expand All @@ -220,13 +232,13 @@ namespace :test do

cfg_arch = cfg_arch_for("_")
csrs = cfg_arch.csrs
failed = false
failed = T.let(false, T::Boolean)
csrs.each_with_index do |csr, idx|
[32, 64].each do |xlen|
next unless csr.defined_in_base?(xlen)

(idx...csrs.size).each do |other_idx|
other_csr = csrs[other_idx]
other_csr = T.must(csrs[other_idx])
next unless other_csr.defined_in_base?(xlen)
next if other_csr == csr

Expand Down Expand Up @@ -271,7 +283,6 @@ def insert_warning(str, from)
first_line = lines.shift
lines.unshift(first_line, "\n# WARNING: This file is auto-generated from #{Pathname.new(from).relative_path_from($root)}").join("")
end
private :insert_warning

(3..31).each do |hpm_num|
file "#{$root}/arch/csr/Zihpm/mhpmcounter#{hpm_num}.yaml" => [
Expand Down Expand Up @@ -424,6 +435,8 @@ namespace :test do
Rake::Task["test:idl_compiler"].invoke
puts "UPDATE: Running test:lib"
Rake::Task["test:lib"].invoke
puts "UPDATE: Running test:sorbet"
Rake::Task["test:sorbet"].invoke
puts "UPDATE: Running test:schema"
Rake::Task["test:schema"].invoke
puts "UPDATE: Running test:idl"
Expand Down
146 changes: 145 additions & 1 deletion arch/csr/mie.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,151 @@ address: 0x304
priv_mode: M
length: MXLEN
definedBy: Sm
description: "mip.yaml#/description"
description: |
The `mie` and `mip` CSRs are MXLEN-bit read/write registers used when
the CLINT or PLIC interrupt controllers are present.
Note that the CLINT refers to an interrupt controller
used by some RISC-V implementations but isn't a ratified
RISC-V International standard.

The `mip` CSR contains information on pending interrupts, while `mie` is the corresponding
CSR containing interrupt enable bits.
Interrupt cause number _i_ (as reported in the `mcause` CSR)
corresponds to bit _i_ in both `mip` and `mie`.
Bits 15:0 are allocated to standard interrupt causes only, while
bits 16 and above are designated for platform use.

NOTE: Interrupts designated for platform use may be designated for custom use
at the platform's discretion.

An interrupt _i_ will trap to M-mode (causing the privilege mode to
change to M-mode) if all of the following are true:

* either the current privilege mode is M and the MIE bit in the `mstatus` register is
set, or the current privilege mode has less privilege than M-mode;
* bit _i_ is set in both `mip` and `mie`
* if register `mideleg` exists, bit _i_ is not set in `mideleg`.

These conditions for an interrupt trap to occur must be evaluated in a
bounded amount of time from when an interrupt becomes, or ceases to be,
pending in `mip`, and must also be evaluated immediately following the
execution of an __x__RET instruction or an explicit write to a CSR on
which these interrupt trap conditions expressly depend (including `mip`,
`mie`, `mstatus`, and `mideleg`).

Interrupts to M-mode take priority over any interrupts to lower
privilege modes.

Each individual bit in register `mip` may be writable or may be
read-only. When bit _i_ in `mip` is writable, a pending interrupt _i_
can be cleared by writing 0 to this bit. If interrupt _i_ can become
pending but bit _i_ in `mip` is read-only, the implementation must
provide some other mechanism for clearing the pending interrupt.

A bit in `mie` must be writable if the corresponding interrupt can ever
become pending. Bits of `mie` that are not writable must be read-only
zero.

[NOTE]
====
The machine-level interrupt registers handle a few root interrupt
sources which are assigned a fixed service priority for simplicity,
while separate external interrupt controllers can implement a more
complex prioritization scheme over a much larger set of interrupts that
are then muxed into the machine-level interrupt sources.

'''

The non-maskable interrupt is not made visible via the `mip` register as
its presence is implicitly known when executing the NMI trap handler.
====

If supervisor mode is implemented, bits `mip`.SEIP and `mie`.SEIE are
the interrupt-pending and interrupt-enable bits for supervisor-level
external interrupts. SEIP is writable in `mip`, and may be written by
M-mode software to indicate to S-mode that an external interrupt is
pending. Additionally, the platform-level interrupt controller may
generate supervisor-level external interrupts. Supervisor-level external
interrupts are made pending based on the logical-OR of the
software-writable SEIP bit and the signal from the external interrupt
controller. When `mip` is read with a CSR instruction, the value of the
SEIP bit returned in the `rd` destination register is the logical-OR of
the software-writable bit and the interrupt signal from the interrupt
controller, but the signal from the interrupt controller is not used to
calculate the value written to SEIP. Only the software-writable SEIP bit
participates in the read-modify-write sequence of a CSRRS or CSRRC
instruction.

[NOTE]
====
For example, if we name the software-writable SEIP bit `B` and the
signal from the external interrupt controller `E`, then if
`csrrs t0, mip, t1` is executed, `t0[9]` is written with `B || E`, then
`B` is written with `B || t1[9]`. If `csrrw t0, mip, t1` is executed,
then `t0[9]` is written with `B || E`, and `B` is simply written with
`t1[9]`. In neither case does `B` depend upon `E`.

The SEIP field behavior is designed to allow a higher privilege layer to
mimic external interrupts cleanly, without losing any real external
interrupts. The behavior of the CSR instructions is slightly modified
from regular CSR accesses as a result.
====

If supervisor mode is implemented, bits `mip`.STIP and `mie`.STIE are
the interrupt-pending and interrupt-enable bits for supervisor-level
timer interrupts. STIP is writable in `mip`, and may be written by
M-mode software to deliver timer interrupts to S-mode.

If supervisor mode is implemented, bits `mip`.SSIP and `mie`.SSIE are
the interrupt-pending and interrupt-enable bits for supervisor-level
software interrupts. SSIP is writable in `mip` and may also be set to 1
by a platform-specific interrupt controller.

<% if ext?(:Sscofpmf) -%>
bits `mip`.LCOFIP and `mie`.LCOFIE
are the interrupt-pending and interrupt-enable bits for local counter-overflow
interrupts.
LCOFIP is read-write in `mip` and reflects the occurrence of a local
counter-overflow overflow interrupt request resulting from any of the
`mhpmevent__n__`.OF bits being set.
<% end -%>

Multiple simultaneous interrupts destined for M-mode are handled in the
following decreasing priority order: MEI, MSI, MTI, SEI, SSI, STI, LCOFI.

[NOTE]
====
The machine-level interrupt fixed-priority ordering rules were developed
with the following rationale.

Interrupts for higher privilege modes must be serviced before interrupts
for lower privilege modes to support preemption.

The platform-specific machine-level interrupt sources in bits 16 and
above have platform-specific priority, but are typically chosen to have
the highest service priority to support very fast local vectored
interrupts.

External interrupts are handled before internal (timer/software)
interrupts as external interrupts are usually generated by devices that
might require low interrupt service times.

Software interrupts are handled before internal timer interrupts,
because internal timer interrupts are usually intended for time slicing,
where time precision is less important, whereas software interrupts are
used for inter-processor messaging. Software interrupts can be avoided
when high-precision timing is required, or high-precision timer
interrupts can be routed via a different interrupt path. Software
interrupts are located in the lowest four bits of `mip` as these are
often written by software, and this position allows the use of a single
CSR instruction with a five-bit immediate.
====

Restricted views of the `mip` and `mie` registers appear as the `sip`
and `sie` registers for supervisor level. If an interrupt is delegated
to S-mode by setting a bit in the `mideleg` register, it becomes visible
in the `sip` register and is maskable using the `sie` register.
Otherwise, the corresponding bits in `sip` and `sie` are read-only zero.
fields:
SSIE:
location: 1
Expand Down
Loading
Loading