From ad364ab8d67c9971a3de22a171fbc8c69f3b3f8d Mon Sep 17 00:00:00 2001 From: Arnav Sacheti <36746504+arnavsacheti@users.noreply.github.com> Date: Wed, 24 Dec 2025 20:12:05 +0000 Subject: [PATCH] add utility tests --- tests/test_sv_int.py | 49 ++++++++++++++++++++++ tests/utils/test_get_indexed_path.py | 9 ++-- tests/utils/test_ref_is_internal.py | 62 ++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 tests/test_sv_int.py create mode 100644 tests/utils/test_ref_is_internal.py diff --git a/tests/test_sv_int.py b/tests/test_sv_int.py new file mode 100644 index 0000000..f7b7905 --- /dev/null +++ b/tests/test_sv_int.py @@ -0,0 +1,49 @@ +from peakrdl_busdecoder.sv_int import SVInt + + +def test_string_formatting() -> None: + """SV literals should format width and value correctly.""" + assert str(SVInt(0x1A, 8)) == "8'h1a" + assert str(SVInt(0xDEADBEEF)) == "'hdeadbeef" + assert str(SVInt(0x1FFFFFFFF)) == "33'h1ffffffff" + + +def test_arithmetic_width_propagation() -> None: + """Addition and subtraction should preserve sizing rules.""" + small = SVInt(3, 4) + large = SVInt(5, 6) + + summed = small + large + assert summed.value == 8 + assert summed.width == 6 # max width wins when both are sized + + diff = large - small + assert diff.value == 2 + assert diff.width == 6 + + unsized_left = SVInt(1) + mixed = unsized_left + small + assert mixed.width is None # any unsized operand yields unsized result + + +def test_length_and_to_bytes() -> None: + """Length and byte conversion should reflect the represented value.""" + sized = SVInt(0x3, 12) + assert len(sized) == 12 + + value = SVInt(0x1234) + assert len(value) == 13 + assert value.to_bytes("little") == b"\x34\x12" + assert value.to_bytes("big") == b"\x12\x34" + + +def test_equality_and_hash() -> None: + """Equality compares both value and width.""" + a = SVInt(7, 4) + b = SVInt(7, 4) + c = SVInt(7) + + assert a == b + assert hash(a) == hash(b) + assert a != c + assert (a == 7) is False # Non-SVInt comparisons fall back to NotImplemented diff --git a/tests/utils/test_get_indexed_path.py b/tests/utils/test_get_indexed_path.py index fc4b3ad..9d1447d 100644 --- a/tests/utils/test_get_indexed_path.py +++ b/tests/utils/test_get_indexed_path.py @@ -159,22 +159,21 @@ class TestGetIndexedPath: addrmap my_addrmap { reg { field {} data; - } always_reg; + } always; }; """ top = compile_rdl(rdl_source, top="my_addrmap") reg_node = None for child in top.children(): - if child.inst_name == "always_reg": + if child.inst_name == "always": reg_node = child break assert reg_node is not None # With keyword filter (default) - SystemRDL identifiers can use keywords but SV can't path = get_indexed_path(top, reg_node) - # The path should contain always_reg - assert "always_reg" in path + assert path == "always_" # Without keyword filter path = get_indexed_path(top, reg_node, skip_kw_filter=True) - assert path == "always_reg" + assert path == "always" diff --git a/tests/utils/test_ref_is_internal.py b/tests/utils/test_ref_is_internal.py new file mode 100644 index 0000000..a838535 --- /dev/null +++ b/tests/utils/test_ref_is_internal.py @@ -0,0 +1,62 @@ +from collections.abc import Callable + +from systemrdl.node import AddrmapNode +from systemrdl.rdltypes.references import PropertyReference + +from peakrdl_busdecoder.utils import ref_is_internal + + +def _find_child_by_name(node: AddrmapNode, inst_name: str): + for child in node.children(): + if child.inst_name == inst_name: + return child + raise AssertionError(f"Child with name {inst_name} not found") + + +class TestRefIsInternal: + """Tests for ref_is_internal utility.""" + + def test_external_components_flagged( + self, compile_rdl: Callable[..., AddrmapNode] + ) -> None: + """External components should be treated as non-internal.""" + rdl_source = """ + reg reg_t { + field { sw=rw; hw=r; } data[7:0]; + }; + + addrmap top { + external reg_t ext @ 0x0; + reg_t intrnl @ 0x10; + }; + """ + top = compile_rdl(rdl_source, top="top") + + internal_reg = _find_child_by_name(top, "intrnl") + assert ref_is_internal(top, internal_reg) is True + + external_reg = _find_child_by_name(top, "ext") + assert external_reg.external is True + assert ref_is_internal(top, external_reg) is False + + external_prop_ref = PropertyReference.__new__(PropertyReference) + external_prop_ref.node = external_reg + assert ref_is_internal(top, external_prop_ref) is False + + def test_property_reference_without_node_defaults_internal( + self, compile_rdl: Callable[..., AddrmapNode] + ) -> None: + """Root-level property references should be treated as internal.""" + rdl_source = """ + addrmap top { + reg { + field { sw=rw; hw=r; } data[7:0]; + } reg0 @ 0x0; + }; + """ + top = compile_rdl(rdl_source, top="top") + + prop_ref = PropertyReference.__new__(PropertyReference) + prop_ref.node = None + + assert ref_is_internal(top, prop_ref) is True