Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
__pycache__
*.dcp
*.log
*.jou
*.jou
.xsim_validation_cache/
42 changes: 7 additions & 35 deletions RapidWrightMCP/rapidwright_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1285,44 +1285,16 @@ def compare_design_structure(golden_dcp: str, revised_dcp: str) -> Dict[str, Any
else:
issues.extend(port_issues)

# Check 4: Cell count (should increase or stay same, small decreases allowed)
checks_total += 1
golden_cell_count = golden.getCells().size()
revised_cell_count = revised.getCells().size()

cell_change_pct = (revised_cell_count - golden_cell_count) / golden_cell_count * 100

# Allow small decrease (<=3%), up to 50% increase (optimizations can add/remove cells)
if (revised_cell_count >= golden_cell_count * 0.97 and
revised_cell_count <= golden_cell_count * 1.5):
checks_passed += 1
# Note small changes as info, not error
if revised_cell_count < golden_cell_count:
issues.append(
f"INFO: Cell count decreased slightly: {golden_cell_count} -> {revised_cell_count} "
f"({abs(cell_change_pct):.2f}% decrease - likely due to optimization)"
)
elif revised_cell_count > golden_cell_count:
issues.append(
f"INFO: Cell count increased: {golden_cell_count} -> {revised_cell_count} "
f"({cell_change_pct:.2f}% increase - likely due to optimization)"
)
else:
if revised_cell_count < golden_cell_count:
issues.append(
f"Cell count decreased significantly: {golden_cell_count} -> {revised_cell_count} "
f"({abs(cell_change_pct):.2f}% decrease - this may indicate logic removal)"
)
else:
issues.append(
f"Cell count increased significantly: {golden_cell_count} -> {revised_cell_count} "
f"({cell_change_pct:.1f}% increase - this may indicate excessive optimization)"
)

# Summary - only count real issues (not INFO)
real_issues = [i for i in issues if not i.startswith("INFO:")]
all_checks_passed = (checks_passed == checks_total)


# Cell counts are reported for information only; they are intentionally
# NOT part of the pass/fail checks above (the cell-count check was
# removed because optimization legitimately changes cell counts).
golden_cell_count = golden.getCells().size()
revised_cell_count = revised.getCells().size()

result = {
"status": "success",
"comparison_result": "PASS" if all_checks_passed else "FAIL",
Expand Down
38 changes: 26 additions & 12 deletions test_validate_dcps.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def _generate(
clock_names=None,
module_name="dut",
verilog_text=None,
mode="golden_trace",
):
validator = DCPValidator(
self.workspace / "golden.dcp",
Expand All @@ -154,6 +155,7 @@ def _generate(
info,
tb_path,
clock_names=clock_names if clock_names is not None else [inputs[0]["name"]],
mode=mode,
)
return tb_path.read_text()
finally:
Expand All @@ -176,7 +178,7 @@ def test_sanitized_interface_ids_are_unique(self):
self.assertIn("reg env_foo_bar_1_pending;", tb)

def test_ready_valid_sink_emits_protocol_check(self):
tb = self._generate(
ports = dict(
inputs=[
{"name": "clk", "width": None},
{"name": "stream_tvalid", "width": None},
Expand All @@ -187,11 +189,17 @@ def test_ready_valid_sink_emits_protocol_check(self):
],
)

self.assertIn("if (golden_stream_tready) begin", tb)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: stream_tready", tb)
# The golden trace TB drives the reactive environment from golden's
# outputs; the revised replay TB compares replayed outputs and emits the
# protocol mismatch check.
golden = self._generate(mode="golden_trace", **ports)
replay = self._generate(mode="revised_replay", **ports)

self.assertIn("if (golden_stream_tready) begin", golden)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: stream_tready", replay)

def test_command_response_emits_protocol_check(self):
tb = self._generate(
ports = dict(
inputs=[
{"name": "clk", "width": None},
{"name": "mem_rsp_valid", "width": None},
Expand All @@ -203,12 +211,15 @@ def test_command_response_emits_protocol_check(self):
],
)

self.assertIn("Reactive responder for mem", tb)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: mem_cmd_valid", tb)
self.assertIn("mem_rsp_payload_data = {", tb)
golden = self._generate(mode="golden_trace", **ports)
replay = self._generate(mode="revised_replay", **ports)

self.assertIn("Reactive responder for mem", golden)
self.assertIn("mem_rsp_payload_data = {", golden)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: mem_cmd_valid", replay)

def test_hls_control_emits_protocol_checks(self):
tb = self._generate(
ports = dict(
inputs=[
{"name": "ap_clk", "width": None},
{"name": "ap_start", "width": None},
Expand All @@ -221,10 +232,13 @@ def test_hls_control_emits_protocol_checks(self):
],
)

self.assertIn("Transactional HLS control driver", tb)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: ap_done", tb)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: ap_idle", tb)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: ap_ready", tb)
golden = self._generate(mode="golden_trace", **ports)
replay = self._generate(mode="revised_replay", **ports)

self.assertIn("Transactional HLS control driver", golden)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: ap_done", replay)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: ap_idle", replay)
self.assertIn("PROTOCOL MISMATCH at cycle %0d: ap_ready", replay)

def test_no_reactive_disables_reactive_environment(self):
tb = self._generate(
Expand Down
Loading