Skip to content

Commit 4a17390

Browse files
committed
fix(ruby): correct bugs found by type checking
1 parent 7db9968 commit 4a17390

File tree

8 files changed

+163
-36
lines changed

8 files changed

+163
-36
lines changed

.github/workflows/regress.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ on:
33
push:
44
branches:
55
- main
6-
merge_group: # this is a new line
7-
types: [checks_requested] # this is a new line
6+
merge_group:
7+
types: [checks_requested]
88
pull_request:
99
branches:
1010
- main

arch/csr/Zihpm/hpmcounterNh.layout

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ kind: csr
77
name: hpmcounter<%= hpm_num %>h
88
long_name: User-mode Hardware Performance Counter <%= hpm_num - 3 %>, high half
99
address: 0x<%= (0xC83 + (hpm_num - 3)).to_s(16).upcase %>
10+
base: 32
1011
description: |
1112
Alias for M-mode CSR `mhpmcounter<%= hpm_num %>h`.
1213

arch/csr/mie.yaml

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,151 @@ address: 0x304
88
priv_mode: M
99
length: MXLEN
1010
definedBy: Sm
11-
description:
12-
$copy: "mip.yaml#/description"
11+
description: |
12+
The `mie` and `mip` CSRs are MXLEN-bit read/write registers used when
13+
the CLINT or PLIC interrupt controllers are present.
14+
Note that the CLINT refers to an interrupt controller
15+
used by some RISC-V implementations but isn't a ratified
16+
RISC-V International standard.
17+
18+
The `mip` CSR contains information on pending interrupts, while `mie` is the corresponding
19+
CSR containing interrupt enable bits.
20+
Interrupt cause number _i_ (as reported in the `mcause` CSR)
21+
corresponds to bit _i_ in both `mip` and `mie`.
22+
Bits 15:0 are allocated to standard interrupt causes only, while
23+
bits 16 and above are designated for platform use.
24+
25+
NOTE: Interrupts designated for platform use may be designated for custom use
26+
at the platform's discretion.
27+
28+
An interrupt _i_ will trap to M-mode (causing the privilege mode to
29+
change to M-mode) if all of the following are true:
30+
31+
* either the current privilege mode is M and the MIE bit in the `mstatus` register is
32+
set, or the current privilege mode has less privilege than M-mode;
33+
* bit _i_ is set in both `mip` and `mie`
34+
* if register `mideleg` exists, bit _i_ is not set in `mideleg`.
35+
36+
These conditions for an interrupt trap to occur must be evaluated in a
37+
bounded amount of time from when an interrupt becomes, or ceases to be,
38+
pending in `mip`, and must also be evaluated immediately following the
39+
execution of an __x__RET instruction or an explicit write to a CSR on
40+
which these interrupt trap conditions expressly depend (including `mip`,
41+
`mie`, `mstatus`, and `mideleg`).
42+
43+
Interrupts to M-mode take priority over any interrupts to lower
44+
privilege modes.
45+
46+
Each individual bit in register `mip` may be writable or may be
47+
read-only. When bit _i_ in `mip` is writable, a pending interrupt _i_
48+
can be cleared by writing 0 to this bit. If interrupt _i_ can become
49+
pending but bit _i_ in `mip` is read-only, the implementation must
50+
provide some other mechanism for clearing the pending interrupt.
51+
52+
A bit in `mie` must be writable if the corresponding interrupt can ever
53+
become pending. Bits of `mie` that are not writable must be read-only
54+
zero.
55+
56+
[NOTE]
57+
====
58+
The machine-level interrupt registers handle a few root interrupt
59+
sources which are assigned a fixed service priority for simplicity,
60+
while separate external interrupt controllers can implement a more
61+
complex prioritization scheme over a much larger set of interrupts that
62+
are then muxed into the machine-level interrupt sources.
63+
64+
'''
65+
66+
The non-maskable interrupt is not made visible via the `mip` register as
67+
its presence is implicitly known when executing the NMI trap handler.
68+
====
69+
70+
If supervisor mode is implemented, bits `mip`.SEIP and `mie`.SEIE are
71+
the interrupt-pending and interrupt-enable bits for supervisor-level
72+
external interrupts. SEIP is writable in `mip`, and may be written by
73+
M-mode software to indicate to S-mode that an external interrupt is
74+
pending. Additionally, the platform-level interrupt controller may
75+
generate supervisor-level external interrupts. Supervisor-level external
76+
interrupts are made pending based on the logical-OR of the
77+
software-writable SEIP bit and the signal from the external interrupt
78+
controller. When `mip` is read with a CSR instruction, the value of the
79+
SEIP bit returned in the `rd` destination register is the logical-OR of
80+
the software-writable bit and the interrupt signal from the interrupt
81+
controller, but the signal from the interrupt controller is not used to
82+
calculate the value written to SEIP. Only the software-writable SEIP bit
83+
participates in the read-modify-write sequence of a CSRRS or CSRRC
84+
instruction.
85+
86+
[NOTE]
87+
====
88+
For example, if we name the software-writable SEIP bit `B` and the
89+
signal from the external interrupt controller `E`, then if
90+
`csrrs t0, mip, t1` is executed, `t0[9]` is written with `B || E`, then
91+
`B` is written with `B || t1[9]`. If `csrrw t0, mip, t1` is executed,
92+
then `t0[9]` is written with `B || E`, and `B` is simply written with
93+
`t1[9]`. In neither case does `B` depend upon `E`.
94+
95+
The SEIP field behavior is designed to allow a higher privilege layer to
96+
mimic external interrupts cleanly, without losing any real external
97+
interrupts. The behavior of the CSR instructions is slightly modified
98+
from regular CSR accesses as a result.
99+
====
100+
101+
If supervisor mode is implemented, bits `mip`.STIP and `mie`.STIE are
102+
the interrupt-pending and interrupt-enable bits for supervisor-level
103+
timer interrupts. STIP is writable in `mip`, and may be written by
104+
M-mode software to deliver timer interrupts to S-mode.
105+
106+
If supervisor mode is implemented, bits `mip`.SSIP and `mie`.SSIE are
107+
the interrupt-pending and interrupt-enable bits for supervisor-level
108+
software interrupts. SSIP is writable in `mip` and may also be set to 1
109+
by a platform-specific interrupt controller.
110+
111+
<% if ext?(:Sscofpmf) -%>
112+
bits `mip`.LCOFIP and `mie`.LCOFIE
113+
are the interrupt-pending and interrupt-enable bits for local counter-overflow
114+
interrupts.
115+
LCOFIP is read-write in `mip` and reflects the occurrence of a local
116+
counter-overflow overflow interrupt request resulting from any of the
117+
`mhpmevent__n__`.OF bits being set.
118+
<% end -%>
119+
120+
Multiple simultaneous interrupts destined for M-mode are handled in the
121+
following decreasing priority order: MEI, MSI, MTI, SEI, SSI, STI, LCOFI.
122+
123+
[NOTE]
124+
====
125+
The machine-level interrupt fixed-priority ordering rules were developed
126+
with the following rationale.
127+
128+
Interrupts for higher privilege modes must be serviced before interrupts
129+
for lower privilege modes to support preemption.
130+
131+
The platform-specific machine-level interrupt sources in bits 16 and
132+
above have platform-specific priority, but are typically chosen to have
133+
the highest service priority to support very fast local vectored
134+
interrupts.
135+
136+
External interrupts are handled before internal (timer/software)
137+
interrupts as external interrupts are usually generated by devices that
138+
might require low interrupt service times.
139+
140+
Software interrupts are handled before internal timer interrupts,
141+
because internal timer interrupts are usually intended for time slicing,
142+
where time precision is less important, whereas software interrupts are
143+
used for inter-processor messaging. Software interrupts can be avoided
144+
when high-precision timing is required, or high-precision timer
145+
interrupts can be routed via a different interrupt path. Software
146+
interrupts are located in the lowest four bits of `mip` as these are
147+
often written by software, and this position allows the use of a single
148+
CSR instruction with a five-bit immediate.
149+
====
150+
151+
Restricted views of the `mip` and `mie` registers appear as the `sip`
152+
and `sie` registers for supervisor level. If an interrupt is delegated
153+
to S-mode by setting a bit in the `mideleg` register, it becomes visible
154+
in the `sip` register and is maskable using the `sie` register.
155+
Otherwise, the corresponding bits in `sip` and `sie` are read-only zero.
13156
fields:
14157
SSIE:
15158
location: 1

lib/arch_obj_models/csr_field.rb

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ def pruned_type_ast(effective_xlen)
180180
# 'RW-R' => Read-write, with a restricted set of legal values
181181
# 'RW-H' => Read-write, with a hardware update
182182
# 'RW-RH' => Read-write, with a hardware update and a restricted set of legal values
183-
sig { params(effective_xlen: T.nilable(Integer)).returns(String) }
183+
# @return [nil] when the type isn't knowable
184+
sig { params(effective_xlen: T.nilable(Integer)).returns(T.nilable(String)) }
184185
def type(effective_xlen = nil)
185186
@type ||= { 32 => nil, 64 => nil }
186187
return @type[effective_xlen] unless @type[effective_xlen].nil?
@@ -218,8 +219,8 @@ def type(effective_xlen = nil)
218219
type = nil
219220
end
220221
ensure
221-
symtab.pop unless symtab.nil?
222-
symtab.release unless symtab.nil?
222+
symtab&.pop
223+
symtab&.release
223224
end
224225
type
225226
# end
@@ -237,11 +238,8 @@ def type(effective_xlen = nil)
237238
# @param effective_xlen [32, 64] The effective xlen to evaluate for
238239
sig { params(effective_xlen: T.nilable(Integer)).returns(String) }
239240
def type_pretty(effective_xlen = nil)
240-
str = T.let(nil, T.nilable(String))
241-
value_result = Idl::AstNode.value_try do
242-
str = type(effective_xlen)
243-
end
244-
Idl::AstNode.value_else(value_result) do
241+
str = type(effective_xlen)
242+
if str.nil?
245243
ast = T.must(type_ast)
246244
str = ast.gen_option_adoc
247245
end
@@ -427,7 +425,7 @@ def reset_value_pretty
427425
ast = T.must(reset_value_ast)
428426
str = ast.gen_option_adoc
429427
end
430-
T.must(str)
428+
T.must(str).to_s
431429
end
432430

433431
# @return [Boolean] true if the CSR field has a custom sw_write function

lib/arch_obj_models/obj.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def keys = @data.keys
251251
def key?(k) = @data.key?(k)
252252

253253
# defer the calculation of 'blk' until later, then memoize the result
254-
sig { params(fn_name: Symbol, block: T.proc.void).returns(T::Boolean) }
254+
sig { params(fn_name: Symbol, block: T.proc.void).returns(T.untyped) }
255255
def defer(fn_name, &block)
256256
cache_value = @cache[fn_name]
257257
return cache_value unless cache_value.nil?

lib/arch_obj_models/req_expression.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,9 @@ def eval(term_values)
341341
if @type == TYPES::Term
342342
ext_req = T.cast(@children[0], ExtensionRequirement)
343343
term_value = term_values.find { |tv| tv.name == ext_req.name }
344-
unless term_value.nil?
345-
ext_req.satisfied_by?(term_value)
346-
end
344+
return false if term_value.nil?
345+
346+
ext_req.satisfied_by?(term_value)
347347
elsif @type == TYPES::If
348348
cond_ext_ret = T.cast(@children[0], LogicNode)
349349
if cond_ext_ret.eval(term_values)

lib/idl/ast.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6224,7 +6224,7 @@ def value(symtab)
62246224
else
62256225
value_error "CSR is not defined" unless symtab.cfg_arch.csrs.any? { |icsr| icsr.name == cd.name }
62266226
end
6227-
cd.fields.each { |f| value_error "#{csr_name(symtab)}.#{f.name} not RO" unless f.type(symtab) == "RO" }
6227+
cd.fields.each { |f| value_error "#{csr_name(symtab)}.#{f.name} not RO" unless f.type(nil) == "RO" }
62286228

62296229
csr_def(symtab).fields.reduce(0) { |val, f| val | (f.value << f.location.begin) }
62306230
end
@@ -6365,7 +6365,7 @@ def value(symtab)
63656365
when "sw_read"
63666366
value_error "CSR not knowable" unless csr_known?(symtab)
63676367
cd = csr_def(symtab)
6368-
cd.fields.each { |f| value_error "#{csr_name(symtab)}.#{f.name} not RO" unless f.type(symtab) == "RO" }
6368+
cd.fields.each { |f| value_error "#{csr_name(symtab)}.#{f.name} not RO" unless f.type(nil) == "RO" }
63696369

63706370
value_error "TODO: CSRs with sw_read function"
63716371
when "address"

schemas/csr_schema.json

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -202,23 +202,8 @@
202202
"description": "Descriptive name for the CSR"
203203
},
204204
"description": {
205-
"oneOf": [
206-
{
207-
"type": "string",
208-
"description": "A full Asciidoc description of the CSR, intended to be used as documentation."
209-
},
210-
{
211-
"type": "object",
212-
"description": "A full Asciidoc description of the CSR, intended to be used as documentation.",
213-
"properties": {
214-
"$copy": {
215-
"type": "string",
216-
"format": "uri-reference"
217-
}
218-
},
219-
"additionalProperties": false
220-
}
221-
]
205+
"type": "string",
206+
"description": "A full Asciidoc description of the CSR, intended to be used as documentation."
222207
},
223208
"definedBy": {
224209
"$ref": "schema_defs.json#/$defs/requires_entry",

0 commit comments

Comments
 (0)