Stitching assigns every scan flop to exactly one chain, illustrated here as round-robin. Real flows assign flops by clock domain, locality, pin limits and shift timing.
Choice | Effect |
|---|---|
One long chain | One pin pair, but every pattern costs the full flop count. |
Several chains | More pin pairs, and the longest chain sets the cost. |
Round-robin (this topic's illustration) | Neighbour flops land on different chains in the model below, spreading congestion. |
Balanced chains differ by at most one flop. With N flops and C chains, each chain holds floor(N/C) flops and the first N%C chains hold one more.
The calc block works two cases. Ten flops over three chains give [4, 3, 3], and 1002 flops over four chains are compared against an unbalanced layout.
N = 10 flops, C = 3 chains
base = 10 // 3 = 3, extra = 10 % 3 = 1
lengths = [4, 3, 3], longest L = 4
check: 4 + 3 + 3 = 10
N = 1002 flops, C = 4 chains
base = 1002 // 4 = 250, extra = 1002 % 4 = 2
lengths = [251, 251, 250, 250], L = 251
unbalanced example [400, 300, 200, 102]: sum = 400 + 300 + 200 + 102 = 1002, L = 400
saving per load = 400 - 251 = 149 shift cyclesOne pattern costs L shift cycles to load, one capture cycle, and L cycles to unload. Without load-unload overlap the total is 2L + 1 cycles per pattern.
Ten flops as one chain cost 21 cycles per pattern, while balanced chains of [4, 3, 3] cost 9. The ratio is 21 / 9 = 7 / 3, about 2.33 times fewer cycles.
The program below stitches ten flops into three chains, checks the exact lengths and cycle counts, and shifts a pattern through every chain to prove the data comes back unchanged.
import sys
def balanced_lengths(n_flops, n_chains):
base = n_flops // n_chains
extra = n_flops % n_chains
return [base + 1 if c < extra else base for c in range(n_chains)]
def stitch(n_flops, n_chains):
chains = [[] for _ in range(n_chains)]
for i in range(n_flops):
chains[i % n_chains].append(i)
return chains
def shift_loopback(length, pattern):
reg = [0] * length
for b in pattern:
reg = [b] + reg[:-1]
out = []
for _ in range(length):
out.append(reg[-1])
reg = [0] + reg[:-1]
return out
def check(cond, msg):
if not cond:
print("FAIL: " + msg)
sys.exit(1)
lengths = balanced_lengths(10, 3)
check(lengths == [4, 3, 3], "10 flops over 3 chains gives lengths 4 3 3")
check(max(lengths) == 4, "longest of 4 3 3 sets 4 shift cycles")
check(sum(lengths) == 10, "chain lengths sum to the flop count")
chains = stitch(10, 3)
check([len(c) for c in chains] == [4, 3, 3], "stitched chains measure 4 3 3")
check(max(balanced_lengths(12, 4)) == 3, "12 flops over 4 chains need 3 cycles")
check(max(balanced_lengths(7, 1)) == 7, "one chain of 7 needs 7 cycles")
check(balanced_lengths(1002, 4) == [251, 251, 250, 250], "1002 flops over 4 chains gives 251 251 250 250")
for c in chains:
pattern = [i % 2 for i in c]
check(shift_loopback(len(c), pattern) == pattern, "shift out reproduces shift in on every chain")
print("PASS: 10 flops stitch to 4 3 3 with 4 shift cycles and loopback proved on all 3 chains")Lengths [4, 3, 3]. Loading costs 4 cycles, set by the longest chain.
It proves each chain is a working shift register: what capture stores, scan-out can observe.