Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mtvec vectored mode #755

Merged
merged 9 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
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
12 changes: 10 additions & 2 deletions coreblocks/backend/retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,16 @@ 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 = m_csr.mtvec._fu_read(m).data
kalinf marked this conversation as resolved.
Show resolved Hide resolved
mcause = m_csr.mcause.read(m).data

# mtvec without mode is [mxlen-1:2], mode is two last bits.
# When mode=1 (Vectored), interrupts set pc to base + 4 * cause_number
with m.If(mcause[-1] & (mtvec[0:1] == 1)):
kalinf marked this conversation as resolved.
Show resolved Hide resolved
m.d.av_comb += mtvec_offset.eq(mcause << 2)

m.d.av_comb += handler_pc.eq((mtvec & ~(0b11)) + 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 @@ -95,6 +93,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 @@ -105,6 +104,19 @@ def elaborate(self, platform):

return m

def mtvec_fields_implementation(self, gen_params: GenParams, mtvec: AliasedCSR):
kalinf marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -186,6 +186,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