Skip to content

Commit 156fde0

Browse files
committed
Update interrupt handling to reflect mtime/mtimecmp writes sooner, trap on mtime access in lower privilege modes
1 parent 2b6820f commit 156fde0

File tree

2 files changed

+68
-49
lines changed

2 files changed

+68
-49
lines changed

src/cpu/controller.mlog.jinja

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ end_slow_init:
162162
# clear pending/enabled interrupts
163163
set csr_mip 0
164164
set csr_mie 0
165-
set interrupts_pending false
165+
set poll_interrupts false
166+
set fire_interrupts false
166167

167168
# reset mtvec to implementation-defined value to help catch bugs
168169
write {{SYSCON}} {{CSRS}} "{{ 'mtvec'|csr }}"
@@ -222,7 +223,8 @@ next_tick:
222223
read csr_mstatus prev_proc "csr_mstatus"
223224
read csr_mip prev_proc "csr_mip"
224225
read csr_mie prev_proc "csr_mie"
225-
read interrupts_pending prev_proc "interrupts_pending"
226+
read poll_interrupts prev_proc "poll_interrupts"
227+
read fire_interrupts prev_proc "fire_interrupts"
226228

227229
# for debug output
228230
read instruction prev_proc "instruction"
@@ -285,26 +287,9 @@ end_pause:
285287
set last_time_update @time
286288
set last_cycle_update @tick
287289

288-
# check if interrupts should be pending
289-
# FIXME: we probably need to check this in the workers
290-
291-
# timer interrupt
292-
op greaterThan high_gt csr_mtimeh csr_mtimecmph
293-
op equal high_eq csr_mtimeh csr_mtimecmph
294-
op greaterThanEq low_ge csr_mtime csr_mtimecmp
295-
296-
op and mip.mtip high_eq low_ge
297-
op or mip.mtip mip.mtip high_gt
298-
op shl mip.mtip mip.mtip 7
299-
300-
op and csr_mip csr_mip 0b11111111111111111111111101111111
301-
op or csr_mip csr_mip mip.mtip
302-
303-
# general "please check interrupts" signal
304-
# this is set by instructions that require interrupts to be checked immediately,
305-
# and cleared by the first worker to handle it
306-
op notEqual m_interrupts_pending csr_mip 0
307-
op or interrupts_pending interrupts_pending m_interrupts_pending
290+
# tell the first worker to poll and fire interrupts
291+
set poll_interrupts true
292+
set fire_interrupts true
308293

309294
# go to next tick
310295

@@ -403,7 +388,8 @@ set _ csr_minstreth
403388
set _ csr_mstatus
404389
set _ csr_mip
405390
set _ csr_mie
406-
set _ interrupts_pending
391+
set _ poll_interrupts
392+
set _ fire_interrupts
407393
set _ instruction
408394
set _ op_id
409395
set _ rs1_id

src/cpu/worker.mlog.jinja

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#%# ... + store_ram_word_unchecked + instret overflow + a bit extra to be safe
1818
#% set base_max_cost = base_instruction_cost + amo_cost + branch_decode + 23 + 3 + 10
1919

20-
#%# ... + check_interrupts + access_icache
21-
#% set MAX_FAST_INSTRUCTION_COST = base_max_cost + 8 + 25
20+
#%# ... + fire_interrupts + access_icache
21+
#% set MAX_FAST_INSTRUCTION_COST = base_max_cost + 9 + 25
2222

2323
#%# ... + load_ram_word_unchecked
2424
#% set MAX_SLOW_INSTRUCTION_COST = base_max_cost + amo_decode + 32
@@ -99,7 +99,8 @@ next_tick_no_check_overrun:
9999
read csr_mstatus prev_proc "csr_mstatus"
100100
read csr_mip prev_proc "csr_mip"
101101
read csr_mie prev_proc "csr_mie"
102-
read interrupts_pending prev_proc "interrupts_pending"
102+
read poll_interrupts prev_proc "poll_interrupts"
103+
read fire_interrupts prev_proc "fire_interrupts"
103104

104105
jump reset equal state "halt"
105106

@@ -111,21 +112,45 @@ next_tick_no_check_overrun:
111112
jump reset notEqual state "running"
112113

113114
set current_tick @tick
114-
op sub accumulator @ipt 30
115-
jump main notEqual interrupts_pending true
115+
op sub accumulator @ipt 33
116116

117-
check_interrupts:
118-
op sub accumulator accumulator 8
117+
# using two variables is slower here, but faster in end_instruction
118+
jump poll_interrupts equal poll_interrupts true
119+
jump fire_interrupts equal fire_interrupts true
120+
jump main always
121+
122+
# check if software-manipulable interrupts should become pending
123+
# this runs at the start of the first worker each tick, and at the start of the next worker if certain CSRs/MMRs are modified
124+
poll_interrupts:
125+
op sub accumulator accumulator 8 # -2 because we took the first jump above
126+
set poll_interrupts false
127+
128+
# machine timer interrupt
129+
op greaterThan high_gt csr_mtimeh csr_mtimecmph
130+
op equal high_eq csr_mtimeh csr_mtimecmph
131+
op greaterThanEq low_ge csr_mtime csr_mtimecmp
132+
133+
op and mip.mtip high_eq low_ge
134+
op or mip.mtip mip.mtip high_gt
135+
op shl mip.mtip mip.mtip 7
136+
137+
op and csr_mip csr_mip 0b11111111111111111111111101111111
138+
op or csr_mip csr_mip mip.mtip
139+
140+
# check if interrupts should fire
141+
# this runs at the start of the first worker each tick, and if certain CSRs are modified
142+
fire_interrupts:
143+
op sub accumulator accumulator 9
144+
set fire_interrupts false
119145

