Fix/better spec enforcing (#41)

* revamp

* consolidate

* version bump
This commit is contained in:
Arnav Sacheti
2026-02-03 00:03:04 -08:00
committed by GitHub
parent 1e09da6dbf
commit bad845d15e
29 changed files with 775 additions and 363 deletions

View File

@@ -1,10 +1,5 @@
"""Pytest fixtures for unit tests."""
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Iterable, Mapping, Optional
from collections.abc import Callable
import pytest

View File

@@ -12,7 +12,7 @@ from peakrdl_busdecoder.cpuif.apb4 import APB4Cpuif
def test_external_nested_components_generate_correct_decoder(external_nested_rdl: AddrmapNode) -> None:
"""Test that external nested components generate correct decoder logic.
The decoder should:
- Generate select signals for multicast and port[16]
- NOT generate select signals for multicast.common[] or multicast.response
@@ -25,21 +25,21 @@ def test_external_nested_components_generate_correct_decoder(external_nested_rdl
tmpdir,
cpuif_cls=APB4Cpuif,
)
# Read the generated module
module_file = Path(tmpdir) / "buffer_t.sv"
content = module_file.read_text()
# Should have correct select signals
assert "cpuif_wr_sel.multicast = 1'b1;" in content
assert "cpuif_wr_sel.port[i0] = 1'b1;" in content
# Should NOT have invalid nested paths
assert "cpuif_wr_sel.multicast.common" not in content
assert "cpuif_wr_sel.multicast.response" not in content
assert "cpuif_rd_sel.multicast.common" not in content
assert "cpuif_rd_sel.multicast.response" not in content
# Verify struct is flat (no nested structs for external children)
assert "typedef struct" in content
assert "logic multicast;" in content
@@ -48,7 +48,7 @@ def test_external_nested_components_generate_correct_decoder(external_nested_rdl
def test_external_nested_components_generate_correct_interfaces(external_nested_rdl: AddrmapNode) -> None:
"""Test that external nested components generate correct interface ports.
The module should have:
- One master interface for multicast
- Array of 16 master interfaces for port[]
@@ -61,15 +61,15 @@ def test_external_nested_components_generate_correct_interfaces(external_nested_
tmpdir,
cpuif_cls=APB4Cpuif,
)
# Read the generated module
module_file = Path(tmpdir) / "buffer_t.sv"
content = module_file.read_text()
# Should have master interfaces for top-level external children
assert "m_apb_multicast" in content
assert "m_apb_port [16]" in content or "m_apb_port[16]" in content
# Should NOT have interfaces for nested external children
assert "m_apb_multicast_common" not in content
assert "m_apb_multicast_response" not in content
@@ -79,7 +79,7 @@ def test_external_nested_components_generate_correct_interfaces(external_nested_
def test_non_external_nested_components_are_descended(compile_rdl: Callable[..., AddrmapNode]) -> None:
"""Test that non-external nested components are still descended into.
This is a regression test to ensure we didn't break normal nested
component handling.
"""
@@ -98,16 +98,16 @@ def test_non_external_nested_components_are_descended(compile_rdl: Callable[...,
};
"""
top = compile_rdl(rdl_source, top="outer_block")
with TemporaryDirectory() as tmpdir:
exporter = BusDecoderExporter()
# Use depth=0 to descend all the way down to registers
exporter.export(top, tmpdir, cpuif_cls=APB4Cpuif, max_decode_depth=0)
# Read the generated module
module_file = Path(tmpdir) / "outer_block.sv"
content = module_file.read_text()
# Should descend into inner and reference inner_reg
assert "inner" in content
assert "inner_reg" in content
@@ -123,7 +123,7 @@ def test_max_decode_depth_parameter_exists(compile_rdl: Callable[..., AddrmapNod
};
"""
top = compile_rdl(rdl_source, top="simple")
with TemporaryDirectory() as tmpdir:
exporter = BusDecoderExporter()
# Should not raise an exception
@@ -133,7 +133,7 @@ def test_max_decode_depth_parameter_exists(compile_rdl: Callable[..., AddrmapNod
cpuif_cls=APB4Cpuif,
max_decode_depth=2,
)
# Verify output was generated
module_file = Path(tmpdir) / "simple.sv"
assert module_file.exists()
@@ -141,7 +141,7 @@ def test_max_decode_depth_parameter_exists(compile_rdl: Callable[..., AddrmapNod
def test_unaligned_external_component_supported(compile_rdl: Callable[..., AddrmapNode]) -> None:
"""Test that external components can be at unaligned addresses.
This test verifies that external components don't need to be aligned
to a power-of-2 multiple of their size, as the busdecoder supports
unaligned access.
@@ -161,16 +161,16 @@ def test_unaligned_external_component_supported(compile_rdl: Callable[..., Addrm
};
"""
top = compile_rdl(rdl_source, top="buffer_t")
with TemporaryDirectory() as tmpdir:
exporter = BusDecoderExporter()
# Should not raise an alignment error
exporter.export(top, tmpdir, cpuif_cls=APB4Cpuif)
# Verify output was generated
module_file = Path(tmpdir) / "buffer_t.sv"
assert module_file.exists()
content = module_file.read_text()
# Verify the external component is in the generated code
assert "multicast" in content
@@ -178,7 +178,7 @@ def test_unaligned_external_component_supported(compile_rdl: Callable[..., Addrm
def test_unaligned_external_component_array_supported(compile_rdl: Callable[..., AddrmapNode]) -> None:
"""Test that external component arrays with non-power-of-2 strides are supported.
This test verifies that external component arrays can have arbitrary strides,
not just power-of-2 strides.
"""
@@ -197,16 +197,16 @@ def test_unaligned_external_component_array_supported(compile_rdl: Callable[...,
};
"""
top = compile_rdl(rdl_source, top="buffer_t")
with TemporaryDirectory() as tmpdir:
exporter = BusDecoderExporter()
# Should not raise an alignment error
exporter.export(top, tmpdir, cpuif_cls=APB4Cpuif)
# Verify output was generated
module_file = Path(tmpdir) / "buffer_t.sv"
assert module_file.exists()
content = module_file.read_text()
# Verify the external component array is in the generated code
assert "port" in content
@@ -214,7 +214,7 @@ def test_unaligned_external_component_array_supported(compile_rdl: Callable[...,
def test_unaligned_external_nested_in_addrmap(compile_rdl: Callable[..., AddrmapNode]) -> None:
"""Test that addrmaps containing external components can be at unaligned addresses.
This verifies that not just external components themselves, but also
non-external addrmaps/regfiles that contain external components can be
at unaligned addresses.
@@ -235,18 +235,16 @@ def test_unaligned_external_nested_in_addrmap(compile_rdl: Callable[..., Addrmap
};
"""
top = compile_rdl(rdl_source, top="outer_block")
with TemporaryDirectory() as tmpdir:
exporter = BusDecoderExporter()
# Should not raise an alignment error
exporter.export(top, tmpdir, cpuif_cls=APB4Cpuif)
# Verify output was generated
module_file = Path(tmpdir) / "outer_block.sv"
assert module_file.exists()
content = module_file.read_text()
# Verify the nested components are in the generated code
assert "inner" in content

View File

@@ -36,7 +36,7 @@ def test_depth_1_generates_top_level_interface_only(compile_rdl: Callable[..., A
assert "m_apb_inner1" in content
# Should NOT have interface for reg1
assert "m_apb_reg1" not in content
# Struct should have inner1 but not nested structure
assert "logic inner1;" in content
@@ -77,7 +77,7 @@ def test_depth_2_generates_second_level_interfaces(compile_rdl: Callable[..., Ad
# Should NOT have interface for inner1 or reg2
assert "m_apb_inner1" not in content
assert "m_apb_reg2" not in content
# Struct should be hierarchical with inner1.reg1 and inner1.inner2
assert "cpuif_sel_inner1_t" in content
assert "logic reg1;" in content
@@ -125,7 +125,7 @@ def test_depth_0_decodes_all_levels(compile_rdl: Callable[..., AddrmapNode]) ->
# Should NOT have interfaces for addrmaps
assert "m_apb_inner1" not in content
assert "m_apb_inner2" not in content
# Struct should be fully hierarchical
assert "cpuif_sel_inner1_t" in content
assert "cpuif_sel_inner2_t" in content