Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into tilk/async-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tilk committed Nov 12, 2024
2 parents 9a8db08 + def471b commit d068792
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 91 deletions.
17 changes: 12 additions & 5 deletions coreblocks/backend/retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,16 @@ def __init__(
max_latency=2 * 2**gen_params.rob_entries_bits,
)

layouts = self.gen_params.get(RetirementLayouts)
self.dependency_manager = DependencyContext.get()
self.core_state = Method(o=self.gen_params.get(RetirementLayouts).core_state, nonexclusive=True)
self.dependency_manager.add_dependency(CoreStateKey(), self.core_state)

self.precommit = Method(o=self.gen_params.get(RetirementLayouts).precommit, nonexclusive=True)
# The argument is only used in argument validation, it is not needed in the method body.
# A dummy combiner is provided.
self.precommit = Method(
i=layouts.precommit_in, o=layouts.precommit_out, nonexclusive=True, combiner=lambda m, args, runs: 0
)
self.dependency_manager.add_dependency(InstructionPrecommitKey(), self.precommit)

def elaborate(self, platform):
Expand Down Expand Up @@ -228,9 +233,11 @@ def flush_instr(rob_entry):
def _():
return {"flushing": core_flushing}

@def_method(m, self.precommit)
def _():
rob_entry = self.rob_peek(m)
return {"rob_id": rob_entry.rob_id, "side_fx": side_fx}
rob_id_val = Signal(self.gen_params.rob_entries_bits)

@def_method(m, self.precommit, validate_arguments=lambda rob_id: rob_id == rob_id_val)
def _(rob_id):
m.d.top_comb += rob_id_val.eq(self.rob_peek(m).rob_id)
return {"side_fx": side_fx}

return m
14 changes: 4 additions & 10 deletions coreblocks/func_blocks/csr/csr.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,12 @@ def elaborate(self, platform):
done = Signal()
call_resume = Signal()
exception = Signal()
precommitting = Signal()

current_result = Signal(self.gen_params.isa.xlen)

instr = Signal(StructLayout(self.csr_layouts.rs.data_layout.members | {"valid": 1}))

m.d.comb += ready_to_process.eq(precommitting & instr.valid & (instr.rp_s1 == 0))
m.d.comb += ready_to_process.eq(instr.valid & (instr.rp_s1 == 0))

# RISCV Zicsr spec Table 1.1
should_read_csr = Signal()
Expand All @@ -134,6 +133,9 @@ def elaborate(self, platform):

# Methods used within this Tranaction are CSRRegister internal _fu_(read|write) handlers which are always ready
with Transaction().body(m, request=(ready_to_process & ~done)):
precommit = self.dependency_manager.get_dependency(InstructionPrecommitKey())
info = precommit(m, instr.rob_id)
m.d.top_comb += exe_side_fx.eq(info.side_fx)
with m.Switch(instr.csr):
for csr_number, methods in self.regfile.items():
read, write = methods
Expand Down Expand Up @@ -257,14 +259,6 @@ def _():
# CSR instructions are never compressed, PC+4 is always next instruction
return {"pc": instr.pc + self.gen_params.isa.ilen_bytes}

# Generate precommitting signal from precommit
with Transaction().body(m):
precommit = self.dependency_manager.get_dependency(InstructionPrecommitKey())
info = precommit(m)
with m.If(instr.rob_id == info.rob_id):
m.d.comb += precommitting.eq(1)
m.d.comb += exe_side_fx.eq(info.side_fx)

return m


Expand Down
14 changes: 8 additions & 6 deletions coreblocks/func_blocks/fu/lsu/dummyLsu.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from coreblocks.func_blocks.interface.func_protocols import FuncUnit
from coreblocks.func_blocks.fu.lsu.pma import PMAChecker
from coreblocks.func_blocks.fu.lsu.lsu_requester import LSURequester
from coreblocks.interface.keys import ExceptionReportKey, CommonBusDataKey, InstructionPrecommitKey
from coreblocks.interface.keys import CoreStateKey, ExceptionReportKey, CommonBusDataKey, InstructionPrecommitKey

__all__ = ["LSUDummy", "LSUComponent"]

Expand Down Expand Up @@ -55,6 +55,11 @@ def elaborate(self, platform):
m = TModule()
flush = Signal() # exception handling, requests are not issued

with Transaction().body(m):
core_state = self.dependency_manager.get_dependency(CoreStateKey())
state = core_state(m)
m.d.comb += flush.eq(state.flushing)

# Signals for handling issue logic
request_rob_id = Signal(self.gen_params.rob_entries_bits)
rob_id_match = Signal()
Expand Down Expand Up @@ -142,11 +147,8 @@ def _():

