Skip to content

Commit 493dbbc

Browse files
committed
implemented toggle coverage with regular & irregular toggleDUT, tracking num of signal transitions
1 parent 1da678b commit 493dbbc

File tree

5 files changed

+145
-99
lines changed

5 files changed

+145
-99
lines changed

amaranth/sim/_base.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ def reset(self):
6767
raise NotImplementedError # :nocov:
6868

6969
def get_signal(self, signal):
70-
val = self._sim.read_signal(signal)
71-
print(f"[DEBUG] Raw value read: {val} for signal: {signal}")
72-
return int(val)
70+
raise NotImplementedError # :nocov:
7371

7472
def get_memory(self, memory):
7573
raise NotImplementedError # :nocov:

amaranth/sim/_coverage.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from ._base import Observer
2+
from amaranth.sim._vcdwriter import eval_value, eval_format
23

34
class ToggleCoverageObserver(Observer):
45
def __init__(self, state, **kwargs):
@@ -13,21 +14,28 @@ def update_signal(self, timestamp, signal):
1314
return
1415

1516
sig_id = id(signal)
16-
curr_val = int(self.state.get_signal(signal)) #FIX???
17+
try:
18+
val = eval_value(self.state, signal)
19+
except Exception:
20+
val = int(self.state.get_signal(signal))
21+
try:
22+
curr_val = int(val)
23+
except TypeError:
24+
curr_val = val
1725
print(f"[DEBUG] Signal {getattr(signal, 'name', signal)} = {curr_val}")
1826

1927
if sig_id not in self._prev_values:
2028
self._prev_values[sig_id] = curr_val
21-
self._toggles[sig_id] = {"0->1": False, "1->0": False}
29+
self._toggles[sig_id] = {"0->1": 0, "1->0": 0}
2230
self._signal_names[sig_id] = signal.name
2331
return
2432

2533
prev_val = self._prev_values[sig_id]
2634

2735
if prev_val == 0 and curr_val == 1:
28-
self._toggles[sig_id]["0->1"] = True
36+
self._toggles[sig_id]["0->1"] += 1
2937
elif prev_val == 1 and curr_val == 0:
30-
self._toggles[sig_id]["1->0"] = True
38+
self._toggles[sig_id]["1->0"] += 1
3139

3240
self._prev_values[sig_id] = curr_val
3341

pyproject.toml

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,56 @@
11
# Project metadata
22

3+
34
[tool.pdm.version]
45
source = "scm"
56
version_format = "pdm_build:format_version"
67

8+
[tool.pdm.build]
9+
# If amaranth 0.3 is checked out with git (e.g. as a part of a persistent editable install or
10+
# a git worktree cached by tools like poetry), it can have an empty `nmigen` directory left over,
11+
# which causes a hard error because setuptools cannot determine the top-level package.
12+
# Add a workaround to improve experience for people upgrading from old checkouts.
13+
includes = ["amaranth/"]
14+
15+
source-includes = [
16+
".gitignore",
17+
".coveragerc",
18+
".env.toolchain",
19+
"CONTRIBUTING.txt",
20+
]
21+
22+
# Development workflow configuration
23+
24+
[tool.pdm.dev-dependencies]
25+
# This version requirement needs to be synchronized with the one in pyproject.toml above!
26+
docs = [
27+
"sphinx~=7.1",
28+
"sphinxcontrib-platformpicker~=1.4",
29+
"sphinxcontrib-yowasp-wavedrom==1.8", # exact version to avoid changes in rendering
30+
"sphinx-rtd-theme~=2.0",
31+
"sphinx-autobuild",
32+
]
33+
examples = [
34+
"amaranth-boards @ git+https://github.com/amaranth-lang/amaranth-boards.git"
35+
]
36+
37+
[tool.pdm.scripts]
38+
_.env_file = ".env.toolchain"
39+
40+
test.composite = ["test-code", "test-docs", "coverage-xml"]
41+
test-code.env = {PYTHONWARNINGS = "error"}
42+
test-code.cmd = "python -m coverage run -m unittest discover -t . -s tests -v"
43+
test-docs.cmd = "sphinx-build -b doctest docs/ docs/_build"
44+
45+
document.cmd = "sphinx-build docs/ docs/_build/ -W --keep-going"
46+
document-live.cmd = "sphinx-autobuild docs/ docs/_build/ --watch amaranth"
47+
document-linkcheck.cmd = "sphinx-build docs/ docs/_linkcheck/ -b linkcheck"
48+
49+
coverage-text.cmd = "python -m coverage report"
50+
coverage-html.cmd = "python -m coverage html"
51+
coverage-xml.cmd = "python -m coverage xml"
52+
53+
extract-schemas.call = "amaranth.lib.meta:_extract_schemas('amaranth', base_uri='https://amaranth-lang.org/schema/amaranth')"
754
[project]
855
dynamic = ["version"]
956

@@ -48,53 +95,10 @@ amaranth-rpc = "amaranth.rpc:main"
4895
requires = ["pdm-backend~=2.3.0"]
4996
build-backend = "pdm.backend"
5097

51-
[tool.pdm.build]
52-
# If amaranth 0.3 is checked out with git (e.g. as a part of a persistent editable install or
53-
# a git worktree cached by tools like poetry), it can have an empty `nmigen` directory left over,
54-
# which causes a hard error because setuptools cannot determine the top-level package.
55-
# Add a workaround to improve experience for people upgrading from old checkouts.
56-
includes = ["amaranth/"]
5798

