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
528559load_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
18281861CSRRWI:
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
20382071modify_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
20422075modify_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
20662099modify_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
20812114modify_csr__minstret:
20822115 set rd csr_minstret
0 commit comments