with Transaction().body(m):
precommit = self.dependency_manager.get_dependency(InstructionPrecommitKey())
info = precommit(m)
with m.If(info.rob_id == request_rob_id):
m.d.comb += rob_id_match.eq(1)
with m.If(~info.side_fx):
m.d.comb += flush.eq(1)
precommit(m, request_rob_id)
m.d.comb += rob_id_match.eq(1)

return m

Expand Down
51 changes: 25 additions & 26 deletions coreblocks/func_blocks/fu/priv.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,32 +101,31 @@ def _(arg):

with Transaction().body(m, request=instr_valid & ~finished):
precommit = self.dm.get_dependency(InstructionPrecommitKey())
info = precommit(m)
with m.If(info.rob_id == instr_rob):
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) & ~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) & ~illegal_wfi):
# async_interrupt_active implies wfi_resume. WFI should continue normal execution
# when interrupt is enabled in xie, but disabled via global mstatus.xIE
m.d.sync += finished.eq(wfi_resume)

m.d.sync += illegal_instruction.eq(illegal_wfi | illegal_mret)
info = precommit(m, instr_rob)
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) & ~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) & ~illegal_wfi):
# async_interrupt_active implies wfi_resume. WFI should continue normal execution
# when interrupt is enabled in xie, but disabled via global mstatus.xIE
m.d.sync += finished.eq(wfi_resume)

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

@def_method(m, self.accept, ready=instr_valid & finished)
def _():
Expand Down
12 changes: 3 additions & 9 deletions coreblocks/interface/layouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ class RetirementLayouts:
def __init__(self, gen_params: GenParams):
fields = gen_params.get(CommonLayoutFields)

self.precommit = make_layout(fields.rob_id, fields.side_fx)
self.precommit_in = make_layout(fields.rob_id)

self.precommit_out = make_layout(fields.side_fx)

self.flushing = ("flushing", 1)
""" Core is currently flushed """
Expand Down Expand Up @@ -581,10 +583,6 @@ class LSULayouts:
def __init__(self, gen_params: GenParams):
fields = gen_params.get(CommonLayoutFields)

retirement = gen_params.get(RetirementLayouts)

self.precommit = retirement.precommit

self.store: LayoutListField = ("store", 1)

self.issue = make_layout(fields.addr, fields.data, fields.funct3, self.store)
Expand Down Expand Up @@ -638,10 +636,6 @@ def __init__(self, gen_params: GenParams):

self.rs = gen_params.get(RSInterfaceLayouts, rs_entries_bits=self.rs_entries_bits, data_layout=data_layout)

retirement = gen_params.get(RetirementLayouts)

self.precommit = retirement.precommit


class ExceptionRegisterLayouts:
"""Layouts used in the exception information register."""
Expand Down
37 changes: 35 additions & 2 deletions coreblocks/priv/csr/csr_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ def __init__(self, gen_params: GenParams):

self.mtval = CSRRegister(CSRAddress.MTVAL, gen_params)

self.misa = CSRRegister(
CSRAddress.MISA, gen_params, init=self._misa_value(gen_params), ro_bits=(1 << gen_params.isa.xlen) - 1
)

self.priv_mode = CSRRegister(
None,
gen_params,
Expand All @@ -94,7 +98,7 @@ def __init__(self, gen_params: GenParams):
self.priv_mode_public = AliasedCSR(CSRAddress.COREBLOCKS_TEST_PRIV_MODE, gen_params)
self.priv_mode_public.add_field(0, self.priv_mode)

self.mstatus_fields_implementation(gen_params, self.mstatus, self.mstatush)
self._mstatus_fields_implementation(gen_params, self.mstatus, self.mstatush)

def elaborate(self, platform):
m = Module()
Expand All @@ -105,7 +109,7 @@ def elaborate(self, platform):

return m

def mstatus_fields_implementation(self, gen_params: GenParams, mstatus: AliasedCSR, mstatush: AliasedCSR):
def _mstatus_fields_implementation(self, gen_params: GenParams, mstatus: AliasedCSR, mstatush: AliasedCSR):
def filter_legal_priv_mode(m: TModule, v: Value):
legal = Signal(1)
with m.Switch(v):
Expand Down Expand Up @@ -179,6 +183,35 @@ def filter_legal_priv_mode(m: TModule, v: Value):
Extension.V in gen_params.isa.extensions or Extension.F in gen_params.isa.extensions,
)

def _misa_value(self, gen_params):
misa_value = 0

misa_extension_bits = {
0: Extension.A,
1: Extension.B,
2: Extension.C,
3: Extension.D,
4: Extension.E,
5: Extension.F,
8: Extension.I,
12: Extension.M,
16: Extension.Q,
21: Extension.V,
}

