Tutorial: read a tile’s contract end to end

In this tutorial you will take one operator tile and follow it through every layer that defines it: its registry entry, its golden model, its measured cost, and the HDL those describe. By the end you will know where each kind of fact about a tile lives, which you need before adding or changing one.

We will use streaming-top-k, the tile that keeps the K largest rows by sort key.

You need dau-core installed. Nothing here needs hardware or a simulator.

Step 1: look the tile up in the registry

Open a Python prompt and ask the registry for it:

from dau_core.registry import core

tile = core("streaming-top-k")
print(tile.name)
print(tile.kind.value)
print(tile.module)

You should see:

streaming-top-k
operator
dau_streaming_top_k

The registry is a Hydra config tree loaded into the ccflow ModelRegistry, and core() reads whatever registry your application has composed.

Step 2: find its meaning

The tile’s golden field names the function that defines what it computes:

print(tile.golden)
dau_core.operators:streaming_top_k

Import it and run it. Give it four rows and ask for the top two:

from dau_core.operators import streaming_top_k

rows = [(5, 1, 0, 0), (9, 2, 0, 0), (1, 3, 0, 0), (9, 4, 0, 0)]
print(streaming_top_k(rows, k=2))
[(9, 2, 0, 0), (9, 4, 0, 0)]

Both rows with key 9 were kept, and they came out in arrival order: the row that arrived first is first. That ordering is part of the contract. The golden’s docstring states it, and the HDL has to reproduce it exactly.

Step 3: see what it costs

Ask for the resource measurement at a specific coordinate:

measurement = tile.resource_measurement(
    part="xc7a200tfbg484-2", clock_ns=8.0, params={"K": 8}
)
envelope = measurement.usable_envelope()
print(envelope.lut, envelope.ff, envelope.bram36, envelope.dsp)
print(measurement.timing.value, measurement.wns_ns)
1295 1196 0.0 0
met 4.35

Now ask for a coordinate that has not been measured:

from dau_core.cores import UnmeasuredResourceError

try:
    tile.resource_measurement(part="xc7a200tfbg484-2", clock_ns=8.0, params={"K": 3})
except UnmeasuredResourceError as error:
    print("refused:", error)

You should see an error naming the missing coordinate. The registry does not interpolate between K=1 and K=5 to invent a number for K=3, because an unmeasured number that looks plausible is how a design gets priced against the wrong hardware.

Step 4: find the HDL those facts describe

The registry knows which sources the tile needs, in compile order:

from dau_core.registry import sources_for

for path in sources_for("streaming-top-k"):
    print(path.name)
dau_streaming_top_k.sv

This tile has no dependencies. A tile that used a shared package would list that package first; sources_for resolves the dependencies so a build does not have to.

Open that file and read its header comment. It describes the same contract you saw in step 2, in the language of the hardware: a size-K systolic sorted register array where physical position encodes arrival order, which is why equal keys keep the earliest arrival without needing a counter.

Step 5: find the proof that they agree

print(tile.bench)
dau_core.tests.cocotb_benches.dau_streaming_top_k_tb

That bench drives the HDL and compares its output against streaming_top_k, the same function you ran in step 2, over generated inputs. It does not use hand-written expected values, so the test author’s assumptions about the tile cannot take the place of the contract.

Run it if you have verilator installed:

python -m pytest dau_core/tests/test_hdl_cocotb.py -q -k streaming_top_k

What you now know

Five facts about a tile live in five places:

Fact

Lives in

That it exists, and its shape

the registry yaml

What it computes

the golden function

What it costs

measurement points on the registry entry

How it computes it

the SystemVerilog

That the last two agree

the cocotb bench

Adding a tile means producing all five, in that order. That is the subject of how to add an operator tile.