diff --git a/test/common/infrastructure.py b/test/common/infrastructure.py index d3903738f..29dd199e5 100644 --- a/test/common/infrastructure.py +++ b/test/common/infrastructure.py @@ -1,3 +1,4 @@ +import sys import os import random import unittest @@ -39,7 +40,10 @@ def __init__(self, dut: _T_HasElaborate): self._io: dict[str, _T_nested_collection[TestbenchIO]] = {} def __getattr__(self, name: str) -> Any: - return self._io[name] + try: + return self._io[name] + except KeyError: + raise AttributeError(f"No mock for '{name}'") def elaborate(self, platform): def transform_methods_to_testbenchios( @@ -181,6 +185,21 @@ def run(self) -> bool: class TestCaseWithSimulator(unittest.TestCase): + def add_class_mocks(self, sim: PysimSimulator) -> None: + for key in dir(self): + val = getattr(self, key) + if hasattr(val, "_transactron_testing_process"): + sim.add_sync_process(val) + + def add_local_mocks(self, sim: PysimSimulator, frame_locals: dict) -> None: + for key, val in frame_locals.items(): + if hasattr(val, "_transactron_testing_process"): + sim.add_sync_process(val) + + def add_all_mocks(self, sim: PysimSimulator, frame_locals: dict) -> None: + self.add_class_mocks(sim) + self.add_local_mocks(sim, frame_locals) + @contextmanager def run_simulation(self, module: HasElaborate, max_cycles: float = 10e4, add_transaction_module=True): traces_file = None @@ -190,6 +209,7 @@ def run_simulation(self, module: HasElaborate, max_cycles: float = 10e4, add_tra sim = PysimSimulator( module, max_cycles=max_cycles, add_transaction_module=add_transaction_module, traces_file=traces_file ) + self.add_all_mocks(sim, sys._getframe(2).f_locals) yield sim res = sim.run() diff --git a/test/common/sugar.py b/test/common/sugar.py index e0ac3473c..de1dc5e21 100644 --- a/test/common/sugar.py +++ b/test/common/sugar.py @@ -76,6 +76,7 @@ def mock(func_self=None, /) -> TestGen[None]: assert isinstance(tb, TestbenchIO) yield from tb.method_handle_loop(f, extra_settle_count=sched_prio, **kw) + mock._transactron_testing_process = 1 # type: ignore return mock return decorator diff --git a/test/frontend/test_fetch.py b/test/frontend/test_fetch.py index 734309d4b..95c9d97b4 100644 --- a/test/frontend/test_fetch.py +++ b/test/frontend/test_fetch.py @@ -56,63 +56,59 @@ def setUp(self) -> None: self.instr_queue = deque() self.iterations = 500 + self.input_q = deque() + self.output_q = deque() random.seed(422) - def cache_processes(self): - input_q = deque() - output_q = deque() + def cache_process(self): + yield Passive() - def cache_process(): - yield Passive() + next_pc = self.gen_params.start_pc - next_pc = self.gen_params.start_pc - - while True: - while len(input_q) == 0: - yield - - while random.random() < 0.5: - yield + while True: + while len(self.input_q) == 0: + yield - addr = input_q.popleft() - is_branch = random.random() < 0.15 + while random.random() < 0.5: + yield - # exclude branches and jumps - data = random.randrange(2**self.gen_params.isa.ilen) & ~0b1111111 + addr = self.input_q.popleft() + is_branch = random.random() < 0.15 - # randomize being a branch instruction - if is_branch: - data |= 0b1100000 + # exclude branches and jumps + data = random.randrange(2**self.gen_params.isa.ilen) & ~0b1111111 - output_q.append({"instr": data, "error": 0}) + # randomize being a branch instruction + if is_branch: + data |= 0b1100000 - # Speculative fetch. Skip, because this instruction shouldn't be executed. - if addr != next_pc: - continue + self.output_q.append({"instr": data, "error": 0}) - next_pc = addr + self.gen_params.isa.ilen_bytes - if is_branch: - next_pc = random.randrange(2**self.gen_params.isa.ilen) & ~0b11 + # Speculative fetch. Skip, because this instruction shouldn't be executed. + if addr != next_pc: + continue - self.instr_queue.append( - { - "instr": data, - "pc": addr, - "is_branch": is_branch, - "next_pc": next_pc, - } - ) + next_pc = addr + self.gen_params.isa.ilen_bytes + if is_branch: + next_pc = random.randrange(2**self.gen_params.isa.ilen) & ~0b11 - @def_method_mock(lambda: self.icache.issue_req_io, enable=lambda: len(input_q) < 2, sched_prio=1) - def issue_req_mock(addr): - input_q.append(addr) + self.instr_queue.append( + { + "instr": data, + "pc": addr, + "is_branch": is_branch, + "next_pc": next_pc, + } + ) - @def_method_mock(lambda: self.icache.accept_res_io, enable=lambda: len(output_q) > 0) - def accept_res_mock(): - return output_q.popleft() + @def_method_mock(lambda self: self.icache.issue_req_io, enable=lambda self: len(self.input_q) < 2, sched_prio=1) + def issue_req_mock(self, addr): + self.input_q.append(addr) - return issue_req_mock, accept_res_mock, cache_process + @def_method_mock(lambda self: self.icache.accept_res_io, enable=lambda self: len(self.output_q) > 0) + def accept_res_mock(self): + return self.output_q.popleft() def fetch_out_check(self): for _ in range(self.iterations): @@ -129,12 +125,8 @@ def fetch_out_check(self): self.assertEqual(v["instr"], instr["instr"]) def test(self): - issue_req_mock, accept_res_mock, cache_process = self.cache_processes() - with self.run_simulation(self.m) as sim: - sim.add_sync_process(issue_req_mock) - sim.add_sync_process(accept_res_mock) - sim.add_sync_process(cache_process) + sim.add_sync_process(self.cache_process) sim.add_sync_process(self.fetch_out_check) @@ -154,6 +146,8 @@ def setUp(self) -> None: self.mem = {} self.memerr = set() + self.input_q = deque() + self.output_q = deque() random.seed(422) @@ -201,39 +195,33 @@ def gen_instr_seq(self): pc = next_pc - def cache_processes(self): - input_q = deque() - output_q = deque() + def cache_process(self): + yield Passive() - def cache_process(): - yield Passive() - - while True: - while len(input_q) == 0: - yield - - while random.random() < 0.5: - yield + while True: + while len(self.input_q) == 0: + yield - req_addr = input_q.popleft() + while random.random() < 0.5: + yield - def get_mem_or_random(addr): - return self.mem[addr] if addr in self.mem else random.randrange(2**16) + req_addr = self.input_q.popleft() - data = (get_mem_or_random(req_addr + 2) << 16) | get_mem_or_random(req_addr) + def get_mem_or_random(addr): + return self.mem[addr] if addr in self.mem else random.randrange(2**16) - err = (req_addr in self.memerr) or (req_addr + 2 in self.memerr) - output_q.append({"instr": data, "error": err}) + data = (get_mem_or_random(req_addr + 2) << 16) | get_mem_or_random(req_addr) - @def_method_mock(lambda: self.icache.issue_req_io, enable=lambda: len(input_q) < 2, sched_prio=1) - def issue_req_mock(addr): - input_q.append(addr) + err = (req_addr in self.memerr) or (req_addr + 2 in self.memerr) + self.output_q.append({"instr": data, "error": err}) - @def_method_mock(lambda: self.icache.accept_res_io, enable=lambda: len(output_q) > 0) - def accept_res_mock(): - return output_q.popleft() + @def_method_mock(lambda self: self.icache.issue_req_io, enable=lambda self: len(self.input_q) < 2, sched_prio=1) + def issue_req_mock(self, addr): + self.input_q.append(addr) - return issue_req_mock, accept_res_mock, cache_process + @def_method_mock(lambda self: self.icache.accept_res_io, enable=lambda self: len(self.output_q) > 0) + def accept_res_mock(self): + return self.output_q.popleft() def fetch_out_check(self): while self.instr_queue: @@ -254,12 +242,8 @@ def fetch_out_check(self): yield from self.verify_branch.call(next_pc=instr["next_pc"]) def test(self): - issue_req_mock, accept_res_mock, cache_process = self.cache_processes() - self.gen_instr_seq() with self.run_simulation(self.m) as sim: - sim.add_sync_process(issue_req_mock) - sim.add_sync_process(accept_res_mock) - sim.add_sync_process(cache_process) + sim.add_sync_process(self.cache_process) sim.add_sync_process(self.fetch_out_check) diff --git a/test/frontend/test_icache.py b/test/frontend/test_icache.py index 6142d37a2..e52b73ba8 100644 --- a/test/frontend/test_icache.py +++ b/test/frontend/test_icache.py @@ -301,6 +301,10 @@ def setUp(self) -> None: self.refill_requests = deque() self.issued_requests = deque() + self.refill_in_fly = False + self.refill_word_cnt = 0 + self.refill_addr = 0 + def init_module(self, ways, sets) -> None: self.gen_params = GenParams( test_core_config.replace( @@ -313,47 +317,37 @@ def init_module(self, ways, sets) -> None: self.cp = self.gen_params.icache_params self.m = ICacheTestCircuit(self.gen_params) - def refiller_processes(self): - refill_in_fly = False - refill_word_cnt = 0 - refill_addr = 0 - - @def_method_mock(lambda: self.m.refiller.start_refill_mock) - def start_refill_mock(addr): - nonlocal refill_in_fly, refill_word_cnt, refill_addr - self.refill_requests.append(addr) - refill_word_cnt = 0 - refill_in_fly = True - refill_addr = addr - - @def_method_mock(lambda: self.m.refiller.accept_refill_mock, enable=lambda: refill_in_fly) - def accept_refill_mock(): - nonlocal refill_in_fly, refill_word_cnt, refill_addr - - addr = refill_addr + refill_word_cnt * self.cp.word_width_bytes - data = self.load_or_gen_mem(addr) - if self.gen_params.isa.xlen == 64: - data = self.load_or_gen_mem(addr + 4) << 32 | data + @def_method_mock(lambda self: self.m.refiller.start_refill_mock) + def start_refill_mock(self, addr): + self.refill_requests.append(addr) + self.refill_word_cnt = 0 + self.refill_in_fly = True + self.refill_addr = addr - refill_word_cnt += 1 + @def_method_mock(lambda self: self.m.refiller.accept_refill_mock, enable=lambda self: self.refill_in_fly) + def accept_refill_mock(self): + addr = self.refill_addr + self.refill_word_cnt * self.cp.word_width_bytes + data = self.load_or_gen_mem(addr) + if self.gen_params.isa.xlen == 64: + data = self.load_or_gen_mem(addr + 4) << 32 | data - err = addr in self.bad_addrs - if self.gen_params.isa.xlen == 64: - err = err or (addr + 4) in self.bad_addrs + self.refill_word_cnt += 1 - last = refill_word_cnt == self.cp.words_in_block or err + err = addr in self.bad_addrs + if self.gen_params.isa.xlen == 64: + err = err or (addr + 4) in self.bad_addrs - if last: - refill_in_fly = False + last = self.refill_word_cnt == self.cp.words_in_block or err - return { - "addr": addr, - "data": data, - "error": err, - "last": last, - } + if last: + self.refill_in_fly = False - return start_refill_mock, accept_refill_mock + return { + "addr": addr, + "data": data, + "error": err, + "last": last, + } def load_or_gen_mem(self, addr: int): if addr not in self.mem: @@ -428,11 +422,7 @@ def cache_user_process(): yield from self.call_cache(random.randrange(0, self.cp.block_size_bytes * self.cp.num_of_sets, 4)) self.assertEqual(len(self.refill_requests), 0) - start_refill_mock, accept_refill_mock = self.refiller_processes() - with self.run_simulation(self.m) as sim: - sim.add_sync_process(start_refill_mock) - sim.add_sync_process(accept_refill_mock) sim.add_sync_process(cache_user_process) def test_2_way(self): @@ -450,11 +440,7 @@ def cache_process(): yield from self.call_cache(0x00020004) self.assertEqual(len(self.refill_requests), 0) - start_refill_mock, accept_refill_mock = self.refiller_processes() - with self.run_simulation(self.m) as sim: - sim.add_sync_process(start_refill_mock) - sim.add_sync_process(accept_refill_mock) sim.add_sync_process(cache_process) # Tests whether the cache is fully pipelined and the latency between requests and response is exactly one cycle. @@ -550,11 +536,7 @@ def cache_process(): yield yield from self.m.accept_res.disable() - start_refill_mock, accept_refill_mock = self.refiller_processes() - with self.run_simulation(self.m) as sim: - sim.add_sync_process(start_refill_mock) - sim.add_sync_process(accept_refill_mock) sim.add_sync_process(cache_process) def test_flush(self): @@ -627,11 +609,7 @@ def cache_process(): yield from self.call_cache(0x00010000) self.expect_refill(0x00010000) - start_refill_mock, accept_refill_mock = self.refiller_processes() - with self.run_simulation(self.m) as sim: - sim.add_sync_process(start_refill_mock) - sim.add_sync_process(accept_refill_mock) sim.add_sync_process(cache_process) def test_errors(self): @@ -704,11 +682,7 @@ def cache_process(): yield yield from self.m.accept_res.disable() - start_refill_mock, accept_refill_mock = self.refiller_processes() - with self.run_simulation(self.m) as sim: - sim.add_sync_process(start_refill_mock) - sim.add_sync_process(accept_refill_mock) sim.add_sync_process(cache_process) def test_random(self): @@ -738,10 +712,6 @@ def receiver(): while random.random() < 0.2: yield - start_refill_mock, accept_refill_mock = self.refiller_processes() - with self.run_simulation(self.m) as sim: - sim.add_sync_process(start_refill_mock) - sim.add_sync_process(accept_refill_mock) sim.add_sync_process(sender) sim.add_sync_process(receiver) diff --git a/test/lsu/test_dummylsu.py b/test/lsu/test_dummylsu.py index 58d554458..d93c59215 100644 --- a/test/lsu/test_dummylsu.py +++ b/test/lsu/test_dummylsu.py @@ -257,7 +257,6 @@ def exception_consumer(arg): sim.add_sync_process(self.wishbone_slave) sim.add_sync_process(self.inserter) sim.add_sync_process(self.consumer) - sim.add_sync_process(exception_consumer) class TestDummyLSULoadsCycles(TestCaseWithSimulator): @@ -316,7 +315,6 @@ def exception_consumer(arg): with self.run_simulation(self.test_module) as sim: sim.add_sync_process(self.one_instr_test) - sim.add_sync_process(exception_consumer) class TestDummyLSUStores(TestCaseWithSimulator): @@ -445,7 +443,6 @@ def exception_consumer(arg): sim.add_sync_process(self.inserter) sim.add_sync_process(self.get_resulter) sim.add_sync_process(self.precommiter) - sim.add_sync_process(exception_consumer) class TestDummyLSUFence(TestCaseWithSimulator): @@ -491,4 +488,3 @@ 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) diff --git a/test/lsu/test_pma.py b/test/lsu/test_pma.py index 78869dd21..d02b218c0 100644 --- a/test/lsu/test_pma.py +++ b/test/lsu/test_pma.py @@ -125,4 +125,3 @@ 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) diff --git a/test/stages/test_retirement.py b/test/stages/test_retirement.py index dca3d7d65..049d3734c 100644 --- a/test/stages/test_retirement.py +++ b/test/stages/test_retirement.py @@ -155,10 +155,5 @@ def test_rand(self): yield from self.retc.mock_fetch_stall.enable() # To be fixed with self.run_simulation(self.retc) as sim: - sim.add_sync_process(self.retire_process) - sim.add_sync_process(self.peek_process) sim.add_sync_process(self.free_reg_process) sim.add_sync_process(self.rat_process) - sim.add_sync_process(self.rf_free_process) - sim.add_sync_process(self.precommit_process) - sim.add_sync_process(self.exception_cause_process) diff --git a/test/structs_common/test_exception.py b/test/structs_common/test_exception.py index 8b91b6e6d..43b6f4512 100644 --- a/test/structs_common/test_exception.py +++ b/test/structs_common/test_exception.py @@ -69,4 +69,3 @@ def process_rob_idx_mock(): with self.run_simulation(m) as sim: sim.add_sync_process(process_test) - sim.add_sync_process(process_rob_idx_mock) diff --git a/test/transactions/test_simultaneous.py b/test/transactions/test_simultaneous.py index 173fbd61c..9a55bce36 100644 --- a/test/transactions/test_simultaneous.py +++ b/test/transactions/test_simultaneous.py @@ -168,5 +168,4 @@ def process(): self.assertIn(result, possibles) with self.run_simulation(m) as sim: - sim.add_sync_process(target_process) sim.add_sync_process(process) diff --git a/test/transactions/test_transaction_lib.py b/test/transactions/test_transaction_lib.py index e096f7860..2747ceaff 100644 --- a/test/transactions/test_transaction_lib.py +++ b/test/transactions/test_transaction_lib.py @@ -435,13 +435,11 @@ def test_method_transformer_dicts(self): self.m = MethodMapTestCircuit(4, False, True) with self.run_simulation(self.m) as sim: sim.add_sync_process(self.source) - sim.add_sync_process(self.target) def test_method_transformer_with_methods(self): self.m = MethodMapTestCircuit(4, True, True) with self.run_simulation(self.m) as sim: sim.add_sync_process(self.source) - sim.add_sync_process(self.target) class TestMethodFilter(TestCaseWithSimulator): @@ -449,6 +447,7 @@ def initialize(self): self.iosize = 4 self.layout = data_layout(self.iosize) self.target = TestbenchIO(Adapter(i=self.layout, o=self.layout)) + self.cmeth = TestbenchIO(Adapter(i=self.layout, o=data_layout(1))) def source(self): for i in range(2**self.iosize): @@ -469,15 +468,12 @@ def cmeth_mock(self, data): @parameterized.expand([(True,), (False,)]) def test_method_filter_with_methods(self, use_condition): self.initialize() - self.cmeth = TestbenchIO(Adapter(i=self.layout, o=data_layout(1))) self.tc = SimpleTestCircuit( MethodFilter(self.target.adapter.iface, self.cmeth.adapter.iface, use_condition=use_condition) ) m = ModuleConnector(test_circuit=self.tc, target=self.target, cmeth=self.cmeth) with self.run_simulation(m) as sim: sim.add_sync_process(self.source) - sim.add_sync_process(self.target_mock) - sim.add_sync_process(self.cmeth_mock) @parameterized.expand([(True,), (False,)]) def test_method_filter(self, use_condition): @@ -487,10 +483,9 @@ def condition(_, v): return v[0] self.tc = SimpleTestCircuit(MethodFilter(self.target.adapter.iface, condition, use_condition=use_condition)) - m = ModuleConnector(test_circuit=self.tc, target=self.target) + m = ModuleConnector(test_circuit=self.tc, target=self.target, cmeth=self.cmeth) with self.run_simulation(m) as sim: sim.add_sync_process(self.source) - sim.add_sync_process(self.target_mock) class MethodProductTestCircuit(Elaboratable): @@ -569,75 +564,74 @@ def method_process(): class TestSerializer(TestCaseWithSimulator): - def test_serial(self): - test_count = 100 + def setUp(self): + self.test_count = 100 - port_count = 2 - data_width = 5 + self.port_count = 2 + self.data_width = 5 - requestor_rand = 4 + self.requestor_rand = 4 - layout = [("field", data_width)] + layout = [("field", self.data_width)] self.req_method = TestbenchIO(Adapter(i=layout)) self.resp_method = TestbenchIO(Adapter(o=layout)) self.test_circuit = SimpleTestCircuit( Serializer( - port_count=port_count, + port_count=self.port_count, serialized_req_method=self.req_method.adapter.iface, serialized_resp_method=self.resp_method.adapter.iface, ) ) - m = ModuleConnector(test_circuit=self.test_circuit, req_method=self.req_method, resp_method=self.resp_method) + self.m = ModuleConnector( + test_circuit=self.test_circuit, req_method=self.req_method, resp_method=self.resp_method + ) random.seed(14) - serialized_data = deque() - port_data = [deque() for _ in range(port_count)] + self.serialized_data = deque() + self.port_data = [deque() for _ in range(self.port_count)] - got_request = False + self.got_request = False - def random_wait(rand: int): - yield from self.tick(random.randrange(rand) + 1) + def random_wait(self, rand: int): + yield from self.tick(random.randrange(rand) + 1) - @def_method_mock(lambda: self.req_method, enable=lambda: not got_request) - def serial_req_mock(field): - nonlocal got_request - serialized_data.append(field) - got_request = True - - @def_method_mock(lambda: self.resp_method, enable=lambda: got_request) - def serial_resp_mock(): - nonlocal got_request - got_request = False - return {"field": serialized_data[-1]} - - def requestor(i: int): - def f(): - for _ in range(test_count): - d = random.randrange(2**data_width) - yield from self.test_circuit.serialize_in[i].call(field=d) - port_data[i].append(d) - yield from random_wait(requestor_rand) - - return f - - def responder(i: int): - def f(): - for _ in range(test_count): - data_out = yield from self.test_circuit.serialize_out[i].call() - self.assertEqual(port_data[i].popleft(), data_out["field"]) - yield from random_wait(requestor_rand) - - return f + @def_method_mock(lambda self: self.req_method, enable=lambda self: not self.got_request) + def serial_req_mock(self, field): + self.serialized_data.append(field) + self.got_request = True - with self.run_simulation(m) as sim: - sim.add_sync_process(serial_req_mock) - sim.add_sync_process(serial_resp_mock) - for i in range(port_count): - sim.add_sync_process(requestor(i)) - sim.add_sync_process(responder(i)) + @def_method_mock(lambda self: self.resp_method, enable=lambda self: self.got_request) + def serial_resp_mock(self): + self.got_request = False + return {"field": self.serialized_data[-1]} + + def requestor(self, i: int): + def f(): + for _ in range(self.test_count): + d = random.randrange(2**self.data_width) + yield from self.test_circuit.serialize_in[i].call(field=d) + self.port_data[i].append(d) + yield from self.random_wait(self.requestor_rand) + + return f + + def responder(self, i: int): + def f(): + for _ in range(self.test_count): + data_out = yield from self.test_circuit.serialize_out[i].call() + self.assertEqual(self.port_data[i].popleft(), data_out["field"]) + yield from self.random_wait(self.requestor_rand) + + return f + + def test_serial(self): + with self.run_simulation(self.m) as sim: + for i in range(self.port_count): + sim.add_sync_process(self.requestor(i)) + sim.add_sync_process(self.responder(i)) class TestMethodTryProduct(TestCaseWithSimulator): @@ -777,5 +771,4 @@ def process(): self.assertIn(selection, [c1, 2 * c2, 3 * c3]) with self.run_simulation(m) as sim: - sim.add_sync_process(target_process) sim.add_sync_process(process)