A floating test pin put a whole product line into scan on a noisy board. A tied-off address bit aliased high memory onto low. An unconnected error output hid the very failure the bring-up team was hunting. On NanoSoC the lines that matter are smaller still: the two reserved interrupt inputs, and the habit that connected the DMA interrupt to a spare input instead of input 1. Ties and unconnected ports are the smallest integration decisions and the most commonly skipped, because each one looks harmless. This lesson makes them explicit: the tie rules, the unconnected-port waivers, and an audit built from the netlist description itself, so the lists cannot drift from what is built.
Three rules govern every top-level port. First, no input floats: every input has a driver, a reviewed slice, or a tie-list entry with a value and a reason, and the checker from the previous lesson enforces it. Second, ties default to the safe value for their meaning: interrupts and enables tie to inactive, which on NanoSoC means the reserved controller inputs 6 and 7 tie to zero. Third, unconnected outputs are waived individually, never by wildcard: each names the signal, the reason no logic needs it, and the reviewer who agreed. A tie is only a tie where a real input exists: a dropped address slice or an absent stream sideband is not a tie, because there is nothing to wire (the slice is recorded in the reviewed-slice list, the absence in the connectivity table). The audit below compares the ties and unconnected outputs found in the netlist description with the lists in the review, as a set difference in both directions, and requires both differences to be empty. It then mutates the netlist to show each kind of drift is reported.
# NanoSoC teaching values: audit built from the netlist, not from the list itself.
TIE_LIST = {"u_intc.irq6": (0, "reserved controller input, tied inactive"),
"u_intc.irq7": (0, "reserved controller input, tied inactive")}
WAIVERS = {} # unconnected output -> (reason, reviewer); none in the baseline
NETLIST = {"tied": {"u_intc.irq6": 0, "u_intc.irq7": 0}, "unconnected_out": []}
def audit(netlist, tie_list, waivers):
bad = []
found, listed = set(netlist["tied"]), set(tie_list)
bad += ["tie not listed: " + s for s in sorted(found - listed)]
bad += ["listed tie not in netlist: " + s for s in sorted(listed - found)]
for sig in sorted(found & listed):
if netlist["tied"][sig] != tie_list[sig][0]:
bad.append("tie value differs: " + sig)
unc, waived = set(netlist["unconnected_out"]), set(waivers)
bad += ["unconnected, no waiver: " + s for s in sorted(unc - waived)]
bad += ["waiver for connected port: " + s for s in sorted(waived - unc)]
bad += ["wildcard waiver: " + s for s in sorted(waivers) if "*" in s]
for sig, (val, reason) in tie_list.items():
if sig.split(".")[1].startswith("irq") and val != 0:
bad.append("spare interrupt not tied to 0: " + sig)
return bad
assert audit(NETLIST, TIE_LIST, WAIVERS) == [] # baseline clean
n = {"tied": {**NETLIST["tied"], "u_dma.test_mode": 0}, "unconnected_out": []}
assert audit(n, TIE_LIST, WAIVERS) == ["tie not listed: u_dma.test_mode"]
n = {"tied": {"u_intc.irq6": 1, "u_intc.irq7": 0}, "unconnected_out": []}
assert "tie value differs: u_intc.irq6" in audit(n, TIE_LIST, WAIVERS)
# a block revision adds an error output nobody connects
n = {"tied": NETLIST["tied"], "unconnected_out": ["u_dma.err_out"]}
assert audit(n, TIE_LIST, WAIVERS) == ["unconnected, no waiver: u_dma.err_out"]
w = {"u_dma.*": ("all outputs", "integrator")} # wildcard attempt
n2 = {"tied": NETLIST["tied"], "unconnected_out": []}
assert "wildcard waiver: u_dma.*" in audit(n2, TIE_LIST, w)
print("PASS: lists match the netlist both ways; drift, new unconnected output and wildcard are caught")The tie list and the no-connect list read aloud at the review, each entry with its reason; silence means consent, recorded.
Reserved and spare lines tied to their inactive value, verified in the audit, not by inspection of the schematic.
No wildcard waivers anywhere in the lint setup; each waiver names one signal and one reviewer.
In simulation, force each tied interrupt and confirm no handler runs, and add a monitor that fires if any listed tie changes value; a tie that toggles was never a tie, it was an undriven net with an opinion. For the physical tie-cell side see Tie Cells and Tie-Off Hygiene.
It appears as an unconnected output with no waiver, fails the lint gate, and is either connected to the fault reporting the safety topic owns or waived by name with a reviewer and a reason. It does not slip in on the old waiver list.