RTL timing and the comparison
The previous page is the pysim’s modeled timeline — 300 cyc/job. This page measures the actual RTL timing from a trace and puts the two side by side. They agree on the steady-state period to ≈1%, and the small gap is a story worth telling: it is the reader stage the interleaver forced into being calibrated.
What the RTL measures
A traced XSI run gives cycle-accurate per-firing timing — every task’s ap_done, beat by beat. The
built-in component_firings reads each stage’s firings from the
waveform; the cadence (the steady-state gap between consecutive firings) is the throughput:
- every stage fires once per job at the pipeline period — except
MemRStream, which fires twice (P then X, ≈151 cycles each), so the pipeline is reader-bound at ≈302 cyc/job: movingPandXover onem_axibus is the critical path.
The RtlTimingStep
InterleaverRtlTimingStep produces this comparison. It consumes the pysim timeline
(the previous page), builds and traces the RTL, extracts each stage’s cadence, and writes
the table — both a results/interleaver_timing_compare.json artifact and, in place, the table below:
python examples/interleaver/interleaver_figures.py --through rtl_timing
It is toolchain-gated — csynth + xsim, so an -m xsi-class rung — unlike the pysim figure, which is
committed and CI-regenerable. That asymmetry is deliberate: the pysim path is the fast, always-available
model, and this rung is the ground-truth check you run when the toolchain is present.
The comparison
The table compares each stage’s cadence (firings/job and the steady-state period), generated by the step above:
| stage | firings/job | RTL cadence | PySim cadence |
|——-|:———–:|:———–:|:————-:|
| cmd_rx (framer) | 1 | 302 | 300 |
| MemRStream (gmem0) | 2 | 151 | 150 |
| il_load → SOB | 1 | 302 | 300 |
| il_compute (gather) | 1 | 302 | 300 |
| il_store (framer) | 1 | 302 | 300 |
| MemWStream (gmem1) | 1 | 302 | 300 |
| period (cyc/job) | | 302 | 300 |
Overall period agreement: 99.3% (RTL 302 vs PySim 300 cyc/job).
Why cadence and not per-firing span: the two sides define a firing’s span differently. RTL
component_firings anchors a span at the first input handshake (excluding the wait for input), while a
pysim fire_log starts before the input get (including it) — so for a paced stage the two span
definitions diverge (il_store, say, reads ≈139 in RTL but ≈300 in the pysim occupancy). The firing
cadence — the rate a stage completes — is well-defined and comparable on both sides, and it reflects
the calibrated residuals (the reader’s 150 includes the ≈15-cycle residual the fire_log window omits).
The agreement — and what it took
RTL 302 vs pysim 300, ≈0.7% off. It holds because every stage’s cost is now a loaded model — but getting
there took calibrating the reader, and that is the interesting part. mem_copy is writer-bound, so it
only ever needed the writer’s residual; the reader’s was never fit. The interleaver is the first
reader-bound design, and it ran ≈10% under the RTL until a reader residual was fit
(the mem-stream residual — the fixture the interleaver forced into
existence). The lesson: you calibrate the stage a design actually bottlenecks on; an un-fit residual
only shows where it lands on the critical path.
The compute stage’s own per-firing span, meanwhile, matches exactly — 256 pysim == 256 RTL. It is not the bottleneck, so it does not set the period, but its cost is faithful: a variant that made the gather the critical path would already have the right model.
Next: where the compute parameters came from
The compute model’s cycles = n did not come from nowhere. Fitting the timing model
shows the measure → fit → ship recipe — reading il_compute’s per-firing span off a full-pipeline XSI run,
fitting the loop law, and storing it in the platform library. That is the custom-component half of the
calibration story, the one this example exists to teach.
See also
- Timing in the pysim — the modeled timeline this checks against (the previous page).
- The mem-stream residual — the reader/writer infra residuals the period rests on, and the fixture that fits them.
- Fitting the timing model — the interleaver’s own compute fit (the next page).
mem_copy— Visualizing timing — the writer-bound sibling that reproduces its RTL period to 0.0%.