Skip to content

Commit

Permalink
mtvec vectored mode (#755)
Browse files Browse the repository at this point in the history
  • Loading branch information
kalinf authored Nov 26, 2024
1 parent 5e0e7d6 commit 40c82ee
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 6 deletions.
6 changes: 6 additions & 0 deletions coreblocks/arch/isa_consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ class PrivilegeLevel(IntEnum, shape=2):
MACHINE = 0b11


@unique
class TrapVectorMode(IntEnum, shape=2):
DIRECT = 0b00
VECTORED = 0b01


@unique
class InterruptCauseNumber(IntEnum):
SSI = 1 # supervisor software interrupt
Expand Down
14 changes: 12 additions & 2 deletions coreblocks/backend/retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from coreblocks.arch import ExceptionCause
from coreblocks.interface.keys import CoreStateKey, CSRInstancesKey, InstructionPrecommitKey
from coreblocks.priv.csr.csr_instances import CSRAddress, DoubleCounterCSR
from coreblocks.arch.isa_consts import TrapVectorMode


class Retirement(Elaboratable):
Expand Down Expand Up @@ -213,8 +214,17 @@ def flush_instr(rob_entry):
self.perf_trap_latency.stop(m)

handler_pc = Signal(self.gen_params.isa.xlen)
# mtvec without mode is [mxlen-1:2], mode is two last bits. Only direct mode is supported
m.d.av_comb += handler_pc.eq(m_csr.mtvec.read(m).data & ~(0b11))
mtvec_offset = Signal(self.gen_params.isa.xlen)
mtvec_base = m_csr.mtvec_base.read(m).data
mtvec_mode = m_csr.mtvec_mode.read(m).data
mcause = m_csr.mcause.read(m).data

# When mode is Vectored, interrupts set pc to base + 4 * cause_number
with m.If(mcause[-1] & (mtvec_mode == TrapVectorMode.VECTORED)):
m.d.av_comb += mtvec_offset.eq(mcause << 2)

# (mtvec_base stores base[MXLEN-1:2])
m.d.av_comb += handler_pc.eq((mtvec_base << 2) + mtvec_offset)

resume_pc = Mux(continue_pc_override, continue_pc, handler_pc)
m.d.sync += continue_pc_override.eq(0)
Expand Down
20 changes: 16 additions & 4 deletions coreblocks/priv/csr/csr_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from coreblocks.arch import CSRAddress
from coreblocks.arch.csr_address import MstatusFieldOffsets
from coreblocks.arch.isa import Extension
from coreblocks.arch.isa_consts import PrivilegeLevel, XlenEncoding
from coreblocks.arch.isa_consts import PrivilegeLevel, XlenEncoding, TrapVectorMode
from coreblocks.params.genparams import GenParams
from coreblocks.priv.csr.csr_register import CSRRegister
from coreblocks.priv.csr.aliased import AliasedCSR
Expand Down Expand Up @@ -75,9 +75,7 @@ def __init__(self, gen_params: GenParams):

self.mcause = CSRRegister(CSRAddress.MCAUSE, gen_params)

# SPEC: The mtvec register must always be implemented, but can contain a read-only value.
# set `MODE` as fixed to 0 - Direct mode "All exceptions set pc to BASE"
self.mtvec = CSRRegister(CSRAddress.MTVEC, gen_params, ro_bits=0b11)
self.mtvec = AliasedCSR(CSRAddress.MTVEC, gen_params)

mepc_ro_bits = 0b1 if Extension.C in gen_params.isa.extensions else 0b11 # pc alignment (SPEC)
self.mepc = CSRRegister(CSRAddress.MEPC, gen_params, ro_bits=mepc_ro_bits)
Expand All @@ -99,6 +97,7 @@ def __init__(self, gen_params: GenParams):
self.priv_mode_public.add_field(0, self.priv_mode)

self._mstatus_fields_implementation(gen_params, self.mstatus, self.mstatush)
self._mtvec_fields_implementation(gen_params, self.mtvec)

def elaborate(self, platform):
m = Module()
Expand All @@ -109,6 +108,19 @@ def elaborate(self, platform):

return m

def _mtvec_fields_implementation(self, gen_params: GenParams, mtvec: AliasedCSR):
def filter_legal_mode(m: TModule, v: Value):
legal = Signal(1)
m.d.av_comb += legal.eq((v == TrapVectorMode.DIRECT) | (v == TrapVectorMode.VECTORED))
return (legal, v)

self.mtvec_base = CSRRegister(None, gen_params, width=gen_params.isa.xlen - 2)
mtvec.add_field(TrapVectorMode.as_shape().width, self.mtvec_base)
self.mtvec_mode = CSRRegister(
None, gen_params, width=TrapVectorMode.as_shape().width, fu_write_filtermap=filter_legal_mode
)
mtvec.add_field(0, self.mtvec_mode)

def _mstatus_fields_implementation(self, gen_params: GenParams, mstatus: AliasedCSR, mstatush: AliasedCSR):
def filter_legal_priv_mode(m: TModule, v: Value):
legal = Signal(1)
Expand Down
136 changes: 136 additions & 0 deletions test/asm/interrupt_vectored.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
.include "init_regs.s"

_start:
INIT_REGS_LOAD

# fibonacci spiced with interrupt handler (also with fibonacci)
li x1, 0x201
csrw mtvec, x1
li x1, 0x203
csrw mtvec, x1
csrr x16, mtvec # since mtvec is WARL, should stay 0x201
ecall # synchronous exception jumps to 0x200 + 0x0

interrupts:
li x27, 0 # handler count
li x30, 0 # interrupt count
li x31, 0xde # branch guard

csrsi mstatus, 0x8 # machine interrupt enable
csrr x29, mstatus
li x1, 0x30000
csrw mie, x1 # enable custom interrupt 0 and 1
li x1, 0
li x2, 1
li x5, 4
li x6, 7
li x7, 0
li x12, 4
li x13, 7
li x14, 0
loop:
add x3, x2, x1
mv x1, x2
mv x2, x3
bne x2, x4, loop
infloop:
j infloop

int0_handler:
# save main loop register state
mv x9, x1
mv x10, x2
mv x11, x3

# check cause
li x2, 0x80000010 # cause for 01,11
csrr x3, mcause
bne x2, x3, fail

# fibonacci step
beq x7, x8, skip
add x7, x6, x5
mv x5, x6
mv x6, x7

skip:
# generate new mie mask
andi x2, x30, 0x3
bnez x2, fill_skip
li x2, 0x3
fill_skip:
slli x2, x2, 16
csrw mie, x2

# clear interrupts
csrr x1, mip
srli x1, x1, 16
andi x2, x1, 0x1
beqz x2, skip_clear_edge
addi x30, x30, 1
li x2, 0x10000
csrc mip, x2 # clear edge reported interrupt
skip_clear_edge:
andi x2, x1, 0x2
beqz x2, skip_clear_level
addi x30, x30, 1
csrwi 0x7ff, 1 # clear level reported interrupt via custom csr
skip_clear_level:
addi x27, x27, 1

# restore main loop register state
mv x1, x9
mv x2, x10
mv x3, x11
mret

int1_handler:
# save main loop register state
mv x9, x1
mv x10, x2
mv x11, x3

# check cause
li x2, 0x80000011 # cause for 10
csrr x3, mcause
bne x2, x3, fail

# fibonacci step
beq x14, x15, skip
add x14, x13, x12
mv x12, x13
mv x13, x14
j skip

ecall_handler:
li x17, 0x111
la x1, interrupts
csrw mepc, x1
mret

fail:
csrwi 0x7ff, 2
j fail

.org 0x200
j ecall_handler
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
j fail
j int0_handler
j int1_handler
li x31, 0xae # should never happen

INIT_REGS_ALLOCATION
9 changes: 9 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ def test_asm_source(self):
("interrupt.asm", 600, {4: 89, 8: 843}, {2: 89, 7: 843, 31: 0xDE}, 30, 50, False),
# interrupts are only inserted on branches, we always have some forward progression. 15 for trigger variantion.
("interrupt.asm", 80, {4: 21, 8: 9349}, {2: 21, 7: 9349, 31: 0xDE}, 0, 15, False),
(
"interrupt_vectored.asm",
200,
{4: 21, 8: 9349, 15: 24476},
{2: 21, 7: 9349, 14: 24476, 31: 0xDE, 16: 0x201, 17: 0x111},
0,
15,
False,
),
("wfi_int.asm", 80, {2: 10}, {2: 10, 3: 10}, 5, 15, True),
],
)
Expand Down

0 comments on commit 40c82ee

Please sign in to comment.