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

User Mode and remaining mstatus fields #729

Merged
merged 20 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why there is no SD field?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because SD field has variable position (last bit of mstatus different on 32/64), but now I added it with value -1, so position is calculated from MstatusFieldOffsets.SD % mstatus.width

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