Skip to content

Commit

Permalink
User Mode and remaining mstatus fields (#729)
Browse files Browse the repository at this point in the history
  • Loading branch information
piotro888 authored Oct 9, 2024
1 parent 87cb8f5 commit 0e2887a
Show file tree
Hide file tree
Showing 18 changed files with 418 additions and 48 deletions.
37 changes: 36 additions & 1 deletion coreblocks/arch/csr_address.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from amaranth.lib.enum import IntEnum, unique

__all__ = [
"CSRAddress",
"MstatusFieldOffsets",
]


@unique
class CSRAddress(IntEnum, shape=12):
Expand Down Expand Up @@ -430,4 +435,34 @@ class CSRAddress(IntEnum, shape=12):
DSCRATCH1 = 0x7B3 # Debug scratch register 1.

# Internal Coreblocks CSRs
COREBLOCKS_TEST_CSR = 0x7FF # used only for testbench verification
# used only for testbench verification

# CSR for custom communication with testbenches
COREBLOCKS_TEST_CSR = 0x7FF
# CSR providing writable current privilege mode (U-mode accesible)
COREBLOCKS_TEST_PRIV_MODE = 0x8FF


@unique
class MstatusFieldOffsets(IntEnum):
SIE = 1 # Supervisor Interrupt Enable
MIE = 3 # Machine Interrupt Enable
SPIE = 5 # Supervisor Previous Interrupt Enable
UBE = 6 # User Endianess Control
MPIE = 7 # Machine Previous Interrupt Enable
SPP = 8 # Supervisor Previous Pirvilege
VS = 9 # Vector Context Status
MPP = 11 # Machine Previous Pirvilege
FS = 13 # Float Context Status
XS = 15 # Additional Extension State Context Status
MPRV = 17 # Modify Pirvilege
SUM = 18 # Supervisor User Memory Access
MXR = 19 # Make Executable Readable
TVM = 20 # Trap Virtual Memory
TW = 21 # Timeout Wait
TSR = 22 # Trap SRET
UXL = 32 # User XLEN
SXL = 34 # Supervisor XLEN
SBE = 36 # Supervisor Endianess Control
MBE = 37 # Machine Endianess Contorol
SD = -1 # Context Status Dirty bit. Placed on last bit of mstatus
8 changes: 8 additions & 0 deletions coreblocks/arch/isa_consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"Registers",
"PrivilegeLevel",
"InterruptCauseNumber",
"XlenEncoding",
]


Expand Down Expand Up @@ -172,3 +173,10 @@ class InterruptCauseNumber(IntEnum):
MTI = 7 # machine timer interrupt
SEI = 9 # supervisor external interrupt
MEI = 11 # machine external interrupt


@unique
class XlenEncoding(IntEnum, shape=2):
W32 = 1
W64 = 2
W128 = 3
4 changes: 2 additions & 2 deletions coreblocks/backend/retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from coreblocks.params.genparams import GenParams
from coreblocks.arch import ExceptionCause
from coreblocks.interface.keys import CoreStateKey, GenericCSRRegistersKey, InstructionPrecommitKey
from coreblocks.interface.keys import CoreStateKey, CSRInstancesKey, InstructionPrecommitKey
from coreblocks.priv.csr.csr_instances import CSRAddress, DoubleCounterCSR


Expand Down Expand Up @@ -67,7 +67,7 @@ def elaborate(self, platform):

m.submodules += [self.perf_instr_ret, self.perf_trap_latency]

m_csr = self.dependency_manager.get_dependency(GenericCSRRegistersKey()).m_mode
m_csr = self.dependency_manager.get_dependency(CSRInstancesKey()).m_mode
m.submodules.instret_csr = self.instret_csr

side_fx = Signal(init=1)
Expand Down
4 changes: 2 additions & 2 deletions coreblocks/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from coreblocks.interface.layouts import *
from coreblocks.interface.keys import (
FetchResumeKey,
GenericCSRRegistersKey,
CSRInstancesKey,
CommonBusDataKey,
)
from coreblocks.params.genparams import GenParams
Expand Down Expand Up @@ -89,7 +89,7 @@ def __init__(self, *, gen_params: GenParams):
)

