Managing a platform
Two things you do to an existing platform: look inside one, and publish into one. Creating and seeding one is Creating a platform.
waveflow_calib show calib/platforms/myboard
waveflow_calib publish calib/work/myboard calib/platforms/myboard --apply
Looking inside one
waveflow_calib show waveflow/calib/platforms/zynq7020_bfm_100mhz
platform 'zynq7020_bfm_100mhz' waveflow/calib/platforms/zynq7020_bfm_100mhz
part xc7z020clg484-1 clock 100.0 MHz
measured in: lut ff dsp bram uram srl
bus law : fitted (2 corpus points)
timing residuals (2):
mem_r_stream_framed_task fitted 3 corpus row(s)
mem_w_stream_framed_done_task fitted 2 corpus row(s)
module records (4 configuration(s)):
MemRStream 2 config(s) hls_estimate=29 record(s)
MemWStream 2 config(s) hls_estimate=29 record(s)
synthesis time these records represent: 12.9 min
Worth reading that last line: it is the cost the library saves you, recorded from the runs that
actually happened rather than estimated. -v lists every module configuration and its counters.
Note what is not there: this is the shipped library, so it carries only the framework’s own modules. A project’s own modules live in its own library — see Creating a platform.
The two timing residuals above are keyed by function name alone — they predate the configuration-qualified key, so they are not known to describe any particular memory width. A model that loads one reports that in its confidence rather than implying a match. See Directory layout.
Publishing into one
Calibration parameters are infra-wide: once a shared component is calibrated, every project reuses it. That makes two things matter — a stray run must never clobber shared parameters, and a re-run that produces the same fit must not churn git. Both fall out of a two-tier storage split.
Two tiers: work vs. tracked
calib/work/<name>/ untracked (gitignored). Sweeps, tests, and the DAG calib steps
write here freely — it churns.
│ publish_calib
▼
waveflow/calib/platforms/<name>/ tracked (committed), and shipped as package data so a
pip-installed user resolves it. EXACTLY ONE writer: publish.
A sweep points the BusCalib / StreamTimingModel fits at
the work dir. When you are satisfied, one command promotes the result into the tracked library —
waveflow/calib/platforms/ for the shipped reference platforms, or a user/project overlay for a
platform you calibrate locally (see Platforms). Because the tracked dir has a single
writer, a test can’t reach it; because a re-fit on the same corpus is deterministic, an unchanged
promotion writes nothing.
The command
publish_calib calib/work/<name> calib/platforms/<name> # dry-run: print the plan, write nothing
publish_calib calib/work/<name> calib/platforms/<name> --apply # write only the changed files
The target is a tracked library — usually your project’s (calib/platforms/). Promoting into the
shipped one (waveflow/calib/platforms/) is for framework modules only; see
what belongs where.
- Dry-run by default. It prints a plan —
+ created,~ updated,= unchanged— and writes nothing.--applyperforms it. - No-op when unchanged. Each artifact is byte-compared against the tracked copy; identical files are never rewritten, so a deterministic re-publish produces 0 files written and no git diff.
-
Only the stable artifacts are promoted; the churny raw firing trees stay behind:
Published Left in the work dir platform.json(identity)components/<c>/rtl/mm_bus.json+points/*.jsoncomponents/<c>/pysim/components/<c>/params.json+corpus.csv - Coverage-regression guard. publish refuses (exit 1) to replace a fit with one built from fewer
datapoints — bus point files, or a component’s
corpus.csvrows — so a thin re-sweep can’t silently clobber a richer library.--forceoverrides it.
Under the hood build_plan(work, tracked) computes the plan (pure inspection) and apply_plan(plan,
force=…) writes the changed files; the CLI is a thin wrapper.
Why publish is not a DAG step
Promotion to shared infra is a deliberate “I’m satisfied” act, not a build side effect. The DAG steps
(CalibBusStep,
CollectTimingStep / FitTimingStep)
populate the work dir; publish_calib is the manual gate to the tracked one.
What is committed, and what is not
The work tier is ignored; a tracked library is not. Getting those rules right is part of setting a
project up, so they live with the setup — see
what to .gitignore, including the anchoring subtlety that decides
whether a nested project’s work tier is actually ignored.
The reference platform, end to end
waveflow/calib/platforms/zynq7020_bfm_100mhz/ is built reproducibly by
examples/mem_copy/calibrate_platform.py:
- Seed the identity — creating the platform writes
platform.json(xc7z020clg484-1, 100 MHz). - Fit the bus law —
add_runthe measured burst laws (writenwords + 2·(num_trans−1), readnwords + (num_trans−1)) at two sizes, thenfit()→mm_bus.json. - Fit the writer residual —
collect_rtlthe measured RTL spans (183 cyc at n=128, 615 at n=512) andcollect_pysima run with the bus law active, so the residual is control-only (~22 cyc), thenfit()→components/mem_w_stream_framed_done_task/. - Publish —
publish_calib calib/work/zynq7020_bfm_100mhz waveflow/calib/platforms/zynq7020_bfm_100mhz --apply.
The pysim runs live (no toolchain); only the RTL spans are measured constants (gated for real by the
-m xsi run that produced 0.0% error). Loading the committed platform reproduces the writer RTL period
to 0.0% at both sizes — a test pins it, so the committed params can’t silently drift.
A known limitation: collinear sizes
The reference platform was swept at two sizes whose num_trans and nwords are proportional
(num_trans = nwords / 16), so the two features are collinear: the fit reproduces the measured
points exactly but its split between a per-word and a per-burst slope is underdetermined (the writer’s
residual reads as 23 at n=128, 0 at n=512). More sizes do not fix this if they are all multiples of
16 — num_trans stays nwords / 16 and the collinearity persists. What breaks it is a size where
ceil(nwords / 16) departs from nwords / 16 — a small or non-multiple-of-16 transfer (e.g.
nwords = 17 → 2 bursts, nwords = 16 → 1) that changes the burst-to-word ratio. Adding those to the
grid identifies the per-word (1) and per-burst (2 write / 1 read) costs cleanly; it is the follow-up
before trusting the model to extrapolate beyond the swept range.
See also
- Platforms — the identity the workflow seeds and confirms.
- The bus-transfer model / Component residuals — the two fits the sweep produces.
- Build system — the DAG the calibration steps plug into.