120146
# if privilege_mode < M, machine interrupts are globally enabled
121-
jump check_interrupts__machine lessThan privilege_mode 0b11
147+
jump fire_interrupts__machine lessThan privilege_mode 0b11
122148
# if privilege_mode = M, machine interrupts are enabled if mstatus.MIE is 1
123149
op and mstatus.mie csr_mstatus 0b1000
124150
jump main equal mstatus.mie 0
125151

126-
check_interrupts__machine:
127-
128-
# timer interrupt
152+
fire_interrupts__machine:
153+
# machine timer interrupt
129154
op and mip.mtip csr_mip 0b10000000
130155
op and mie.mtie csr_mie mip.mtip
131156
set mcause 0x80000007 # machine timer interrupt
@@ -230,9 +255,15 @@ main__read_icache:
230255
read @counter {{LABELS}} index
231256

232257
# this is a normal length for a label
258+
# cost: 4
259+
end_instruction_with_rd_and_poll_interrupts:
260+
set poll_interrupts true
261+
# continue into end_instruction_with_rd_and_fire_interrupts
262+
263+
# this is too
233264
# cost: 3
234-
end_instruction_with_rd_and_interrupts:
235-
set interrupts_pending true
265+
end_instruction_with_rd_and_fire_interrupts:
266+
set fire_interrupts true
236267
# continue into end_instruction_with_rd
237268

238269
# most instructions with an output register jump here after completing successfully
@@ -265,8 +296,8 @@ end_instruction_trap:
265296
jump state->pause equal single_step_enabled true
266297

267298
jump next_tick lessThanEq accumulator {{MAX_FAST_INSTRUCTION_COST}}
268-
jump main notEqual interrupts_pending true
269-
jump check_interrupts always
299+
jump main notEqual fire_interrupts true
300+
jump fire_interrupts always
270301

271302
# exceptions
272303

@@ -526,15 +557,17 @@ load_rom_word_unchecked:
526557

527558
# address, mcause, mtval -> result
528559
load_mmio_word:
529-
op sub accumulator accumulator 14
560+
op sub accumulator accumulator 15
530561

531562
op sub _offset address {{MMIO_START}}
532563
op and _offset _offset 0xfffffffc
533564
jump trap greaterThanEq _offset 0x90
534565

535566
# if we're in UART range, look it up
536-
op add access_uart_ret @counter 1
567+
op add access_uart_ret @counter 2 # skip privilege check if accessing uart
537568
jump access_uart greaterThanEq _offset 0x10
569+
# otherwise, we're in machine timer range, so trap if not in M-mode
570+
jump trap lessThan privilege_mode 0b11
538571

539572
# MCR, MSR, and SPR are all hardwired to zero
540573
set result 0
@@ -1325,22 +1358,22 @@ store_mmio_word_unchecked:
13251358
jump store_mmio_word_unchecked__uart_fcr equal _offset 0x18
13261359
jump end_instruction greaterThan _offset 0x10
13271360

1328-
# NOTE: if we get to this point, the MMIO address must be valid, so we don't need a bounds check
1361+
# NOTE: if we get to this point, the MMIO address must be valid, so we don't need bounds or privilege checks
13291362
op idiv _jump _offset 2
13301363
op add @counter @counter _jump
13311364

13321365
# mtime
13331366
set csr_mtime value
1334-
jump end_instruction_with_rd_and_interrupts always
1367+
jump end_instruction_with_rd_and_poll_interrupts always
13351368
# mtimeh
13361369
set csr_mtimeh value
1337-
jump end_instruction_with_rd_and_interrupts always
1370+
jump end_instruction_with_rd_and_poll_interrupts always
13381371
# mtimecmp
13391372
set csr_mtimecmp value
1340-
jump end_instruction_with_rd_and_interrupts always
1373+
jump end_instruction_with_rd_and_poll_interrupts always
13411374
# mtimecmph
13421375
set csr_mtimecmph value
1343-
jump end_instruction_with_rd_and_interrupts always
1376+
jump end_instruction_with_rd_and_poll_interrupts always
13441377
# UART
13451378
# Transmitter Holding Register
13461379
# append value to queue
@@ -1823,7 +1856,7 @@ MRET:
18231856
set icache_var null
18241857

18251858
set rd_id 0
1826-
jump end_instruction_with_rd_and_interrupts always
1859+
jump end_instruction_with_rd_and_fire_interrupts always
18271860

18281861
CSRRWI:
18291862
# CSRI-type: rs1_id=uimm, imm=csr, rd_id
@@ -2037,7 +2070,7 @@ modify_csr__mstatus:
20372070
op and csr_mstatus csr_mstatus 0b11111111111111111110011111111111
20382071
modify_csr__mstatus__mpp_m:
20392072

2040-
jump end_instruction_with_rd_and_interrupts always
2073+
jump end_instruction_with_rd_and_fire_interrupts always
20412074

20422075
modify_csr__mip:
20432076
set rd csr_mip
@@ -2061,7 +2094,7 @@ modify_csr__mip:
20612094
# enforce read-only non-zero fields
20622095
op or csr_mip csr_mip tmp
20632096

2064-
jump end_instruction_with_rd_and_interrupts always
2097+
jump end_instruction_with_rd_and_fire_interrupts always
20652098

20662099
modify_csr__mie:
20672100
set rd csr_mie
@@ -2076,7 +2109,7 @@ modify_csr__mie:
20762109
# MSIE -
20772110
op and csr_mie new_value 0b11111111111111110000100010000000
20782111

2079-
jump end_instruction_with_rd_and_interrupts always
2112+
jump end_instruction_with_rd_and_fire_interrupts always
20802113

20812114
modify_csr__minstret:
20822115
set rd csr_minstret

0 commit comments

Comments
 (0)