self.csr_generic = GenericCSRRegisters(self.gen_params)
self.connections.add_dependency(GenericCSRRegistersKey(), self.csr_generic)
self.connections.add_dependency(CSRInstancesKey(), self.csr_generic)

self.interrupt_controller = InternalInterruptController(self.gen_params)

Expand Down
9 changes: 5 additions & 4 deletions coreblocks/func_blocks/csr/csr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from coreblocks.interface.keys import (
CSRListKey,
FetchResumeKey,
CSRInstancesKey,
InstructionPrecommitKey,
ExceptionReportKey,
AsyncInterruptInsertSignalKey,
Expand Down Expand Up @@ -128,9 +129,6 @@ def elaborate(self, platform):
| ((instr.exec_fn.funct3 == Funct3.CSRRCI) & (instr.s1_val != 0))
)

# Temporary, until privileged spec is implemented
priv_level = Signal(PrivilegeLevel, init=PrivilegeLevel.MACHINE)

exe_side_fx = Signal()

# Methods used within this Tranaction are CSRRegister internal _fu_(read|write) handlers which are always ready
Expand All @@ -142,7 +140,10 @@ def elaborate(self, platform):

with m.Case(csr_number):
priv_valid = Signal()
m.d.comb += priv_valid.eq(priv_level_required <= priv_level)
current_priv_mode = (
self.dependency_manager.get_dependency(CSRInstancesKey()).m_mode.priv_mode.read(m).data
)
m.d.comb += priv_valid.eq(priv_level_required <= current_priv_mode)

with m.If(priv_valid):
read_val = Signal(self.gen_params.isa.xlen)
Expand Down
16 changes: 11 additions & 5 deletions coreblocks/func_blocks/fu/exception.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Sequence
from amaranth import *
from coreblocks.arch.isa_consts import PrivilegeLevel
from transactron.utils.dependencies import DependencyContext

from transactron import *
Expand All @@ -9,7 +10,7 @@
from coreblocks.arch import OpType, Funct3, ExceptionCause
from coreblocks.interface.layouts import FuncUnitLayouts
from transactron.utils import OneHotSwitch
from coreblocks.interface.keys import ExceptionReportKey
from coreblocks.interface.keys import ExceptionReportKey, CSRInstancesKey

from coreblocks.func_blocks.fu.common.fu_decoder import DecoderManager
from enum import IntFlag, auto
Expand Down Expand Up @@ -50,8 +51,8 @@ def __init__(self, gen_params: GenParams, unit_fn=ExceptionUnitFn()):
self.issue = Method(i=layouts.issue)
self.accept = Method(o=layouts.accept)

dm = DependencyContext.get()
self.report = dm.get_dependency(ExceptionReportKey())
self.dm = DependencyContext.get()
self.report = self.dm.get_dependency(ExceptionReportKey())

def elaborate(self, platform):
m = TModule()
Expand All @@ -69,12 +70,17 @@ def _(arg):

cause = Signal(ExceptionCause)

priv_level = self.dm.get_dependency(CSRInstancesKey()).m_mode.priv_mode.read(m).data

with OneHotSwitch(m, decoder.decode_fn) as OneHotCase:
with OneHotCase(ExceptionUnitFn.Fn.EBREAK):
m.d.comb += cause.eq(ExceptionCause.BREAKPOINT)
with OneHotCase(ExceptionUnitFn.Fn.ECALL):
# TODO: Switch privilege level when implemented
m.d.comb += cause.eq(ExceptionCause.ENVIRONMENT_CALL_FROM_M)
with m.Switch(priv_level):
with m.Case(PrivilegeLevel.MACHINE):
m.d.comb += cause.eq(ExceptionCause.ENVIRONMENT_CALL_FROM_M)
with m.Case(PrivilegeLevel.USER):
m.d.comb += cause.eq(ExceptionCause.ENVIRONMENT_CALL_FROM_U)
with OneHotCase(ExceptionUnitFn.Fn.INSTR_ACCESS_FAULT):
m.d.comb += cause.eq(ExceptionCause.INSTRUCTION_ACCESS_FAULT)
with OneHotCase(ExceptionUnitFn.Fn.ILLEGAL_INSTRUCTION):
Expand Down
33 changes: 27 additions & 6 deletions coreblocks/func_blocks/fu/priv.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from enum import IntFlag, auto, unique
from typing import Sequence
from coreblocks.arch.isa_consts import PrivilegeLevel