for bit, extension in misa_extension_bits.items():
if extension in gen_params.isa.extensions:
misa_value |= 1 << bit

if gen_params.user_mode:
misa_value |= 1 << 20
# 7 - Hypervisor, 18 - Supervisor, 23 - Custom Extensions

xml_field_mapping = {32: XlenEncoding.W32, 64: XlenEncoding.W64, 128: XlenEncoding.W128}
misa_value |= xml_field_mapping[gen_params.isa.xlen] << (gen_params.isa.xlen - 2)

return misa_value


class GenericCSRRegisters(Elaboratable):
def __init__(self, gen_params: GenParams):
Expand Down
4 changes: 1 addition & 3 deletions test/backend/test_retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,10 @@ async def rat_process(self, sim: TestbenchContext):

async def precommit_process(self, sim: TestbenchContext):
await sim.tick() # TODO mocks inactive during first cycle
self.retc.precommit_adapter.call_init(sim)
while self.precommit_q:
info = await self.retc.precommit_adapter.call_result(sim)
info = await self.retc.precommit_adapter.call_try(sim, rob_id=self.precommit_q[0])
assert info is not None
assert info["side_fx"]
assert self.precommit_q[0] == info["rob_id"]
self.precommit_q.popleft()

@async_def_method_mock(lambda self: self.retc.mock_rf_free)
Expand Down
30 changes: 17 additions & 13 deletions test/func_blocks/csr/test_csr.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@


class CSRUnitTestCircuit(Elaboratable):
def __init__(self, gen_params, csr_count, only_legal=True):
def __init__(self, gen_params: GenParams, csr_count: int, only_legal=True):
self.gen_params = gen_params
self.csr_count = csr_count
self.only_legal = only_legal
Expand All @@ -32,7 +32,12 @@ def elaborate(self, platform):
m = Module()

m.submodules.precommit = self.precommit = AsyncTestbenchIO(
Adapter(o=self.gen_params.get(RetirementLayouts).precommit, nonexclusive=True)
Adapter(
i=self.gen_params.get(RetirementLayouts).precommit_in,
o=self.gen_params.get(RetirementLayouts).precommit_out,
nonexclusive=True,
combiner=lambda m, args, runs: args[0],
).set(with_validate_arguments=True)
)
DependencyContext.get().add_dependency(InstructionPrecommitKey(), self.precommit.adapter.iface)

Expand Down Expand Up @@ -142,11 +147,13 @@ async def process_test(self, sim: TestbenchContext):
await self.dut.update.call(sim, reg_id=op["exp"]["rs1"]["rp_s1"], reg_val=op["exp"]["rs1"]["value"])

await self.async_random_wait_geom(sim)
# TODO: this is a hack, a real method mock should be used
for _, r in self.dut.precommit.adapter.validators: # type: ignore
sim.set(r, 1)
self.dut.precommit.call_init(sim, side_fx=1) # TODO: sensible precommit handling

await self.async_random_wait_geom(sim)
res, resume_res = await CallTrigger(sim).call(self.dut.accept).call(self.dut.fetch_resume).until_done()
self.dut.fetch_resume.enable(sim)
res, resume_res = await CallTrigger(sim).call(self.dut.accept).sample(self.dut.fetch_resume).until_done()
self.dut.precommit.disable(sim)

assert res is not None and resume_res is not None
Expand Down Expand Up @@ -203,20 +210,17 @@ async def process_exception_test(self, sim: TestbenchContext):
)

await self.async_random_wait_geom(sim)
self.dut.precommit.call_init(sim, rob_id=rob_id, side_fx=1)
# TODO: this is a hack, a real method mock should be used
for _, r in self.dut.precommit.adapter.validators: # type: ignore
sim.set(r, 1)
self.dut.precommit.call_init(sim, side_fx=1)

await self.async_random_wait_geom(sim)
self.dut.accept.call_init(sim)
*_, res, report_done, report = (
await sim.tick()
.sample(self.dut.accept.outputs, self.dut.exception_report.done, self.dut.exception_report.outputs)
.until(self.dut.accept.done)
)
self.dut.accept.disable(sim)
res, report = await CallTrigger(sim).call(self.dut.accept).sample(self.dut.exception_report).until_done()
self.dut.precommit.disable(sim)

assert res["exception"] == 1
assert report_done
assert report is not None
report_dict = data_const_to_dict(report)
report_dict.pop("mtval") # mtval tested in mtval.asm test
assert {"rob_id": rob_id, "cause": ExceptionCause.ILLEGAL_INSTRUCTION, "pc": 0} == report_dict
Expand Down
Loading

0 comments on commit d068792

Please sign in to comment.