feat: enhance DMA and RIB functionality with burst support and performance tests

- Updated dma_error_tb to implement burst transfer handling and performance measurement for INCR4 transfers.
- Introduced rib_burst_bus_tb to validate RIB burst arbitration and protocol compliance.
- Added soc_rib_burst_ram_tb to test SRAM behavior with burst commands.
- Created spill_register_tb to verify the functionality of the spill register under various conditions.
- Modified test_dma.py to reflect changes in DMA error handling and performance reporting.
- Updated test_memory_map.py to include new RIB burst interfaces and spill register utilities.
- Enhanced test_rib_burst.py with directed tests for RIB burst building blocks and spill register behavior.
- Adjusted test_soc_topology.py to ensure correct instantiation counts for RIB interfaces.
- Improved test_user_extensions.py to validate the generation of isolated scalar bindings for burst interfaces.
This commit is contained in:
maksyuki committed 2026-08-03 22:23:37 +08:00
1 parent 9dc32ab9cc
commit bef5881149
57 files changed
+3239 -727

No files matched your search

+5 -1
View File
@@ -18,6 +18,7 @@ REQUIRED_FIELDS = {
"op",
"words",
"checksum",
"cycles",
"mgmt_wait",
"rib_wait",
"sdram_wait",
@@ -46,8 +47,11 @@ def parse_log(content: str) -> dict[str, object]:
"operation": fields["op"],
"words": parse_number(fields["words"]),
"checksum": int(fields["checksum"], 16),
"cycles": parse_number(fields["cycles"]),
}
for counter in sorted(REQUIRED_FIELDS - {"region", "op", "words", "checksum"}):
for counter in sorted(
REQUIRED_FIELDS - {"region", "op", "words", "checksum", "cycles"}
):
sample[counter] = parse_number(fields[counter])
samples.append(sample)
passed = PASS_MARKER in content
+26
View File
@@ -113,6 +113,25 @@ def regression_environment() -> dict[str, str]:
return environment
def with_netsim_boot_only(
commands: tuple[tuple[str, tuple[str, ...]], ...],
) -> tuple[tuple[str, tuple[str, ...]], ...]:
"""Replace only the local netlist target and retain its selected firmware image."""
transformed: list[tuple[str, tuple[str, ...]]] = []
for profile, values in commands:
if "netsim" not in values:
transformed.append((profile, values))
continue
filtered = tuple(
value
for value in values
if not value.startswith("SIM_SUCCESS_MARKER=")
and value != "netsim"
)
transformed.append((profile, (*filtered, "netsim-boot")))
return tuple(transformed)
def run_command(
command: list[str], root: Path, capture_output: bool, environment: dict[str, str]
) -> str:
@@ -157,11 +176,18 @@ def main() -> int:
parser.add_argument("--suite", choices=("smoke", "pr", "nightly"), required=True)
parser.add_argument("--pdk", choices=tuple(PDK_PR_PROFILES), help="run one PDK matrix")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument(
"--netsim-boot-only",
action="store_true",
help="locally stop each Icarus assembly netlist run after Hello retroSoC!",
)
args = parser.parse_args()
try:
commands, profiles = select_regression(args.suite, args.pdk)
except ValueError as error:
parser.error(str(error))
if args.netsim_boot_only:
commands = with_netsim_boot_only(commands)
environment = regression_environment()
for profile, values in commands:
command = ["make", f"CONFIG={profile}", *values]
+37 -1
View File
@@ -18,6 +18,9 @@ sys.path.insert(0, str(ROOT))
from scripts.setup_helpers import atomic_write # noqa: E402
FAILURE_MARKER = re.compile(r"(?:\bFAILED?\b|\bFATAL\b|assertion failed|%Error)", re.IGNORECASE)
def parse_env(values: list[str]) -> dict[str, str]:
environment = os.environ.copy()
for item in values:
@@ -84,11 +87,21 @@ def main() -> int:
action="store_true",
help="stream child output byte-by-byte and preserve raw log bytes",
)
parser.add_argument("--success-marker", help="marker used by an opt-in early-stop flow")
parser.add_argument(
"--terminate-on-success-marker",
action="store_true",
help="terminate the child successfully after --success-marker is logged",
)
parser.add_argument("command", nargs=argparse.REMAINDER)
args = parser.parse_args()
command = args.command[1:] if args.command[:1] == ["--"] else args.command
if not command:
parser.error("a command is required after --")
if args.terminate_on_success_marker and not args.success_marker:
parser.error("--terminate-on-success-marker requires --success-marker")
if args.terminate_on_success_marker and args.stream_bytes:
parser.error("success-marker termination is unsupported with --stream-bytes")
try:
environment = parse_env(args.env)
except ValueError as error:
@@ -101,6 +114,8 @@ def main() -> int:
returncode = 127
error_message = None
process: subprocess.Popen[str] | subprocess.Popen[bytes] | None = None
marker_seen = False
rejected_matches: list[str] = []
try:
if args.stream_bytes:
log_context = args.log.open("wb")
@@ -128,9 +143,23 @@ def main() -> int:
for line in process.stdout:
log.write(line)
log.flush()
match = FAILURE_MARKER.search(line)
if match:
rejected_matches.append(match.group(0))
if should_display(args.tool, line):
write_console_output(line)
returncode = process.wait()
if args.terminate_on_success_marker and args.success_marker in line:
marker_seen = True
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
process.wait()
returncode = 1 if rejected_matches else 0
break
if not marker_seen:
returncode = process.wait()
except KeyboardInterrupt:
error_message = "interrupted"
if process is not None and process.poll() is None:
@@ -159,6 +188,13 @@ def main() -> int:
}
if error_message:
result["error"] = error_message
if args.success_marker:
result["success_marker"] = args.success_marker
result["success_marker_seen"] = marker_seen
if marker_seen:
result["completion_mode"] = "success_marker"
if rejected_matches:
result["rejected_matches"] = sorted(set(rejected_matches))
atomic_write(args.result, json.dumps(result, indent=2, sort_keys=True) + "\n")
return returncode