from transactron import *
Expand All @@ -18,7 +19,7 @@
MretKey,
AsyncInterruptInsertSignalKey,
ExceptionReportKey,
GenericCSRRegistersKey,
CSRInstancesKey,
InstructionPrecommitKey,
FetchResumeKey,
FlushICacheKey,
Expand Down Expand Up @@ -71,6 +72,7 @@ def elaborate(self, platform):

instr_valid = Signal()
finished = Signal()
illegal_instruction = Signal()

instr_rob = Signal(self.gp.rob_entries_bits)
instr_pc = Signal(self.gp.isa.xlen)
Expand All @@ -79,7 +81,8 @@ def elaborate(self, platform):
mret = self.dm.get_dependency(MretKey())
async_interrupt_active = self.dm.get_dependency(AsyncInterruptInsertSignalKey())
exception_report = self.dm.get_dependency(ExceptionReportKey())
csr = self.dm.get_dependency(GenericCSRRegistersKey())
csr = self.dm.get_dependency(CSRInstancesKey())
priv_mode = csr.m_mode.priv_mode
flush_icache = self.dm.get_dependency(FlushICacheKey())

m.submodules.fetch_resume_fifo = self.fetch_resume_fifo
Expand All @@ -101,14 +104,26 @@ def _(arg):
m.d.sync += finished.eq(1)
self.perf_instr.incr(m, instr_fn, cond=info.side_fx)

priv_data = priv_mode.read(m).data

illegal_mret = (instr_fn == PrivilegedFn.Fn.MRET) & (priv_data != PrivilegeLevel.MACHINE)
# future todo: WFI should be illegal in U-Mode only if S-Mode is supported
illegal_wfi = (
(instr_fn == PrivilegedFn.Fn.WFI)
& (priv_data == PrivilegeLevel.USER)
& csr.m_mode.mstatus_tw.read(m).data
)

with condition(m, nonblocking=True) as branch:
with branch(info.side_fx & (instr_fn == PrivilegedFn.Fn.MRET)):
with branch(info.side_fx & (instr_fn == PrivilegedFn.Fn.MRET) & ~illegal_mret):
mret(m)
with branch(info.side_fx & (instr_fn == PrivilegedFn.Fn.FENCEI)):
flush_icache(m)
with branch(info.side_fx & (instr_fn == PrivilegedFn.Fn.WFI)):
with branch(info.side_fx & (instr_fn == PrivilegedFn.Fn.WFI) & ~illegal_wfi):
m.d.sync += finished.eq(async_interrupt_active)

m.d.sync += illegal_instruction.eq(illegal_wfi | illegal_mret)

@def_method(m, self.accept, ready=instr_valid & finished)
def _():
m.d.sync += instr_valid.eq(0)
Expand All @@ -125,13 +140,19 @@ def _():
with OneHotCase(PrivilegedFn.Fn.WFI):
m.d.av_comb += ret_pc.eq(instr_pc + 4)

with m.If(illegal_instruction):
m.d.av_comb += ret_pc.eq(instr_pc)

