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

Implement LSU clear #499

Merged
merged 2 commits into from
Nov 13, 2023
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
21 changes: 13 additions & 8 deletions coreblocks/lsu/dummyLsu.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,19 @@ def elaborate(self, platform):

m.d.sync += self.loadedData.eq(self.postprocess_load_data(m, fetched.data, addr))

with m.If(fetched.err):
cause = Mux(is_load, ExceptionCause.LOAD_ACCESS_FAULT, ExceptionCause.STORE_ACCESS_FAULT)
self.report(m, rob_id=self.current_instr.rob_id, cause=cause)
# no clear (synonymous with instruction valid signal) was asserted
with m.If(self.current_instr.valid):
with m.If(fetched.err):
cause = Mux(is_load, ExceptionCause.LOAD_ACCESS_FAULT, ExceptionCause.STORE_ACCESS_FAULT)
self.report(m, rob_id=self.current_instr.rob_id, cause=cause)

m.d.sync += self.op_exception.eq(fetched.err)
m.d.sync += self.result_ready.eq(1)
m.d.sync += self.op_exception.eq(fetched.err)
m.d.sync += self.result_ready.eq(1)
with m.Else():
m.next = "Start"

with m.If(self.get_result_ack):
# result read ack or clear asserted
with m.If(self.get_result_ack | (self.result_ready & ~self.current_instr.valid)):
m.d.sync += self.result_ready.eq(0)
m.d.sync += self.op_exception.eq(0)
m.next = "Start"
Expand Down Expand Up @@ -271,9 +276,9 @@ def _(rob_id: Value):

@def_method(m, self.clear)
def _():
# TODO: clearing internal lsu component ;)
m.d.sync += current_instr.eq(0)
m.d.sync += current_instr.valid.eq(0)
Copy link
Contributor

Choose a reason for hiding this comment

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

If you have ready "load" instruction to execute, and you call clear in the same cycle, then load will start regardless of clear because (self.execute | is_load) will be true and instr_ready will be false first in next cycle.

m.d.sync += reserved.eq(0)
m.d.comb += internal.execute.eq(0)

return m

Expand Down
123 changes: 123 additions & 0 deletions test/lsu/test_dummylsu.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def elaborate(self, platform):
m.submodules.update_mock = self.update = TestbenchIO(AdapterTrans(func_unit.update))
m.submodules.get_result_mock = self.get_result = TestbenchIO(AdapterTrans(func_unit.get_result))
m.submodules.precommit_mock = self.precommit = TestbenchIO(AdapterTrans(func_unit.precommit))
m.submodules.clear_mock = self.clear = TestbenchIO(AdapterTrans(func_unit.clear))
self.io_in = WishboneInterfaceWrapper(self.bus.wbMaster)
m.submodules.bus = self.bus
return m
Expand Down Expand Up @@ -489,3 +490,125 @@ def exception_consumer(arg):
with self.run_simulation(self.test_module) as sim:
sim.add_sync_process(self.process)
sim.add_sync_process(exception_consumer)


class TestDummyLSUClear(TestCaseWithSimulator):
def setUp(self):
self.gp = GenParams(test_core_config)
self.test_module = DummyLSUTestCircuit(self.gp)

def generate_instr(self, *, is_store: bool = False, **kwargs):
addr = (random.randrange(1, 2**self.gp.isa.xlen) // 4) * 4
return {
"rp_s1": 0,
"rp_s2": 0,
"rp_dst": 0,
"rob_id": 0,
"exec_fn": {"op_type": OpType.STORE if is_store else OpType.LOAD, "funct3": Funct3.W, "funct7": 0},
"s1_val": addr,
"s2_val": 0,
"imm": 0,
} | kwargs, addr >> 2

def check_second_instr_liveness(self, m, is_store):
yield from m.select.call()
instr, addr = self.generate_instr(is_store=is_store)
yield from m.insert.call(rs_data=instr, rs_entry_id=0)
if is_store:
yield from m.precommit.call(rob_id=0)
yield from m.io_in.slave_wait()
yield from m.io_in.slave_verify(exp_addr=addr, exp_data=0, exp_we=is_store, exp_sel=0xF)
if not is_store:
yield from m.io_in.slave_respond(data=0xCAFEBABE)
self.assertEqual(0xCAFEBABE, (yield from m.get_result.call())["result"])

def test_clear_before_bus_request(self):
"""
Tests clear method when it's called before request was sent to the bus
"""

@def_method_mock(lambda: self.test_module.exception_report)
def exception_consumer(arg):
self.assertTrue(False)

def process():
m = self.test_module
for is_store in (False, True):
# first instruction
yield from m.select.call()
# instruction that won't execute immediately since not all operands are ready
instr, addr = self.generate_instr(is_store=is_store, rp_s1=1)
yield from m.insert.call(rs_data=instr, rs_entry_id=0)
yield from m.clear.call()

self.check_second_instr_liveness(m, is_store)

with self.run_simulation(self.test_module) as sim:
sim.add_sync_process(process)
sim.add_sync_process(exception_consumer)

def test_clear_after_bus_request(self):
"""
Test clear method when it's called after the bus request was sent,
but before the response on the bus arrived.
"""

@def_method_mock(lambda: self.test_module.exception_report)
def exception_consumer(arg):
self.assertTrue(False)

def process():
m = self.test_module
for is_store in (False, True):
# first instruction
yield from m.select.call()
instr, addr = self.generate_instr(is_store=is_store)
yield from m.insert.call(rs_data=instr, rs_entry_id=0)
if is_store:
yield from m.precommit.call(rob_id=0)
yield from m.io_in.slave_wait()
yield from m.io_in.slave_verify(exp_addr=addr, exp_data=0, exp_we=is_store, exp_sel=0xF)
yield from m.clear.call()
# needs some response regardless of being load/store
yield from m.io_in.slave_respond(data=0xDEADBEEF)

yield from self.check_second_instr_liveness(m, is_store)

with self.run_simulation(self.test_module) as sim:
sim.add_sync_process(process)
sim.add_sync_process(exception_consumer)

def test_clear_after_bus_response(self):
"""
Test clear method when it's called after the bus request was sent
and after response from the bus arrived, but wasn't read by
get_result yet
"""

@def_method_mock(lambda: self.test_module.exception_report)
def exception_consumer(arg):
self.assertTrue(False)

def process():
m = self.test_module
for is_store in (False, True):
# first instruction
yield from m.select.call()
instr, addr = self.generate_instr(is_store=is_store)
yield from m.insert.call(rs_data=instr, rs_entry_id=0)
if is_store:
yield from m.precommit.call(rob_id=0)
yield from m.io_in.slave_wait()
yield from m.io_in.slave_verify(exp_addr=addr, exp_data=0, exp_we=is_store, exp_sel=0xF)
# needs some response regardless of being load/store
yield from m.io_in.slave_respond(data=0xDEADBEEF)
# wait for get_result to become ready before we call clear
while (yield from m.get_result.call_try()) is None:
pass
yield from m.clear.call()

yield from self.check_second_instr_liveness(m, is_store)

with self.run_simulation(self.test_module) as sim:
sim.add_sync_process(process)
sim.add_sync_process(exception_consumer)
Loading