From f5b814c923c491c9e381b8cfe7efbc487cccecc7 Mon Sep 17 00:00:00 2001 From: Lekcyjna <309016@uwr.edu.pl> Date: Sun, 31 Dec 2023 16:59:40 +0100 Subject: [PATCH 1/4] Add TrueSettle --- stubs/amaranth/sim/core.pyi | 5 ++- test/common/_test/test_infrastructure.py | 26 +++++++++++++ test/common/infrastructure.py | 49 +++++++++++++++++++++--- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/stubs/amaranth/sim/core.pyi b/stubs/amaranth/sim/core.pyi index 23fef3472..6b1f56aba 100644 --- a/stubs/amaranth/sim/core.pyi +++ b/stubs/amaranth/sim/core.pyi @@ -5,6 +5,7 @@ This type stub file was generated by pyright. from .._utils import deprecated from ..hdl.cd import * from ..hdl.ir import * +from .pysim import * __all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"] class Command: @@ -49,7 +50,9 @@ class Active(Command): class Simulator: def __init__(self, fragment, *, engine=...) -> None: - ... + self._fragment : Fragment + self._engine : PySimEngine + self._clocked : set def add_process(self, process): # -> None: ... diff --git a/test/common/_test/test_infrastructure.py b/test/common/_test/test_infrastructure.py index ecf1c84d9..1631f3578 100644 --- a/test/common/_test/test_infrastructure.py +++ b/test/common/_test/test_infrastructure.py @@ -29,3 +29,29 @@ def process(self): def test_random(self): with self.run_simulation(self.m, 50) as sim: sim.add_sync_process(self.process) + +class TestTrueSettle(TestCaseWithSimulator): + def setUp(self): + self.m = SimpleTestCircuit(EmptyCircuit()) + self.test_cycles = 10 + self.flag = False + random.seed(14) + + def true_settle_process(self): + for k in range(self.test_cycles): + yield TrueSettle() + self.assertTrue(self.flag) + self.flag = False + yield + + def flag_process(self): + for k in range(self.test_cycles): + for i in range(random.randrange(0,5)): + yield Settle() + self.flag = True + yield + + def test_flag(self): + with self.run_simulation(self.m, 50) as sim: + sim.add_sync_process(self.true_settle_process) + sim.add_sync_process(self.flag_process) diff --git a/test/common/infrastructure.py b/test/common/infrastructure.py index 058d5b9ed..5fa05e524 100644 --- a/test/common/infrastructure.py +++ b/test/common/infrastructure.py @@ -2,6 +2,7 @@ import random import unittest import functools +from enum import Enum from contextlib import contextmanager, nullcontext from typing import TypeVar, Generic, Type, TypeGuard, Any, Union, Callable, cast, TypeAlias from abc import ABC @@ -108,15 +109,26 @@ class CoreblocksCommand(ABC): class Now(CoreblocksCommand): pass +class TrueSettle(CoreblocksCommand): + pass + +class SyncProcessState(Enum): + sleeping = 0 + running = 1 + ended = 2 + true_settle = 3 class SyncProcessWrapper: def __init__(self, f): self.org_process = f self.current_cycle = 0 + self.state = None + self.blocked = None def _wrapping_function(self): response = None org_coroutine = self.org_process() + self.state = SyncProcessState.running try: while True: # call orginal test process and catch data yielded by it in `command` variable @@ -124,14 +136,26 @@ def _wrapping_function(self): # If process wait for new cycle if command is None: self.current_cycle += 1 + self.state = SyncProcessState.sleeping # forward to amaranth yield - elif isinstance(command, Now): - response = self.current_cycle + self.state = SyncProcessState.running + elif isinstance(command, CoreblocksCommand): + if isinstance(command, Now): + response = self.current_cycle + elif isinstance(command, TrueSettle): + self.state = SyncProcessState.true_settle + self.blocked=True + while self.blocked: + yield Settle() + self.state = SyncProcessState.running + else: + raise RuntimeError(f"Not known CoreblocksCommand: {command}") # Pass everything else to amaranth simulator without modifications else: response = yield command except StopIteration: + self.state = SyncProcessState.ended pass @@ -168,15 +192,30 @@ def __init__(self, module: HasElaborate, max_cycles: float = 10e4, add_transacti self.ctx = nullcontext() self.deadline = clk_period * max_cycles + self.sync_proc_list = [] def add_sync_process(self, f: Callable[[], TestGen]): f_wrapped = SyncProcessWrapper(f) + self.sync_proc_list.append(f_wrapped) super().add_sync_process(f_wrapped._wrapping_function) - def run(self) -> bool: - with self.ctx: - self.run_until(self.deadline) + def _check_true_settle_ready(self): + return all(p.state != SyncProcessState.running for p in self.sync_proc_list) + def _unblock_sync_processes(self): + for p in self.sync_proc_list: + if p.blocked: + p.blocked = False + + def run(self) -> bool: + deadline = self.deadline * 1e12 + assert self._engine.now <= deadline + last_now = self._engine.now + while self.advance() and self._engine.now < deadline: + if last_now == self._engine.now: + if self._check_true_settle_ready(): + self._unblock_sync_processes() + last_now = self._engine.now return not self.advance() From f32cc637e1c1922081a4c775534e634343a742d0 Mon Sep 17 00:00:00 2001 From: Lekcyjna <309016@uwr.edu.pl> Date: Sun, 31 Dec 2023 17:02:39 +0100 Subject: [PATCH 2/4] Add comments --- test/common/infrastructure.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/common/infrastructure.py b/test/common/infrastructure.py index 5fa05e524..beab0eaaa 100644 --- a/test/common/infrastructure.py +++ b/test/common/infrastructure.py @@ -110,13 +110,15 @@ class Now(CoreblocksCommand): pass class TrueSettle(CoreblocksCommand): + """Wait till all process are waiting for the next cycle or for the TrueSettle""" pass class SyncProcessState(Enum): - sleeping = 0 + """State of SyncProcessWrapper.""" + sleeping = 0 # Wait for the next cycle running = 1 ended = 2 - true_settle = 3 + true_settle = 3 # Wait for the TrueSettle class SyncProcessWrapper: def __init__(self, f): From e1af70c29973240c4d91fb2689d756b9ace7d42a Mon Sep 17 00:00:00 2001 From: Lekcyjna <309016@uwr.edu.pl> Date: Sun, 31 Dec 2023 17:17:27 +0100 Subject: [PATCH 3/4] Lint --- test/common/_test/test_infrastructure.py | 3 ++- test/common/infrastructure.py | 13 +++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/test/common/_test/test_infrastructure.py b/test/common/_test/test_infrastructure.py index 1631f3578..5dea7ba67 100644 --- a/test/common/_test/test_infrastructure.py +++ b/test/common/_test/test_infrastructure.py @@ -30,6 +30,7 @@ def test_random(self): with self.run_simulation(self.m, 50) as sim: sim.add_sync_process(self.process) + class TestTrueSettle(TestCaseWithSimulator): def setUp(self): self.m = SimpleTestCircuit(EmptyCircuit()) @@ -46,7 +47,7 @@ def true_settle_process(self): def flag_process(self): for k in range(self.test_cycles): - for i in range(random.randrange(0,5)): + for i in range(random.randrange(0, 5)): yield Settle() self.flag = True yield diff --git a/test/common/infrastructure.py b/test/common/infrastructure.py index beab0eaaa..4c6106c92 100644 --- a/test/common/infrastructure.py +++ b/test/common/infrastructure.py @@ -109,16 +109,21 @@ class CoreblocksCommand(ABC): class Now(CoreblocksCommand): pass + class TrueSettle(CoreblocksCommand): """Wait till all process are waiting for the next cycle or for the TrueSettle""" + pass + class SyncProcessState(Enum): """State of SyncProcessWrapper.""" - sleeping = 0 # Wait for the next cycle + + sleeping = 0 # Wait for the next cycle running = 1 ended = 2 - true_settle = 3 # Wait for the TrueSettle + true_settle = 3 # Wait for the TrueSettle + class SyncProcessWrapper: def __init__(self, f): @@ -147,7 +152,7 @@ def _wrapping_function(self): response = self.current_cycle elif isinstance(command, TrueSettle): self.state = SyncProcessState.true_settle - self.blocked=True + self.blocked = True while self.blocked: yield Settle() self.state = SyncProcessState.running @@ -211,7 +216,7 @@ def _unblock_sync_processes(self): def run(self) -> bool: deadline = self.deadline * 1e12 - assert self._engine.now <= deadline + assert self._engine.now <= deadline last_now = self._engine.now while self.advance() and self._engine.now < deadline: if last_now == self._engine.now: From 0c1a4da5272ee3d6401615c9a71e3360812fc231 Mon Sep 17 00:00:00 2001 From: Lekcyjna <309016@uwr.edu.pl> Date: Tue, 2 Jan 2024 17:16:25 +0100 Subject: [PATCH 4/4] Fixed --- stubs/amaranth/sim/core.pyi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stubs/amaranth/sim/core.pyi b/stubs/amaranth/sim/core.pyi index 6b1f56aba..58241c6bb 100644 --- a/stubs/amaranth/sim/core.pyi +++ b/stubs/amaranth/sim/core.pyi @@ -49,10 +49,11 @@ class Active(Command): class Simulator: + _fragment : Fragment + _engine : PySimEngine + _clocked : set def __init__(self, fragment, *, engine=...) -> None: - self._fragment : Fragment - self._engine : PySimEngine - self._clocked : set + ... def add_process(self, process): # -> None: ...