Skip to content

Build whole-struct sensitivity after identity port-net collapse (§9.2.2.2, §7.2, #991) - #992

Open
ds54e wants to merge 2 commits into
aionhw:mainfrom
ds54e:pr/whole-struct-sensitivity-after-collapse
Open

ds54e wants to merge 2 commits into
aionhw:mainfrom
ds54e:pr/whole-struct-sensitivity-after-collapse

Conversation

@ds54e

@ds54e ds54e commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #991

always_comb and always @* missed changes on a whole read of an unpacked struct that is a continuous copy of another and is folded into it by the identity port-net collapse (assign q = p; always_comb c = q;). The testcase is in the issue.

Cause. A whole-struct read depends on the struct's member signals. The map from a struct name to those signals (unpacked_struct_sens) was built in Simulator::new before its identity port-net collapse, and compile can run the collapse again. The collapse points the member names of a continuous copy at its source, so the procedures waited on the copy's original member signals, which nothing writes after the collapse.

Change (code and tests in separate commits).

  • The map is built right after the last collapse call in compile, and no longer in Simulator::new. Its only reader, build_comb_entries, runs after that point.
  • The map is still built once per compile, so nothing is added to event processing apart from the missing wake-ups.
  • PREPARED_COMB_MAGIC goes from XZCMB030 to XZCMB031, as for An always_comb that writes one packed element and reads another never wakes (§9.2.2.2.1) #844, because caches made before this change hold the stale edges.

Testing

  • New tests/scheduling/unpacked_struct_copy_chain.rs:

    • copy chains of one to three steps, read by always_comb and always @*;
    • nested and real members changed one at a time;
    • copies inside a child instance and through an output port;
    • clocked and initial-block readers as controls (IEEE 1800-2023 §9.2.2.2, §9.4.2.2 and §7.2).

    With the src/ change reverted, three of the four tests fail; the clocked/initial control passes either way.

  • All nine generated test programs pass slang 12.1 with no diagnostics.

  • Full suites on this branch:

    • xezim: 6,336 passed, 0 failed, 1 ignored (the ignored test is also ignored on main);
    • xezim-core (unchanged): 345 passed.
  • Same output on a struct-nettype charge-pump regulator model. Three alternating runs on a shared machine give medians of 100.5 s on main and 101.2 s with this change, which is within noise. WSL has no hardware counters, so there is no perf stat count. The change is outside the event loop.

🤖 Generated with Claude Code

ds54e added 2 commits November 9, 2026 14:18
….2.2, §7.2, aionhw#991)

`always_comb c = q;` and `always @* c = q;` depend on the member signals of the unpacked struct `q`. The map from a struct name to those signals was built in `Simulator::new` before its identity port-net collapse, and `compile` can run the collapse again. The collapse points the member names of a continuous copy such as `assign q = p;` at its source, so the process waited on the copy's original member signals, which nothing writes after the collapse, and `c` missed every change. The map is now built right after the last collapse call in `compile`; its only reader, `build_comb_entries`, runs after that point. It is still built once per compile. The prepared combinational cache format is bumped by one, since caches made before this change hold those stale edges (IEEE 1800-2023 9.2.2.2, 9.4.2.2, 10.3.2 and 7.2).
…2, §7.2, aionhw#991)

Covers copy chains of one to three steps read by `always_comb` and `always @*`, nested and `real` members changed one at a time, copies inside a child instance and through an output port, and clocked and initial-block readers as controls (IEEE 1800-2023 9.2.2.2, 9.4.2.2 and 7.2).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

always_comb and always @* miss changes on a whole read of a continuously copied unpacked struct (§9.2.2.2, §10.3.2)

1 participant