From 0809619bfc78e1cd4b28374ec8c868f3108d500b Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Thu, 14 Nov 2024 15:02:30 +0100 Subject: [PATCH 1/3] Changes for Amaranth 0.5.3 --- amaranth-stubs | 2 +- requirements.txt | 2 +- test/func_blocks/fu/fpu/test_fpu_error.py | 25 ++++--- test/func_blocks/fu/fpu/test_fpu_rounding.py | 23 +++---- transactron/testing/gtkw_extension.py | 71 -------------------- transactron/testing/infrastructure.py | 19 +++--- 6 files changed, 33 insertions(+), 109 deletions(-) delete mode 100644 transactron/testing/gtkw_extension.py diff --git a/amaranth-stubs b/amaranth-stubs index e8efebb9d..edb302b00 160000 --- a/amaranth-stubs +++ b/amaranth-stubs @@ -1 +1 @@ -Subproject commit e8efebb9dfc8f89a93b92b290eb9f9b11899ed0d +Subproject commit edb302b001433edf4c8568190adc9bd0c0039f45 diff --git a/requirements.txt b/requirements.txt index 376b0f0b1..73a29a158 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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@994fa815995b1ac5b3c708915dcece2a45796569 +amaranth==0.5.3 dataclasses-json==0.6.3 diff --git a/test/func_blocks/fu/fpu/test_fpu_error.py b/test/func_blocks/fu/fpu/test_fpu_error.py index fb4310131..451ce5242 100644 --- a/test/func_blocks/fu/fpu/test_fpu_error.py +++ b/test/func_blocks/fu/fpu/test_fpu_error.py @@ -53,7 +53,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": help_values.not_max_norm_even_sig, "exp": help_values.not_max_norm_exp, "inexact": 0, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 0, @@ -64,7 +64,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": help_values.not_max_norm_even_sig, "exp": help_values.not_max_norm_exp, "inexact": 1, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 0, @@ -75,7 +75,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": help_values.sub_norm_sig, "exp": 0, "inexact": 1, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 0, @@ -86,7 +86,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": help_values.qnan, "exp": help_values.max_exp, "inexact": 1, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 1, "division_by_zero": 0, "input_inf": 0, @@ -97,7 +97,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": 0, "exp": help_values.max_exp, "inexact": 1, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 1, "input_inf": 0, @@ -108,7 +108,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": 0, "exp": help_values.max_exp, "inexact": 0, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 0, @@ -119,7 +119,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": help_values.sub_norm_sig, "exp": 0, "inexact": 0, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 0, @@ -130,7 +130,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": help_values.qnan, "exp": help_values.max_exp, "inexact": 1, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 0, @@ -141,7 +141,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": 0, "exp": help_values.max_exp, "inexact": 1, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 1, @@ -152,7 +152,7 @@ async def other_cases_test(sim: TestbenchContext): "sig": help_values.min_norm_sig, "exp": 0, "inexact": 1, - "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY.value, + "rounding_mode": RoundingModes.ROUND_NEAREST_AWAY, "invalid_operation": 0, "division_by_zero": 0, "input_inf": 0, @@ -262,14 +262,13 @@ def test_rounding( fpue = TestFPUError.FPUErrorModule(params) async def one_rounding_mode_test(sim: TestbenchContext): - rm_int = rm.value # TODO: workaround for amaranth bug test_cases = [ # overflow detection { "sign": 0, "sig": 0, "exp": help_values.max_exp, - "rounding_mode": rm_int, + "rounding_mode": rm, "inexact": 0, "invalid_operation": 0, "division_by_zero": 0, @@ -279,7 +278,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "sign": 1, "sig": 0, "exp": help_values.max_exp, - "rounding_mode": rm_int, + "rounding_mode": rm, "inexact": 0, "invalid_operation": 0, "division_by_zero": 0, diff --git a/test/func_blocks/fu/fpu/test_fpu_rounding.py b/test/func_blocks/fu/fpu/test_fpu_rounding.py index 86a132f3d..d33849c06 100644 --- a/test/func_blocks/fu/fpu/test_fpu_rounding.py +++ b/test/func_blocks/fu/fpu/test_fpu_rounding.py @@ -96,7 +96,6 @@ def test_rounding( fpurt = TestFPURounding.FPURoundingModule(params) async def one_rounding_mode_test(sim: TestbenchContext): - rm_int = rm.value # TODO: workaround for Amaranth bug test_cases = [ # carry after increment { @@ -105,7 +104,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 1, "sticky_bit": 1, - "rounding_mode": rm_int, + "rounding_mode": rm, }, # no overflow 00 { @@ -114,7 +113,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 0, "sticky_bit": 0, - "rounding_mode": rm_int, + "rounding_mode": rm, }, { "sign": 1, @@ -122,7 +121,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 0, "sticky_bit": 0, - "rounding_mode": rm_int, + "rounding_mode": rm, }, # no overflow 10 { @@ -131,7 +130,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 1, "sticky_bit": 0, - "rounding_mode": rm_int, + "rounding_mode": rm, }, { "sign": 1, @@ -139,7 +138,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 1, "sticky_bit": 0, - "rounding_mode": rm_int, + "rounding_mode": rm, }, # no overflow 01 { @@ -148,7 +147,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 0, "sticky_bit": 1, - "rounding_mode": rm_int, + "rounding_mode": rm, }, { "sign": 1, @@ -156,7 +155,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 0, "sticky_bit": 1, - "rounding_mode": rm_int, + "rounding_mode": rm, }, # no overflow 11 { @@ -165,7 +164,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 1, "sticky_bit": 1, - "rounding_mode": rm_int, + "rounding_mode": rm, }, { "sign": 1, @@ -173,7 +172,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 1, "sticky_bit": 1, - "rounding_mode": rm_int, + "rounding_mode": rm, }, # Round to nearest tie to even { @@ -182,7 +181,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 1, "sticky_bit": 0, - "rounding_mode": rm_int, + "rounding_mode": rm, }, { "sign": 0, @@ -190,7 +189,7 @@ async def one_rounding_mode_test(sim: TestbenchContext): "exp": help_values.not_max_norm_exp, "round_bit": 1, "sticky_bit": 0, - "rounding_mode": rm_int, + "rounding_mode": rm, }, ] expected_results = [ diff --git a/transactron/testing/gtkw_extension.py b/transactron/testing/gtkw_extension.py deleted file mode 100644 index 21eb90ea5..000000000 --- a/transactron/testing/gtkw_extension.py +++ /dev/null @@ -1,71 +0,0 @@ -from typing import Iterable, Mapping -from contextlib import contextmanager -from amaranth.lib.data import View -from amaranth.sim.pysim import _VCDWriter -from amaranth.sim import Tick -from amaranth import * -from transactron.utils import flatten_signals - - -class _VCDWriterExt(_VCDWriter): - def __init__(self, state, design, *, vcd_file, gtkw_file, traces): - super().__init__( - state=state, design=design, vcd_file=vcd_file, gtkw_file=gtkw_file, traces=list(flatten_signals(traces)) - ) - self._tree_traces = traces - - def close(self, timestamp): - def save_signal(value: Value): - for signal in value._rhs_signals(): # type: ignore - if signal in self.gtkw_signal_names: - for name in self.gtkw_signal_names[signal]: - self.gtkw_save.trace(name) - - def gtkw_traces(traces): - if isinstance(traces, Mapping): - for k, v in traces.items(): - with self.gtkw_save.group(k): - gtkw_traces(v) - elif isinstance(traces, Iterable): - for v in traces: - gtkw_traces(v) - elif isinstance(traces, Record): - if len(traces.fields) > 1: - with self.gtkw_save.group(traces.name): - for v in traces.fields.values(): - gtkw_traces(v) - elif len(traces.fields) == 1: # to make gtkwave view less verbose - gtkw_traces(next(iter(traces.fields.values()))) - elif isinstance(traces, View): - v = Value.cast(traces) - with self.gtkw_save.group(v.name if isinstance(v, Signal) else ""): - save_signal(v) - elif isinstance(traces, Value): - save_signal(traces) - - if self.vcd_writer is not None: - self.vcd_writer.close(timestamp) - - if self.gtkw_save is not None: - self.gtkw_save.dumpfile(self.vcd_file.name) - self.gtkw_save.dumpfile_size(self.vcd_file.tell()) - self.gtkw_save.zoom_markers(-21) - - self.gtkw_save.treeopen("top") - gtkw_traces(self._tree_traces) - - if self.vcd_file is not None: - self.vcd_file.close() - if self.gtkw_file is not None: - self.gtkw_file.close() - - -@contextmanager -def write_vcd_ext(engine, vcd_file, gtkw_file, traces): - vcd_writer = _VCDWriterExt(engine._state, engine._design, vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces) - try: - engine._vcd_writers.append(vcd_writer) - yield Tick() - finally: - vcd_writer.close(engine.now) - engine._vcd_writers.remove(vcd_writer) diff --git a/transactron/testing/infrastructure.py b/transactron/testing/infrastructure.py index a67e1cf67..77d8c7b34 100644 --- a/transactron/testing/infrastructure.py +++ b/transactron/testing/infrastructure.py @@ -16,7 +16,6 @@ from .profiler import profiler_process, Profile from .logging import make_logging_process, parse_logging_level, _LogFormatter from .tick_count import make_tick_count_process -from .gtkw_extension import write_vcd_ext from .method_mock import MethodMock from transactron import Method from transactron.lib import AdapterTrans @@ -156,8 +155,7 @@ def __init__( extra_signals = extra_signals() clocks = [d.clk for d in cast(Any, self)._design.fragment.domains.values()] - self.ctx = write_vcd_ext( - cast(Any, self)._engine, + self.ctx = self.write_vcd( f"{traces_dir}/{traces_file}.vcd", f"{traces_dir}/{traces_file}.gtkw", traces=[clocks, extra_signals], @@ -167,12 +165,6 @@ def __init__( self.deadline = clk_period * max_cycles - def run(self) -> bool: - with self.ctx: - self.run_until(self.deadline) - - return not self.advance() - class TestCaseWithSimulator: dependency_manager: DependencyManager @@ -304,13 +296,18 @@ def run_simulation(self, module: HasElaborate, max_cycles: float = 10e4, add_tra yield sim + async def timeout_testbench(sim: SimulatorContext): + await sim.delay(max_cycles * clk_period) + raise Exception(f"Simulation time limit exceeded ({max_cycles} clock cycles)") + + sim.add_testbench(timeout_testbench, background=True) + for f in self._transactron_sim_processes_to_add: ret = f() if ret is not None: sim.add_process(ret) - res = sim.run() - assert res, "Simulation time limit exceeded" + sim.run() async def tick(self, sim: SimulatorContext, cycle_cnt: int = 1): """ From 5ca7161321771c012e439d947ea0480ab3bf3b3d Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Thu, 14 Nov 2024 16:14:55 +0100 Subject: [PATCH 2/3] Fixes for 0.5.3 --- coreblocks/core.py | 4 ++-- test/transactron/testing/test_log.py | 2 ++ transactron/testing/logging.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/coreblocks/core.py b/coreblocks/core.py index 935a789f6..913654f1c 100644 --- a/coreblocks/core.py +++ b/coreblocks/core.py @@ -96,8 +96,8 @@ def __init__(self, *, gen_params: GenParams): def elaborate(self, platform): m = TModule() - connect(m, flipped(self.wb_instr), self.wb_master_instr.wb_master) - connect(m, flipped(self.wb_data), self.wb_master_data.wb_master) + connect(m.top_module, flipped(self.wb_instr), self.wb_master_instr.wb_master) + connect(m.top_module, flipped(self.wb_data), self.wb_master_data.wb_master) m.submodules.wb_master_instr = self.wb_master_instr m.submodules.wb_master_data = self.wb_master_data diff --git a/test/transactron/testing/test_log.py b/test/transactron/testing/test_log.py index 97efb9d3b..e0cbd4af8 100644 --- a/test/transactron/testing/test_log.py +++ b/test/transactron/testing/test_log.py @@ -95,6 +95,7 @@ def test_error_log(self, caplog): async def proc(sim: TestbenchContext): await sim.tick() sim.set(m.input, 1) + await sim.tick() # A log after the last tick is not handled with pytest.raises(AssertionError): with self.run_simulation(m) as sim: @@ -112,6 +113,7 @@ def test_assertion(self, caplog): async def proc(sim: TestbenchContext): await sim.tick() sim.set(m.input, 1) + await sim.tick() # A log after the last tick is not handled with pytest.raises(AssertionError): with self.run_simulation(m) as sim: diff --git a/transactron/testing/logging.py b/transactron/testing/logging.py index 2a40d6f1b..449a43ced 100644 --- a/transactron/testing/logging.py +++ b/transactron/testing/logging.py @@ -97,7 +97,7 @@ async def log_process(sim: ProcessContext) -> None: ticks = DependencyContext.get().get_dependency(TicksKey()) async for _, _, ticks_val, combined_trigger_val, *record_vals in ( - sim.tick("sync_neg") + sim.tick() .sample(ticks, combined_trigger) .sample(*itertools.chain(*([record.trigger] + record.fields for record in records))) ): From c3c1d597a9744b5f155376464778f15d32eff0c3 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Thu, 14 Nov 2024 17:07:47 +0100 Subject: [PATCH 3/3] More fixes --- transactron/testing/infrastructure.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/transactron/testing/infrastructure.py b/transactron/testing/infrastructure.py index 77d8c7b34..c41829c80 100644 --- a/transactron/testing/infrastructure.py +++ b/transactron/testing/infrastructure.py @@ -163,7 +163,19 @@ def __init__( else: self.ctx = nullcontext() - self.deadline = clk_period * max_cycles + self.timeouted = False + + async def timeout_testbench(sim: SimulatorContext): + await sim.delay(clk_period * max_cycles) + self.timeouted = True + + self.add_testbench(timeout_testbench, background=True) + + def run(self) -> bool: + with self.ctx: + super().run() + + return not self.timeouted class TestCaseWithSimulator: @@ -296,18 +308,13 @@ def run_simulation(self, module: HasElaborate, max_cycles: float = 10e4, add_tra yield sim - async def timeout_testbench(sim: SimulatorContext): - await sim.delay(max_cycles * clk_period) - raise Exception(f"Simulation time limit exceeded ({max_cycles} clock cycles)") - - sim.add_testbench(timeout_testbench, background=True) - for f in self._transactron_sim_processes_to_add: ret = f() if ret is not None: sim.add_process(ret) - sim.run() + res = sim.run() + assert res, "Simulation time limit exceeded" async def tick(self, sim: SimulatorContext, cycle_cnt: int = 1): """