Skip to content

Commit

Permalink
RFC 36 (async/await testing) (#742)
Browse files Browse the repository at this point in the history
  • Loading branch information
tilk authored Nov 19, 2024
1 parent c19fcfe commit 75c2706
Show file tree
Hide file tree
Showing 57 changed files with 2,430 additions and 2,428 deletions.
7 changes: 3 additions & 4 deletions coreblocks/core_structs/rob.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from amaranth import *
from amaranth.lib.data import View
import amaranth.lib.memory as memory
from transactron import Method, Transaction, def_method, TModule
from transactron.lib.metrics import *
Expand All @@ -19,7 +18,7 @@ def __init__(self, gen_params: GenParams) -> None:
self.retire = Method()
self.done = Array(Signal() for _ in range(2**self.params.rob_entries_bits))
self.exception = Array(Signal() for _ in range(2**self.params.rob_entries_bits))
self.data = memory.Memory(shape=layouts.data_layout.size, depth=2**self.params.rob_entries_bits, init=[])
self.data = memory.Memory(shape=layouts.data_layout, depth=2**self.params.rob_entries_bits, init=[])
self.get_indices = Method(o=layouts.get_indices, nonexclusive=True)

self.perf_rob_wait_time = FIFOLatencyMeasurer(
Expand Down Expand Up @@ -54,8 +53,8 @@ def elaborate(self, platform):

@def_method(m, self.peek, ready=peek_possible)
def _():
return { # remove View after Amaranth upgrade
"rob_data": View(self.params.get(ROBLayouts).data_layout, read_port.data),
return {
"rob_data": read_port.data,
"rob_id": start_idx,
"exception": self.exception[start_idx],
}
Expand Down
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ testpaths =
tests
norecursedirs = '*.egg', '.*', 'build', 'dist', 'venv', '__traces__', '__pycache__'
filterwarnings =
ignore:cannot collect test class 'TestbenchContext':pytest.PytestCollectionWarning
ignore:cannot collect test class 'TestbenchIO':pytest.PytestCollectionWarning
ignore:No files were found in testpaths:pytest.PytestConfigWarning:
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
./amaranth-stubs/ # can't use -e -- pyright doesn't see the stubs then :(
amaranth-yosys==0.40.0.0.post100
git+https://github.com/amaranth-lang/amaranth@5e59189c2b8689a453891e17e378bf73806efdd3
git+https://github.com/amaranth-lang/amaranth@994fa815995b1ac5b3c708915dcece2a45796569
dataclasses-json==0.6.3
35 changes: 18 additions & 17 deletions test/backend/test_annoucement.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from coreblocks.interface.layouts import *
from coreblocks.params import GenParams
from coreblocks.params.configurations import test_core_config
from transactron.testing import TestCaseWithSimulator, TestbenchIO
from transactron.testing import TestCaseWithSimulator, TestbenchIO, TestbenchContext


class BackendTestCircuit(Elaboratable):
Expand Down Expand Up @@ -104,32 +104,33 @@ def generate_producer(self, i: int):
results to its output FIFO. This records will be next serialized by FUArbiter.
"""

def producer():
async def producer(sim: TestbenchContext):
inputs = self.fu_inputs[i]
for rob_id, result, rp_dst in inputs:
io: TestbenchIO = self.m.fu_fifo_ins[i]
yield from io.call_init(rob_id=rob_id, result=result, rp_dst=rp_dst)
yield from self.random_wait(self.max_wait)
io.call_init(sim, rob_id=rob_id, result=result, rp_dst=rp_dst)
await self.random_wait(sim, self.max_wait)
self.producer_end[i] = True

return producer

def consumer(self):
yield from self.m.rs_announce_val_tbio.enable()
yield from self.m.rob_mark_done_tbio.enable()
async def consumer(self, sim: TestbenchContext):
# TODO: this test doesn't do anything, fix it!
self.m.rs_announce_val_tbio.enable(sim)
self.m.rob_mark_done_tbio.enable(sim)
while reduce(and_, self.producer_end, True):
# All 3 methods (in RF, RS and ROB) need to be enabled for the result
# announcement transaction to take place. We want to have at least one
# method disabled most of the time, so that the transaction is performed
# only when we enable it inside the loop. Otherwise the transaction could
# get executed at any time, particularly when we wouldn't be monitoring it
yield from self.m.rf_announce_val_tbio.enable()
self.m.rf_announce_val_tbio.enable(sim)

rf_result = yield from self.m.rf_announce_val_tbio.method_argument()
rs_result = yield from self.m.rs_announce_val_tbio.method_argument()
rob_result = yield from self.m.rob_mark_done_tbio.method_argument()
rf_result = self.m.rf_announce_val_tbio.get_outputs(sim)
rs_result = self.m.rs_announce_val_tbio.get_outputs(sim)
rob_result = self.m.rob_mark_done_tbio.get_outputs(sim)

yield from self.m.rf_announce_val_tbio.disable()
self.m.rf_announce_val_tbio.disable(sim)

assert rf_result is not None
assert rs_result is not None
Expand All @@ -144,20 +145,20 @@ def consumer(self):
del self.expected_output[t]
else:
self.expected_output[t] -= 1
yield from self.random_wait(self.max_wait)
await self.random_wait(sim, self.max_wait)

def test_one_out(self):
self.fu_count = 1
self.initialize()
with self.run_simulation(self.m) as sim:
sim.add_process(self.consumer)
sim.add_testbench(self.consumer)
for i in range(self.fu_count):
sim.add_process(self.generate_producer(i))
sim.add_testbench(self.generate_producer(i))

def test_many_out(self):
self.fu_count = 4
self.initialize()
with self.run_simulation(self.m) as sim:
sim.add_process(self.consumer)
sim.add_testbench(self.consumer)
for i in range(self.fu_count):
sim.add_process(self.generate_producer(i))
sim.add_testbench(self.generate_producer(i))
35 changes: 20 additions & 15 deletions test/backend/test_retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from coreblocks.params import GenParams
from coreblocks.interface.layouts import ROBLayouts, RFLayouts, SchedulerLayouts
from coreblocks.params.configurations import test_core_config
from transactron.lib.adapters import AdapterTrans

from transactron.testing import *
from collections import deque
Expand Down Expand Up @@ -120,42 +121,46 @@ def setup_method(self):
# (and the retirement code doesn't have any special behaviour to handle these cases), but in this simple
# test we don't care to make sure that the randomly generated inputs are correct in this way.

@def_method_mock(lambda self: self.retc.mock_rob_retire, enable=lambda self: bool(self.submit_q), sched_prio=1)
@def_method_mock(lambda self: self.retc.mock_rob_retire, enable=lambda self: bool(self.submit_q))
def retire_process(self):
self.submit_q.popleft()
@MethodMock.effect
def eff():
self.submit_q.popleft()

@def_method_mock(lambda self: self.retc.mock_rob_peek, enable=lambda self: bool(self.submit_q))
def peek_process(self):
return self.submit_q[0]

def free_reg_process(self):
async def free_reg_process(self, sim: TestbenchContext):
while self.rf_exp_q:
reg = yield from self.retc.free_rf_adapter.call()
reg = await self.retc.free_rf_adapter.call(sim)
assert reg["reg_id"] == self.rf_exp_q.popleft()

def rat_process(self):
async def rat_process(self, sim: TestbenchContext):
while self.rat_map_q:
current_map = self.rat_map_q.popleft()
wait_cycles = 0
# this test waits for next rat pair to be correctly set and will timeout if that assignment fails
while (yield self.retc.rat.entries[current_map["rl_dst"]]) != current_map["rp_dst"]:
while sim.get(self.retc.rat.entries[current_map["rl_dst"]]) != current_map["rp_dst"]:
wait_cycles += 1
if wait_cycles >= self.cycles + 10:
assert False, "RAT entry was not updated"
yield Tick()
await sim.tick()
assert not self.submit_q
assert not self.rf_free_q

def precommit_process(self):
async def precommit_process(self, sim: TestbenchContext):
while self.precommit_q:
info = yield from self.retc.precommit_adapter.call_try(rob_id=self.precommit_q[0])
info = await self.retc.precommit_adapter.call_try(sim, rob_id=self.precommit_q[0])
assert info is not None
assert info["side_fx"]
self.precommit_q.popleft()

@def_method_mock(lambda self: self.retc.mock_rf_free, sched_prio=2)
@def_method_mock(lambda self: self.retc.mock_rf_free)
def rf_free_process(self, reg_id):
assert reg_id == self.rf_free_q.popleft()
@MethodMock.effect
def eff():
assert reg_id == self.rf_free_q.popleft()

@def_method_mock(lambda self: self.retc.mock_exception_cause)
def exception_cause_process(self):
Expand All @@ -174,7 +179,7 @@ def mock_trap_entry_process(self):
pass

@def_method_mock(lambda self: self.retc.mock_fetch_continue)
def mock_fetch_continue_process(self):
def mock_fetch_continue_process(self, pc):
pass

@def_method_mock(lambda self: self.retc.mock_async_interrupt_cause)
Expand All @@ -184,6 +189,6 @@ def mock_async_interrupt_cause(self):
def test_rand(self):
self.retc = RetirementTestCircuit(self.gen_params)
with self.run_simulation(self.retc) as sim:
sim.add_process(self.free_reg_process)
sim.add_process(self.rat_process)
sim.add_process(self.precommit_process)
sim.add_testbench(self.free_reg_process)
sim.add_testbench(self.rat_process)
sim.add_testbench(self.precommit_process)
Loading

0 comments on commit 75c2706

Please sign in to comment.