The last three drills price the three scarcest things: energy, buffering and reliability. Power decides what ships in a handheld envelope, queue depth decides whether the fabric stalls under burst, and MTBF decides whether the synchroniser is a guarantee or a hope. Each drill ends with a decision the numbers force, because interviewers ask what you would do, not what you computed.
Formula: dynamic = activity x capacitance x V^2 x f, leakage per state from the table, total per domain with duty cycle, junction = ambient + total x theta-JA. Worked: RUN 77.10 dynamic plus 5.64 leakage = 82.74 mW, IDLE 16.89, SLEEP 1.40; the CPU core is 39.20 or 47.4 percent of RUN; one 64-word DMA job at RUN power costs 62.1 nJ. Against illustrative 60 C ambient and 10 C/W, the junction rises under 1 C, so NanoSoC is power-comfortable and the fight is energy per job, not temperature.
dyn_run, leak = 77.10, 5.64
run_total = dyn_run + leak
assert abs(run_total - 82.74) < 0.01, run_total
cpu = 38.00 + 1.20
assert abs(cpu / run_total - 0.474) < 0.002, cpu / run_total
job_j = run_total * 1e-3 * 750e-9
assert abs(job_j * 1e9 - 62.1) < 0.2, job_j
t_amb, theta = 60.0, 10.0 # illustrative teaching values
t_j = t_amb + run_total * 1e-3 * theta
assert abs(t_j - 60.83) < 0.02, t_j
sleep_save = run_total - 1.40
assert abs(sleep_save - 81.34) < 0.02, sleep_save
print(f"RUN {run_total:.2f} mW, CPU share {cpu / run_total * 100:.1f}%")
print(f"job energy {job_j * 1e9:.1f} nJ; junction {t_j:.2f} C; SLEEP saves {sleep_save:.2f} mW")
print("PASS: 82.74 mW, 62.1 nJ per job, thermally comfortable")Variation: the workload runs ten jobs per millisecond, which is 10,000 jobs per second. Average job power is 10,000 x 62.1 nJ = 0.621 mW and the streaming duty is 10,000 x 750 ns = 0.75 percent; compute both before running, then say what the other 99.25 percent of the time costs (IDLE at 16.89 mW unless SLEEP is allowed).
Formula: outstanding = bandwidth x latency / bytes per beat (Little's law); async FIFO depth = burst x (1 - f_read/f_write) + synchroniser allowance + margin. Worked: 361.4 MB/s times 93.75 ns is 33.9 bytes in flight, 8.47 beats, so 9 beats must be outstanding to keep the link busy; one 16-beat burst already covers that, which is why one outstanding burst per master is enough on NanoSoC. For the FIFO, a 16-beat burst written at 192 MHz and read at 96 MHz leaves 16 x (1 - 96/192) = 8 words waiting, plus 2 for synchroniser latency and 2 margin (both illustrative) makes 12, so a power-of-two 16. NanoSoC's bridge uses 4-deep FIFOs per channel with gray-coded pointers and relies on valid/ready back-pressure to stall the writer; the rule gives the depth you would need to absorb a whole burst without stalling. The checker proves the 9 and the depth bound.
import math
bw, lat, beat = 361.4e6, 93.75e-9, 4
in_flight = bw * lat # Little's law: bytes in flight to keep the link busy
outstanding = in_flight / beat
assert abs(in_flight - 33.88) < 0.01 and abs(outstanding - 8.47) < 0.01
need = math.ceil(outstanding)
assert need == 9, need
assert 16 >= need, "one 16-beat burst already covers the round trip"
burst, f_w, f_r = 16, 192e6, 96e6
allowance, margin = 2, 2 # synchroniser latency and margin, illustrative
depth = burst * (1 - f_r / f_w) + allowance + margin
assert abs(depth - 12) < 0.01, depth
chosen = 16 # next power of two at or above the bound, NanoSoC teaching value
assert chosen >= depth and chosen & (chosen - 1) == 0
# A reader as fast as the writer needs no burst term, only the allowance.
assert burst * (1 - 192e6 / 192e6) + allowance + margin == 4
# Credits: 64-byte bursts needed to cover the round trip.
credits = math.ceil(in_flight / 64)
assert credits == 1
print(f"in flight {in_flight:.1f} B = {outstanding:.2f} beats -> {need}; FIFO bound {depth:.0f} -> choose {chosen}")
print(f"credits for 64-byte bursts: {credits}")
print("PASS: 9 beats outstanding, one burst covers it; depth bound 12, choose 16")Variation: latency doubles to 187.5 ns with a slower slave. The bytes in flight double to 67.8 and the outstanding beats to 16.94, so 17: more than one 16-beat burst, so two bursts must be in flight. Predict that before running, then say who pays: the master's tracking table, or an interconnect that throttles it.
Formula: MTBF = e^(t_res/tau) / (T_w x f_clk x f_data); t_res = period minus setup minus logic; each extra stage adds about one clock period to t_res. Constants tau and T_w are technology inputs, never facts: the drill uses illustrative teaching values and shows the ratio between one, two and three stages, which is the decision. Worked: at 96 MHz with illustrative tau 30 ps and T_w 10 ps, the second flop multiplies MTBF by e^(10.4 ns / 30 ps), an astronomically large factor, which is why two flops are the minimum and three are the audit answer for very fast clocks.
import math as m
tau, t_w = 30e-12, 10e-12 # illustrative technology inputs, not facts
setup, logic = 0.2e-9, 0.3e-9 # illustrative
YEAR = 31_556_926
def log10_years(f_clk, f_data, stages):
period = 1 / f_clk
t_res = period - setup - logic + (stages - 1) * period # each stage adds a period
return (t_res / tau - m.log(t_w * f_clk * f_data)) / m.log(10) - m.log10(YEAR)
s1, s2, s3 = (log10_years(96e6, 10e6, n) for n in (1, 2, 3))
print(f"96 MHz: 1 stage 1e{s1:.0f} years, 2 stages 1e{s2:.0f}, 3 stages 1e{s3:.0f}")
assert s1 > 100, "one stage already far beyond any product life at 96 MHz"
# The decision is the ratio: one more stage multiplies MTBF by e^(period/tau).
assert abs((s2 - s1) - (1 / 96e6) / tau / m.log(10)) < 0.01
# Variation: at 1.92 GHz the margin disappears and the stage count decides.
f1, f2, f3 = (log10_years(1.92e9, 10e6, n) for n in (1, 2, 3))
print(f"1.92 GHz: 1 stage 1e{f1:.0f} years, 2 stages 1e{f2:.1f}, 3 stages 1e{f3:.1f}")
assert f1 < -10 and f2 < 0 < f3, "only the third stage restores a usable MTBF"
print("PASS: two flops suffice at 96 MHz; at 1.92 GHz the third flop is the audit answer")Variation: raise the clock to 1.92 GHz (illustrative) with the same constants. The checker already computes it: one stage collapses to a fraction of a second, two stages give minutes, three stages give centuries. That collapse is the argument for the third flop. Change the data rate to 1 GHz (illustrative) and predict which stage count first exceeds a ten-year product life before running.
Power answers separate the envelope (junction) from the budget (battery): NanoSoC passes one and the job energy prices the other.
Queue answers quote Little's law with units before naming a depth, and the depth is a power of two above the bound.
MTBF answers treat constants as inputs and decide on ratios between stage counts, never on absolute years.
Run all three, then stress each: double the job rate, double the latency, raise the clock tenfold. Each stress must flip exactly one conclusion (average power, outstanding count, single-stage safety); if a stress flips nothing, the drill's inputs are too comfortable and you should say what real input would flip it. Forty minutes for all three with variations.
When latency matters more than the extra ratio: each stage adds a destination clock, so a three-deep chain on an interrupt path costs against the deadline. Price it: the drill's numbers show two stages already beyond any product life here, so the third buys nothing and costs 41.667 ns on the slow clock.