Skip to content

Commit c43c924

Browse files
committed
Added test coverage for the automatic generation of default .gtkw file.
1 parent 5899982 commit c43c924

File tree

2 files changed

+179
-15
lines changed

2 files changed

+179
-15
lines changed

apio/scons/gtkwave_util.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
GTKW_AUTO_FILE_MARKER = "THIS FILE WAS GENERATED AUTOMATICALLY BY APIO"
1313

1414

15-
def _get_gtkw_file_header(testbench_path: Path) -> str:
15+
def _get_gtkw_file_header(testbench_path: str) -> str:
1616
"""Return a header string for the auto generated .gtkw file. 'testbench'
1717
is the relative path of the testbench file."""
1818

@@ -21,7 +21,8 @@ def _get_gtkw_file_header(testbench_path: Path) -> str:
2121

2222
lines = [
2323
f"# GTKWave display configuration for 'apio sim {tb_path_posix}'",
24-
f"# {GTKW_AUTO_FILE_MARKER}. DO NOT EDIT IT MANUALLY!",
24+
f"# {GTKW_AUTO_FILE_MARKER}.",
25+
"# DO NOT EDIT IT MANUALLY!",
2526
f"# To customize this file, run 'apio sim {tb_path_posix}'",
2627
"# and save the file from GTKWave.",
2728
"",
@@ -33,20 +34,29 @@ def _get_gtkw_file_header(testbench_path: Path) -> str:
3334
return "\n".join(lines) + "\n"
3435

3536

36-
def _signal_sort_key(s: str) -> Tuple[int, str]:
37-
"""Given a signal name, returns a key to use for signals sorting."""
38-
lcs = s.lower()
37+
def _signal_sort_key(signal: str) -> Tuple[int, str]:
38+
"""Given a top level signal name, returns a key to use for signals
39+
sorting. Signal is expected to have a two part format
40+
top-module.signal-name.
41+
"""
42+
# -- Parse signal
43+
parts = signal.split(".")
44+
assert len(parts) == 2, signal
45+
46+
# -- Prepare value to test.
47+
lc_name1 = parts[0].lower()
48+
lc_name2 = parts[1].lower()
3949

4050
# -- Priority 1: clock signals..
41-
if re.search(r"clk|clock", s):
42-
return (1, lcs)
51+
if re.search(r"clk|clock", lc_name2):
52+
return (lc_name1, 1, lc_name2)
4353

4454
# -- Priority 2: reset signals.
45-
if re.search(r"reset|rst", s):
46-
return (2, lcs)
55+
if re.search(r"reset|rst", lc_name2):
56+
return (lc_name1, 2, lc_name2)
4757

4858
# -- Priority 3: all the rest.
49-
return (3, lcs)
59+
return (lc_name1, 3, lc_name2)
5060

5161

5262
def create_gtkwave_file(
@@ -68,11 +78,7 @@ def create_gtkwave_file(
6878
vcd = VCDVCD(vcd_path, signal_res=[pattern], store_tvs=False)
6979

7080
# -- Get a list with raw names of matching signals.
71-
raw_signals = list(vcd.references_to_ids.keys())
72-
73-
# -- Strip range suffixes such as "[31:0]""
74-
range_pattern = re.compile(r"\[\d+:\d+\]$")
75-
signals = [range_pattern.sub("", sig) for sig in raw_signals]
81+
signals = list(vcd.references_to_ids.keys())
7682

7783
# -- Use basic heuristics to sort the files.
7884
signals.sort(key=_signal_sort_key)
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"""
2+
Tests of gtkwave_util.py
3+
"""
4+
5+
from pathlib import Path
6+
from tests.conftest import ApioRunner
7+
from apio.commands.apio import apio_top_cli as apio
8+
from apio.scons.gtkwave_util import create_gtkwave_file, _signal_sort_key
9+
10+
# Expected default gtkw file lines for the examples we use below.
11+
EXPECTED_GTKW_LINES = [
12+
"# GTKWave display configuration for 'apio sim main_tb.v'",
13+
"# THIS FILE WAS GENERATED AUTOMATICALLY BY APIO.",
14+
"# DO NOT EDIT IT MANUALLY!",
15+
"# To customize this file, run 'apio sim main_tb.v'",
16+
"# and save the file from GTKWave.",
17+
"",
18+
"[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI",
19+
"",
20+
"[*]",
21+
"testbench.CLK",
22+
"testbench.expected_led",
23+
"testbench.i[31:0]",
24+
"testbench.LED1",
25+
"testbench.LED2",
26+
"",
27+
]
28+
29+
30+
def test_signal_sort_key():
31+
"""Test the default signals sorting heuristic."""
32+
33+
# -- Priority 1: clock.
34+
assert _signal_sort_key("tb.clk") == ("tb", 1, "clk")
35+
assert _signal_sort_key("tb.Clk") == ("tb", 1, "clk")
36+
assert _signal_sort_key("tb.sys_clk") == ("tb", 1, "sys_clk")
37+
assert _signal_sort_key("tb.clock1") == ("tb", 1, "clock1")
38+
assert _signal_sort_key("tb.CLOCK1") == ("tb", 1, "clock1")
39+
40+
# -- Priority 2: reset.
41+
assert _signal_sort_key("tb.rst") == ("tb", 2, "rst")
42+
assert _signal_sort_key("tb.RST") == ("tb", 2, "rst")
43+
assert _signal_sort_key("tb.Reset") == ("tb", 2, "reset")
44+
assert _signal_sort_key("tb.reset") == ("tb", 2, "reset")
45+
assert _signal_sort_key("tb.reset_n") == ("tb", 2, "reset_n")
46+
assert _signal_sort_key("tb.sys_rst") == ("tb", 2, "sys_rst")
47+
assert _signal_sort_key("tb.sys_reset") == ("tb", 2, "sys_reset")
48+
assert _signal_sort_key("tb.RESET_N") == ("tb", 2, "reset_n")
49+
50+
# -- Priority 3: all other signals.
51+
assert _signal_sort_key("tb.other") == ("tb", 3, "other")
52+
assert _signal_sort_key("tb.Other") == ("tb", 3, "other")
53+
assert _signal_sort_key("tb.OTHER") == ("tb", 3, "other")
54+
55+
56+
def test_create_gtkwave_file(apio_runner: ApioRunner):
57+
"""Test the create_gtkwave_file() function"""
58+
59+
with apio_runner.in_sandbox() as sb:
60+
61+
# -- Create relative paths to the .gtkw and .vcd files
62+
gtkw_path = Path("main_tb.gtkw")
63+
vcd_path = Path("_build/blink-slow/main_tb.vcd")
64+
65+
# -- Execute "apio examples fetch alhambra-ii/getting-started"
66+
result = sb.invoke_apio_cmd(
67+
apio, ["examples", "fetch", "alhambra-ii/getting-started"]
68+
)
69+
sb.assert_result_ok(result)
70+
71+
# -- Execute "apio test main_tb.v" to create the .vcd file
72+
result = sb.invoke_apio_cmd(apio, ["test", "main_tb.v"])
73+
sb.assert_result_ok(result)
74+
75+
# -- Verify that the .vcd file exists.
76+
assert vcd_path.is_file()
77+
78+
# -- Delete the file main_tb.gtkw
79+
assert gtkw_path.is_file()
80+
gtkw_path.unlink()
81+
assert not gtkw_path.exists()
82+
83+
# -- Create the default .gtkw file
84+
create_gtkwave_file("main_tb.v", str(vcd_path), str(gtkw_path))
85+
assert gtkw_path.is_file()
86+
87+
# -- Test the generated .gtkw file.
88+
text = sb.read_file(gtkw_path)
89+
lines = text.split("\n")
90+
print(f"Actual {gtkw_path} lines:")
91+
for line in lines:
92+
print(f' "{line}",')
93+
94+
assert lines == EXPECTED_GTKW_LINES
95+
96+
97+
def test_default_signals_creation(apio_runner: ApioRunner):
98+
"""Test the automatic .gtkw file creation by 'apio sim'."""
99+
100+
with apio_runner.in_sandbox() as sb:
101+
102+
# -- Execute "apio examples fetch alhambra-ii/getting-started"
103+
result = sb.invoke_apio_cmd(
104+
apio, ["examples", "fetch", "alhambra-ii/getting-started"]
105+
)
106+
sb.assert_result_ok(result)
107+
108+
# -- Delete the file main_tb.gtkw
109+
gtkw_path = Path("main_tb.gtkw")
110+
assert gtkw_path.exists()
111+
gtkw_path.unlink()
112+
assert not gtkw_path.exists()
113+
114+
# -- Execute "apio sim --no-gtkwave main_tb.v"
115+
result = sb.invoke_apio_cmd(apio, ["sim", "--no-gtkwave", "main_tb.v"])
116+
sb.assert_result_ok(result)
117+
assert gtkw_path.exists()
118+
119+
# -- Test the generated .gtkw file.
120+
text = sb.read_file(gtkw_path)
121+
lines = text.split("\n")
122+
print(f"Actual {gtkw_path} lines:")
123+
for line in lines:
124+
print(f' "{line}",')
125+
126+
assert lines == EXPECTED_GTKW_LINES
127+
128+
129+
def test_user_gtkw_file_protection(apio_runner: ApioRunner):
130+
"""Test that a user's .gtkw file is not overwritten by apio'."""
131+
132+
with apio_runner.in_sandbox() as sb:
133+
134+
# -- Execute "apio examples fetch alhambra-ii/getting-started"
135+
result = sb.invoke_apio_cmd(
136+
apio, ["examples", "fetch", "alhambra-ii/getting-started"]
137+
)
138+
sb.assert_result_ok(result)
139+
140+
# -- Read the user's .gtkw file
141+
gtkw_path = Path("main_tb.gtkw")
142+
assert gtkw_path.exists()
143+
text_before = sb.read_file(gtkw_path)
144+
145+
# gtkw_path.unlink()
146+
# assert not gtkw_path.exists()
147+
148+
# -- Execute "apio sim --no-gtkwave main_tb.v"
149+
result = sb.invoke_apio_cmd(apio, ["sim", "--no-gtkwave", "main_tb.v"])
150+
sb.assert_result_ok(result)
151+
assert gtkw_path.exists()
152+
153+
# -- Read the .gtkw file after the operation.
154+
assert gtkw_path.exists()
155+
text_after = sb.read_file(gtkw_path)
156+
157+
# -- Verify that apio didn't change it.
158+
assert text_after == text_before

0 commit comments

Comments
 (0)