58-
source-includes = [
59-
".gitignore",
60-
".coveragerc",
61-
".env.toolchain",
62-
"CONTRIBUTING.txt",
63-
]
64-
65-
# Development workflow configuration
66-
67-
[tool.pdm.dev-dependencies]
68-
# This version requirement needs to be synchronized with the one in pyproject.toml above!
99+
[dependency-groups]
69100
test = [
70-
"yowasp-yosys>=0.40",
71-
"coverage",
72-
]
73-
docs = [
74-
"sphinx~=7.1",
75-
"sphinxcontrib-platformpicker~=1.4",
76-
"sphinxcontrib-yowasp-wavedrom==1.8", # exact version to avoid changes in rendering
77-
"sphinx-rtd-theme~=2.0",
78-
"sphinx-autobuild",
79-
]
80-
examples = [
81-
"amaranth-boards @ git+https://github.com/amaranth-lang/amaranth-boards.git"
101+
"yowasp-yosys>=0.40",
102+
"coverage",
103+
"pytest>=8.4.1",
82104
]
83-
84-
[tool.pdm.scripts]
85-
_.env_file = ".env.toolchain"
86-
87-
test.composite = ["test-code", "test-docs", "coverage-xml"]
88-
test-code.env = {PYTHONWARNINGS = "error"}
89-
test-code.cmd = "python -m coverage run -m unittest discover -t . -s tests -v"
90-
test-docs.cmd = "sphinx-build -b doctest docs/ docs/_build"
91-
92-
document.cmd = "sphinx-build docs/ docs/_build/ -W --keep-going"
93-
document-live.cmd = "sphinx-autobuild docs/ docs/_build/ --watch amaranth"
94-
document-linkcheck.cmd = "sphinx-build docs/ docs/_linkcheck/ -b linkcheck"
95-
96-
coverage-text.cmd = "python -m coverage report"
97-
coverage-html.cmd = "python -m coverage html"
98-
coverage-xml.cmd = "python -m coverage xml"
99-
100-
extract-schemas.call = "amaranth.lib.meta:_extract_schemas('amaranth', base_uri='https://amaranth-lang.org/schema/amaranth')"

tests/test_base.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

tests/test_coverage.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import unittest
2+
from amaranth import *
3+
from amaranth.sim import Tick, Simulator
4+
from amaranth.sim._coverage import ToggleCoverageObserver
5+
6+
class ToggleDUT(Elaboratable):
7+
def __init__(self):
8+
self.out = Signal(name="out")
9+
10+
def elaborate(self, platform):
11+
m = Module()
12+
counter = Signal(2, name="counter")
13+
m.d.sync += counter.eq(counter + 1)
14+
m.d.comb += self.out.eq(counter[1])
15+
return m
16+
17+
class IrregularToggleDUT(Elaboratable):
18+
def __init__(self):
19+
self.out = Signal(name="out")
20+
21+
def elaborate(self, platform):
22+
m = Module()
23+
counter = Signal(4, name="counter")
24+
toggle = Signal()
25+
26+
m.d.sync += counter.eq(counter + 1)
27+
with m.If((counter == 1) | (counter == 3) | (counter == 6)):
28+
m.d.sync += toggle.eq(~toggle)
29+
m.d.comb += self.out.eq(toggle)
30+
31+
return m
32+
33+
class ToggleCoverageTest(unittest.TestCase):
34+
def test_toggle_coverage_regular(self):
35+
dut = ToggleDUT()
36+
sim = Simulator(dut)
37+
38+
toggle_cov = ToggleCoverageObserver(sim._engine.state)
39+
sim._engine.add_observer(toggle_cov)
40+
41+
def process():
42+
for _ in range(16):
43+
yield Tick()
44+
45+
sim.add_clock(1e-6)
46+
sim.add_testbench(process)
47+
sim.run()
48+
49+
results = toggle_cov.get_results()
50+
print("[Regular] Toggle coverage results:")
51+
for signal_name, toggles in results.items():
52+
print(f"{signal_name}: 0→1={toggles['0->1']}, 1→0={toggles['1->0']}")
53+
54+
self.assertTrue(results["out"]["0->1"], "Expected at least one 0→1 toggle on 'out'")
55+
self.assertTrue(results["out"]["1->0"], "Expected at least one 1→0 toggle on 'out'")
56+
57+
def test_toggle_coverage_irregular(self):
58+
dut = IrregularToggleDUT()
59+
sim = Simulator(dut)
60+
61+
toggle_cov = ToggleCoverageObserver(sim._engine.state)
62+
sim._engine.add_observer(toggle_cov)
63+
64+
def process():
65+
for _ in range(16):
66+
yield Tick()
67+
68+
sim.add_clock(1e-6)
69+
sim.add_testbench(process)
70+
sim.run()
71+
72+
results = toggle_cov.get_results()
73+
print("[Irregular] Toggle coverage results:")
74+
for signal_name, toggles in results.items():
75+
print(f"{signal_name}: 0→1={toggles['0->1']}, 1→0={toggles['1->0']}")
76+
77+
self.assertTrue(results["out"]["0->1"], "Expected at least one 0→1 toggle on 'out'")
78+
self.assertTrue(results["out"]["1->0"], "Expected at least one 1→0 toggle on 'out'")
79+
self.assertGreaterEqual(results["out"]["0->1"], 1)
80+
self.assertGreaterEqual(results["out"]["1->0"], 1)
81+

0 commit comments

Comments
 (0)