* fix * fix pyrefly * remove tests * Update tests/unit/test_exporter.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/peakrdl_busdecoder/listener.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update tests/unit/test_exporter.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix iter --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
330 lines
10 KiB
Python
330 lines
10 KiB
Python
"""Integration tests for the BusDecoderExporter."""
|
|
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from peakrdl_busdecoder.cpuif.apb4 import APB4Cpuif
|
|
from peakrdl_busdecoder.exporter import BusDecoderExporter
|
|
|
|
|
|
class TestBusDecoderExporter:
|
|
"""Test the top-level BusDecoderExporter."""
|
|
|
|
def test_simple_register_export(self, compile_rdl, tmp_path):
|
|
"""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, tmp_path):
|
|
"""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, tmp_path):
|
|
"""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)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
# 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, tmp_path):
|
|
"""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, tmp_path):
|
|
"""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, tmp_path):
|
|
"""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
|
|
|
|
|
|
class TestAPB4Interface:
|
|
"""Test APB4 CPU interface generation."""
|
|
|
|
def test_apb4_port_declaration(self, compile_rdl, tmp_path):
|
|
"""Test that APB4 interface ports are generated."""
|
|
rdl_source = """
|
|
addrmap apb_test {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} my_reg @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="apb_test")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
module_file = tmp_path / "apb_test.sv"
|
|
module_content = module_file.read_text()
|
|
|
|
# Check for APB4 signals
|
|
assert "PSEL" in module_content or "psel" in module_content
|
|
assert "PENABLE" in module_content or "penable" in module_content
|
|
assert "PWRITE" in module_content or "pwrite" in module_content
|
|
assert "PADDR" in module_content or "paddr" in module_content
|
|
assert "PWDATA" in module_content or "pwdata" in module_content
|
|
assert "PRDATA" in module_content or "prdata" in module_content
|
|
assert "PREADY" in module_content or "pready" in module_content
|
|
|
|
def test_apb4_read_write_logic(self, compile_rdl, tmp_path):
|
|
"""Test that APB4 read/write logic is generated."""
|
|
rdl_source = """
|
|
addrmap apb_rw {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} my_reg @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="apb_rw")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
module_file = tmp_path / "apb_rw.sv"
|
|
module_content = module_file.read_text()
|
|
|
|
# Basic sanity checks for logic generation
|
|
assert "always" in module_content or "assign" in module_content
|
|
assert "my_reg" in module_content
|
|
|
|
def test_nested_addrmap_with_array_stride(self, compile_rdl, tmp_path):
|
|
"""Test that nested addrmaps with arrays use correct stride values."""
|
|
rdl_source = """
|
|
addrmap inner_block {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} inner_reg @ 0x0;
|
|
};
|
|
|
|
addrmap outer_block {
|
|
inner_block inner[4] @ 0x0 += 0x100;
|
|
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} outer_data[31:0];
|
|
} outer_reg @ 0x400;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="outer_block")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
module_file = tmp_path / "outer_block.sv"
|
|
module_content = module_file.read_text()
|
|
|
|
# Check that the generated code uses the correct stride (0x100 = 256)
|
|
# not the array dimension (4)
|
|
# The decode logic should contain something like: i0)*11'h100 or i0)*256
|
|
assert "i0)*11'h100" in module_content or "i0)*'h100" in module_content, \
|
|
"Array stride should be 0x100 (256), not the dimension value (4)"
|
|
|
|
# Ensure it's NOT using the incorrect dimension value
|
|
assert (
|
|
"i0)*11'h4" not in module_content
|
|
and "i0)*4" not in module_content
|
|
), "Should not use array dimension (4) as stride"
|
|
|
|
def test_multidimensional_array_strides(self, compile_rdl, tmp_path):
|
|
"""Test that multidimensional arrays calculate correct strides for each dimension."""
|
|
rdl_source = """
|
|
addrmap test_block {
|
|
reg {
|
|
field {
|
|
sw=rw;
|
|
hw=r;
|
|
} data[31:0];
|
|
} my_reg[2][3] @ 0x0;
|
|
};
|
|
"""
|
|
top = compile_rdl(rdl_source, top="test_block")
|
|
|
|
exporter = BusDecoderExporter()
|
|
output_dir = str(tmp_path)
|
|
exporter.export(top, output_dir, cpuif_cls=APB4Cpuif)
|
|
|
|
module_file = tmp_path / "test_block.sv"
|
|
module_content = module_file.read_text()
|
|
# For a [2][3] array where each register is 4 bytes:
|
|
# i0 (leftmost/slowest) should have stride = 3 * 4 = 12 (0xc)
|
|
# i1 (rightmost/fastest) should have stride = 4 (0x4)
|
|
assert ("i0)*5'hc" in module_content or "i0)*12" in module_content), \
|
|
"i0 should use stride 12 (0xc) for [2][3] array"
|
|
assert ("i1)*5'h4" in module_content or "i1)*4" in module_content), \
|
|
"i1 should use stride 4 for [2][3] array"
|