* Initial plan * Fix max_decode_depth to properly control decoder hierarchy and port generation Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Fix test that relied on old depth behavior Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Update documentation for max_decode_depth parameter Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * fix format * Add variable_depth RDL file and smoke tests for max_decode_depth parameter Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * Add variable depth tests for APB3 and AXI4-Lite CPUIFs Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com> * fix * fix * bump --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>
284 lines
8.9 KiB
Python
284 lines
8.9 KiB
Python
from collections.abc import Callable
|
|
from pathlib import Path
|
|
|
|
from systemrdl.node import AddrmapNode
|
|
|
|
from peakrdl_busdecoder import BusDecoderExporter
|
|
from peakrdl_busdecoder.cpuif.apb4 import APB4Cpuif
|
|
|
|
|
|
class TestBusDecoderExporter:
|
|
"""Test the top-level BusDecoderExporter."""
|
|
|
|
def test_simple_register_export(self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path) -> None:
|
|
"""Test exporting a simple register."""
|
|
rdl_source = """
|
|
addrmap simple_reg {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} my_reg @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="simple_reg")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
# Check that output files are created
|
|
module_file = tmp_path / "simple_reg.sv"
|
|
package_file = tmp_path / "simple_reg_pkg.sv"
|
|
|
|
assert module_file.exists()
|
|
assert package_file.exists()
|
|
|
|
# Check basic content
|
|
module_content = module_file.read_text()
|
|
assert "module simple_reg" in module_content
|
|
assert "my_reg" in module_content
|
|
|
|
package_content = package_file.read_text()
|
|
assert "package simple_reg_pkg" in package_content
|
|
|
|
def test_register_array_export(self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path) -> None:
|
|
"""Test exporting a register array."""
|
|
rdl_source = """
|
|
addrmap reg_array {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} my_regs[4] @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="reg_array")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
# Check that output files are created
|
|
module_file = tmp_path / "reg_array.sv"
|
|
assert module_file.exists()
|
|
|
|
module_content = module_file.read_text()
|
|
assert "module reg_array" in module_content
|
|
assert "my_regs" in module_content
|
|
|
|
def test_nested_addrmap_export(self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path) -> None:
|
|
"""Test exporting nested addrmaps."""
|
|
rdl_source = """
|
|
addrmap inner_block {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} inner_reg @ 0x0;
|
|
};
|
|
|
|
addrmap outer_block {
|
|
inner_block inner @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="outer_block")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
# Use depth=0 to descend all the way to registers
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif, max_decode_depth=0)
|
|
|
|
# Check that output files are created
|
|
module_file = tmp_path / "outer_block.sv"
|
|
assert module_file.exists()
|
|
|
|
module_content = module_file.read_text()
|
|
assert "module outer_block" in module_content
|
|
assert "inner" in module_content
|
|
assert "inner_reg" in module_content
|
|
|
|
def test_custom_module_name(self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path) -> None:
|
|
"""Test exporting with custom module name."""
|
|
rdl_source = """
|
|
addrmap my_addrmap {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} my_reg @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="my_addrmap")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, module_name="custom_module", cpuif_cls=APB4Cpuif)
|
|
|
|
# Check that output files use custom name
|
|
module_file = tmp_path / "custom_module.sv"
|
|
package_file = tmp_path / "custom_module_pkg.sv"
|
|
|
|
assert module_file.exists()
|
|
assert package_file.exists()
|
|
|
|
module_content = module_file.read_text()
|
|
assert "module custom_module" in module_content
|
|
|
|
def test_custom_package_name(self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path) -> None:
|
|
"""Test exporting with custom package name."""
|
|
rdl_source = """
|
|
addrmap my_addrmap {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} my_reg @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="my_addrmap")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, package_name="custom_pkg", cpuif_cls=APB4Cpuif)
|
|
|
|
# Check that output files use custom package name
|
|
package_file = tmp_path / "custom_pkg.sv"
|
|
assert package_file.exists()
|
|
|
|
package_content = package_file.read_text()
|
|
assert "package custom_pkg" in package_content
|
|
|
|
def test_multiple_registers(self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path) -> None:
|
|
"""Test exporting multiple registers."""
|
|
rdl_source = """
|
|
addrmap multi_reg {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} reg1 @ 0x0;
|
|
|
|
reg {
|
|
field {
|
|
sw=r;
|
|
hw=w;
|
|
} status[15:0];
|
|
} reg2 @ 0x4;
|
|
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} control[7:0];
|
|
} reg3 @ 0x8;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="multi_reg")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
module_file = tmp_path / "multi_reg.sv"
|
|
assert module_file.exists()
|
|
|
|
module_content = module_file.read_text()
|
|
assert "module multi_reg" in module_content
|
|
assert "reg1" in module_content
|
|
assert "reg2" in module_content
|
|
assert "reg3" in module_content
|
|
|
|
def test_master_address_widths_export(
|
|
self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path
|
|
) -> None:
|
|
"""Test exporting master address width parameters for child addrmaps."""
|
|
rdl_source = """
|
|
addrmap child1 {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} reg1 @ 0x0;
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} reg2 @ 0x4;
|
|
};
|
|
|
|
addrmap child2 {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[15:0];
|
|
} reg2 @ 0x0;
|
|
};
|
|
|
|
addrmap parent {
|
|
external child1 c1 @ 0x0000;
|
|
external child2 c2 @ 0x1000;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="parent")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
package_file = tmp_path / "parent_pkg.sv"
|
|
assert package_file.exists()
|
|
|
|
package_content = package_file.read_text()
|
|
assert "package parent_pkg" in package_content
|
|
# Check for master address width parameters
|
|
assert "localparam PARENT_C1_ADDR_WIDTH = 3" in package_content
|
|
assert "localparam PARENT_C2_ADDR_WIDTH = 2" in package_content
|
|
|
|
def test_master_address_widths_with_arrays(
|
|
self, compile_rdl: Callable[..., AddrmapNode], tmp_path: Path
|
|
) -> None:
|
|
"""Test exporting master address width parameters for arrayed child addrmaps."""
|
|
rdl_source = """
|
|
addrmap child {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} reg1 @ 0x0;
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} reg2 @ 0x4;
|
|
};
|
|
|
|
addrmap parent {
|
|
external child children[4] @ 0x0 += 0x100;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="parent")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
package_file = tmp_path / "parent_pkg.sv"
|
|
assert package_file.exists()
|
|
|
|
package_content = package_file.read_text()
|
|
assert "package parent_pkg" in package_content
|
|
# Check for master address width parameter - array should have a single parameter
|
|
assert "localparam PARENT_CHILDREN_ADDR_WIDTH = 3" in package_content
|