A new link needs a tag pool and a receive buffer. The RTL team asks how big each should be. One answer is "as big as we can afford"; another is "whatever the last project used". Both are guesses that will be expensive to change after the RTL exists. A model of a few dozen lines, written in an afternoon, answers the question with numbers and shows which knob matters.
How do you size a protocol's resources before there is any RTL?
The obvious approach is to wait for the RTL and measure. But by then the sizes are built in, and changing a tag width or a buffer depth means changing interfaces and verification. The other obvious approach, a spreadsheet of averages, misses interactions: a link limited by both its tags and its credits behaves like neither limit alone. Architects work in between: bounds first, then a small cycle model, then a sweep.
Write down the bounds. Each resource caps throughput by Little's law: tags ÷ round trip, credits ÷ credit loop, the target's service rate. The smallest bound is the first estimate.
Build a small cycle model with only the mechanisms that create those bounds and their interactions: latency, outstanding limit, credits, service time. Leave out everything else.
Sweep the parameters you can choose (tags, buffer depth) across the range you could afford, under the traffic you expect.
Present the result as a picture with the decision on it: where each curve flattens, and what each extra entry buys.
A requester reads from a target over a link with 10 cycles of wire delay each way. The target serves one request per cycle from a queue protected by credits. The model sweeps the requester's outstanding limit and the target's queue size, and checks every result against the three bounds.
"""A small cycle model for sizing a link before any RTL exists.
A requester issues read requests over a link to a target and back:
- at most `outstanding` requests may be in flight (tags, lesson 6.4)
- requests take `wire` cycles each way
- the target holds at most `credits` requests (its queue, lesson 4.5) and
serves one every `service` cycles
The model sweeps outstanding requests and target queue size and reports
throughput (completions per cycle), then checks it never beats the three bounds:
the target's rate, outstanding / round trip, and credits / credit loop.
"""
from collections import deque
WIRE, SERVICE = 10, 1
def run(outstanding, credits, cycles=4000):
inflight = 0 # requests issued and not yet completed
crd = credits # requester's view of free target slots
to_target = deque() # (arrival cycle) requests on the wire
queue = deque() # requests waiting at the target
to_req = deque() # (arrival cycle) responses on the wire
crd_back = deque() # (arrival cycle) credits on the wire
next_free = 0
done = 0
for t in range(cycles):
while to_req and to_req[0] <= t:
to_req.popleft(); inflight -= 1
done += t >= cycles // 2
while crd_back and crd_back[0] <= t:
crd_back.popleft(); crd += 1
while to_target and to_target[0] <= t:
to_target.popleft(); queue.append(t)
if queue and t >= next_free: # target starts serving one
queue.popleft()
next_free = t + SERVICE
to_req.append(t + SERVICE + WIRE)
crd_back.append(t + WIRE) # slot freed: credit goes back
if inflight < outstanding and crd > 0: # issue a new request
inflight += 1; crd -= 1
to_target.append(t + WIRE)
return done / (cycles - cycles // 2)
rtt = 2 * WIRE + SERVICE
print(f"wire {WIRE} cycles each way, service {SERVICE} cycle: round trip {rtt} cycles")
print("outstanding: " + " ".join(f"{o:5d}" for o in (4, 8, 16, 24, 32)))
for credits in (4, 8, 32):
row = [run(o, credits) for o in (4, 8, 16, 24, 32)]
print(f"credits {credits:>2}: " + " ".join(f"{x:5.2f}" for x in row))
for o, x in zip((4, 8, 16, 24, 32), row):
bound = min(1 / SERVICE, o / rtt, credits / (2 * WIRE))
assert x <= bound + 0.01 and x >= bound - 0.06, (o, credits, x, bound)
print("PASS: throughput follows the tightest of the three bounds; size both tags and credits from the round trip")It prints:
wire 10 cycles each way, service 1 cycle: round trip 21 cycles
outstanding: 4 8 16 24 32
credits 4: 0.19 0.20 0.20 0.20 0.20
credits 8: 0.19 0.38 0.40 0.40 0.40
credits 32: 0.19 0.38 0.76 1.00 1.00
PASS: throughput follows the tightest of the three bounds; size both tags and credits from the round tripEach curve is capped by its credits: 4 credits over a 20-cycle credit loop allow at most 0.2 per cycle, whatever the tag count.
The 32-credit curve is capped by tags until 21 outstanding cover the 21-cycle round trip; 24 and 32 tags both reach full rate.
The decision: 24 tags and at least 20 credits give full rate. Below that, the plot shows exactly what each smaller choice would cost.
Include | Leave out (until it matters) |
|---|---|
Latency of each path, worst case as well as typical | Exact bit widths, encodings, field layouts |
Every limit on in-flight work: tags, credits, buffers | Logic details that do not change timing |
The traffic you expect: rate, burstiness, mix | Rare error paths (model them separately) |
The arbitration or scheduling policy, if resources are shared | Anything you cannot yet estimate: say so instead |
Validate the model twice: against the bounds (as the listing does), and later against the RTL, so its next use is trusted.
A team (illustrative) sized a link's tag pool from its typical round trip, 40 cycles, and modelled nothing else. Under load the round trip stretched to 90 cycles in a busy fabric, and the 48-tag pool capped the link at about half rate. A model that swept latency as well as tags would have shown the curve flattening early, and the fix (64 more tags) would have cost almost nothing before the RTL was written.
Credits are the tighter bound: 8 credits over a 20-cycle credit loop allow at most 0.4 per cycle, which 16 outstanding already reach.
Because every Little's-law bound divides by latency; a resource sized for typical latency becomes the bottleneck when latency grows under load.
Size before RTL: write the bounds, build a small cycle model, sweep, and present a picture with the decision on it.
Throughput follows the tightest of the service rate, outstanding / round trip and credits / credit loop.
Validate the model against the bounds and later the RTL, and sweep worst-case latency.