exception = Signal()
with m.If(async_interrupt_active):
with m.If(illegal_instruction):
m.d.comb += exception.eq(1)
exception_report(m, cause=ExceptionCause.ILLEGAL_INSTRUCTION, pc=ret_pc, rob_id=instr_rob)
with m.Elif(async_interrupt_active):
# SPEC: "These conditions for an interrupt trap to occur [..] must also be evaluated immediately
# following the execution of an xRET instruction."
# mret() method is called from precommit() that was executed at least one cycle earlier (because
# of finished condition). If calling mret() caused interrupt to be active, it is already represented
# by updated async_interrupt_active singal.
# by updated async_interrupt_active signal.
# Interrupt is reported on this xRET instruction with return address set to instruction that we
# would normally return to (mepc value is preserved)
m.d.comb += exception.eq(1)
Expand Down
4 changes: 2 additions & 2 deletions coreblocks/interface/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"PredictedJumpTargetKey",
"FetchResumeKey",
"ExceptionReportKey",
"GenericCSRRegistersKey",
"CSRInstancesKey",
"AsyncInterruptInsertSignalKey",
"MretKey",
"CoreStateKey",
Expand Down Expand Up @@ -58,7 +58,7 @@ class ExceptionReportKey(SimpleKey[Method]):


@dataclass(frozen=True)
class GenericCSRRegistersKey(SimpleKey["GenericCSRRegisters"]):
class CSRInstancesKey(SimpleKey["GenericCSRRegisters"]):
pass


Expand Down
4 changes: 4 additions & 0 deletions coreblocks/params/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class _CoreConfigurationDataClass:
Bit mask specifing if interrupt should be edge or level triggered. If nth bit is set to 1, interrupt
with id 16+n will be considered as edge triggered and clearable via `mip`. In other case bit `mip` is
read-only and directly connected to input signal (implementation must provide clearing method)
user_mode: bool
Enable User Mode.
allow_partial_extensions: bool
Allow partial support of extensions.
extra_verification: bool
Expand Down Expand Up @@ -128,6 +130,8 @@ def __post_init__(self):
interrupt_custom_count: int = 0
interrupt_custom_edge_trig_mask: int = 0

user_mode: bool = True

allow_partial_extensions: bool = False

extra_verification: bool = True
Expand Down
2 changes: 2 additions & 0 deletions coreblocks/params/genparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ def __init__(self, cfg: CoreConfiguration):
self.interrupt_custom_count = cfg.interrupt_custom_count
self.interrupt_custom_edge_trig_mask = cfg.interrupt_custom_edge_trig_mask

self.user_mode = cfg.user_mode

self._toolchain_isa_str = gen_isa_string(extensions, cfg.xlen, skip_internal=True)

self._generate_test_hardware = cfg._generate_test_hardware
Expand Down
15 changes: 14 additions & 1 deletion coreblocks/priv/csr/aliased.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from amaranth import *

from typing import Optional
from enum import Enum

from coreblocks.interface.layouts import CSRRegisterLayouts
from coreblocks.params.genparams import GenParams
Expand All @@ -22,7 +23,10 @@ def __init__(self, csr_number: Optional[int], gen_params: GenParams, width: Opti
self.gen_params = gen_params
self.csr_number = csr_number
self.width = width if width is not None else gen_params.isa.xlen
self.fields = []

self.fields: list[tuple[int, CSRRegister]] = []
self.ro_values: list[tuple[int, int, int | Enum]] = []

csr_layouts = gen_params.get(CSRRegisterLayouts)

self._fu_read = Method(o=csr_layouts._fu_read)
Expand All @@ -44,6 +48,11 @@ def add_field(self, bit_position: int, csr: CSRRegister):
self.fields.append((bit_position, csr))
# TODO: verify bounds

def add_read_only_field(self, bit_position: int, bit_width: int, value: int | Enum):
assert not self.elaborated
self.ro_values.append((bit_position, bit_width, value))
# TODO: verify bounds

def elaborate(self, platform):
m = TModule()
self.elaborated = True
Expand All @@ -57,6 +66,10 @@ def _(data: Value):
def _() -> Value:
for start, csr in self.fields:
m.d.av_comb += self.value[start : start + csr.width].eq(csr._fu_read(m)["data"])

for start, width, value in self.ro_values:
m.d.av_comb += self.value[start : start + width].eq(value)

return self.value

return m
Loading

0 comments on commit 0e2887a

Please sign in to comment.