The lookup model
The simplest resource model: resource usage is looked up directly from prior synthesis results for exactly those parameter values.
When there is no prior synthesis result with an exact parameter match, it reports a confidence of
UNCALIBRATED — it does not interpolate.
It is a machine-learning model like any other. Its training data is pairs of (parameter values, measured resources); its fitted parameters are the table those pairs become. The only unusual thing about it is that it refuses to predict between the points it was given.
Creating and fitting one
State which resource types to store, then fit it on measured points:
from waveflow.calib.platform import VITIS_RES_TYPES
from waveflow.calib.resource_model import LookupResourceModel
rm = LookupResourceModel(res_types=VITIS_RES_TYPES)
rm.fit(samples)
samples is [(component, measured_resources), ...] — the same shape every model’s
fit takes, one pair per configuration you measured. The measurements come straight out of a
synthesis report, so the report’s own spelling (LUT, BRAM_18K) is accepted.
In practice you rarely build that list yourself: the build files the measurements for you, and the default lookup reads them back. Samples covers all three routes.
Using it
for w in (32, 64, 128):
comp = elaborate(Framer, {"dwid": w}, name="f")
rm.predict(comp), rm.confidence(comp).level
dwid=32 {'lut': 251, 'ff': 198} EXACT
dwid=64 {'lut': 402, 'ff': 331} EXACT
dwid=128 {} UNCALIBRATED
dwid=128 was never synthesized. Rather than guessing from the two neighbours, the model says so:
Framer: no measurement stored for key framer-51909685; a lookup cannot interpolate,
so this is a gap, not an estimate
Why refuse to interpolate? Resource costs are full of binding thresholds — a multiply that stops fitting one DSP, an array that stops fitting block RAM — where the answer jumps rather than slopes. Between two measured points the truth can be anywhere. A model that interpolated would be confidently wrong exactly where it matters, so this one declines.
To predict between points you need a model that knows why the numbers move: see
VitisResourceModel.
Usually you write none of this
If a module has no get_rm at all, the base installs a lookup backed by the platform’s
measurement store:
LookupResourceModel(store=ModuleStore(platform.dir), platform=platform)
and InspectSynthStep — the rung after csynth in a build DAG — files a record for every module on
every synthesis. So the ordinary workflow needs no model code:
Build a configuration, and it becomes predictable.
Sweep the configurations you care about and the lookup covers them. One you never built stays honestly uncalibrated.
Constructing one by hand, as above, is for the case where the numbers should live in source rather than in a platform directory — a committed corpus, so model tests run on a machine with no toolchain installed.
When to use a lookup
A lookup is the lowest-bias model available. It stores each configuration independently, so it can represent any mapping from parameters to resources — including one that jumps, doubles, or drops to zero between neighbouring points. It assumes nothing about the shape of the function, so no structural assumption can be wrong.
What it gives up is generalization. It predicts nothing between the points it holds, so its sample complexity is the size of the parameter space you intend to query: one synthesis per point.
So the criterion is not “does this module’s cost stay constant?” — it may vary arbitrarily. It is:
- can you enumerate the configurations you will ask about, and
- are you willing to synthesize each one — or have they already been synthesized?
If yes, a lookup is the safest model you can use: exact everywhere it answers, honest everywhere else.
If the space is too large to enumerate — a multi-parameter sweep, a design-space exploration over
thousands of points — you need a model that generalizes, and generalizing means committing to a
shape. That is bias you accept in exchange for coverage, and it is
VitisResourceModel’s trade.
The instinct is that per-module modelling means authoring a model per module. Measured on
examples/fir_block across a 24-point sweep, it does not — a sweep’s points collapse onto far fewer
distinct configurations per module, because not every knob reaches every module:
| module | distinct configurations | what it needs |
|---|---|---|
MemRStream |
1 | a lookup |
MemWStream |
1 | a lookup |
FirCmdRx |
4 | a 4-entry lookup |
FirCompute |
24 | the only one needing a fit |
Three of the four were cheap to cover exhaustively, so none of them needed a model that generalizes — and the one that did is the one whose cost the sweep actually moved.
That is a happy special case, not the criterion. A module whose cost varies wildly is still a lookup if you can afford to measure every point you will ask about; a module with two configurations you cannot synthesize is not.
Next
- Binding a model to a design — how a model gets installed.
VitisResourceModel— when the space is too large to enumerate.