This lesson has one job: test every bit of both banks without doubling the hardware. Two private test engines would each sit idle half the time and each need their own fuse routing; one shared controller with a port mux serves both banks in turn. The risk is a test path that disturbs the live bank, or a repair decision that never reaches the fuse array, so the sharing, the time budget and the handoff each get their own check.
A shared test controller (a NanoSoC teaching value; it is not in the NanoSoC block list, and neither is the fuse array it hands repair addresses to) connects to one bank at a time through the test-mode mux in each wrapper, running a march pattern that visits every address. Repair replaces a faulty row or column with a spare by blowing fuses that the wrapper reads at reset; the controller discovers the failing address during test, and the fuse array stores the repair permanently. For the mechanism of the controller and the repair flow see MBIST Integration and Memory Repair Flow; this lesson owns only the integration facts: which controller serves which bank, how long the whole test takes, how the repair bits travel from tester to fuse array to wrapper, and how the power budget covers test mode. BIST signoff is tracked in BIST Signoff, and the DFT-side handoff view is Memory, JTAG, Compression, and Handoff.
NanoSoC carries two 64 KB banks, each 16,384 words of 32 bits. A march pattern touches each word a fixed number of times; with a NanoSoC teaching value of 10 operations per word at one operation per system clock, one bank takes 163,840 clocks and both banks take 327,680 clocks, or about 3.4 ms at 96 MHz. The script below budgets exactly that and proves the arithmetic scales with the operation count.
words_per_bank = 64 * 1024 // 4
banks = 2
ops_per_word = 10
f_sys = 96e6
clocks = words_per_bank * banks * ops_per_word
seconds = clocks / f_sys
assert words_per_bank == 16384, words_per_bank
assert clocks == 327680, clocks
assert abs(seconds * 1000 - 3.413333) < 0.001, seconds
for ops in (6, 10, 13):
t_ms = words_per_bank * banks * ops / f_sys * 1000
print(f"ops {ops}: {t_ms:.2f} ms for both banks")
print("PASS: 327680 clocks, about 3.41 ms at 96 MHz")One controller, two muxed ports: the block diagram names which bank is under test and which stays live at each step.
Test time is budgeted in the power-on or production-test plan, with the operation count stated, not assumed.
Every repair fuse has a named path: discovery log, fuse field, wrapper pin, and a read-back check at reset.
Run the controller against both wrappers in simulation with a planted stuck-at fault in one address: the test must flag exactly that address, the repair register must capture it, and a retest with the repair applied must pass. Then confirm the live bank answers normal traffic while the other is under test. If the live bank stalls, the test-mode mux is stealing the shared path instead of only the macro port.
Because the tester is gone in the field. A register fix evaporates at power-off; a fuse fix travels with the die. The register path is still used, but only to shadow the fuse value after reset so the wrapper never waits on the fuse array during normal access.