The generator draws the network; the integrator makes it agree with the chip. Four things go wrong in practice: the network's address-to-node table disagrees with the memory map (a hole answers with data, or a slave is unreachable), AXI IDs are widened or reordered at the network interface in a way the masters did not expect, an island powers down with packets in flight, and a QoS class set in the master is lost in the network. Each is an integration check, not router design.
Address to node. Each master-side network interface holds a table from address range to destination node. It must equal the chip map entry for entry, including the holes: an address that matches no entry never enters the network, so the interface itself must answer DECERR (on NanoSoC the crossbar's default slave does this today). A hole inside a slave's window, like the APB hole H3, still reaches the APB node, and the SLVERR comes from the APB decoder behind it. The check is mechanical: route a sample address from every region, every hole and every boundary, and compare with the map.
IDs. The network interface widens the AXI ID with a master index and keeps a table from packet transaction ID to AXI ID. AXI requires responses to the same ID in order. If one ID is sent to two destination nodes with different latencies, responses can return out of order, so the interface must either stall a request to a second destination until the first has drained, or hold a reorder buffer. NanoSoC's one-ID-per-master rule makes this trivial there and it must not be assumed elsewhere.
Crossings inside the network. Each router and link belongs to a clock and a power domain. A link between two clocks needs an asynchronous crossing with its own credit round trip; a link into a power island needs isolation on its data, valid and credit wires. Packets in flight when an island powers down are lost, so the same quiesce-then-isolate-then-switch order as for a bridge applies: no new requests, drain responses, return credits, then isolate. A router that carries traffic between two always-on nodes must itself be always on, or the always-on nodes lose their path when the island sleeps. QoS: the interface maps the AxQOS value to a packet priority, every router must honour it, and the end-to-end check is that a high-priority packet really overtakes a low-priority one under load.
# Illustrative NoC version of the NanoSoC map: address ranges to destination node IDs.
NODES = {"ROM": 2, "SRAM0": 3, "SRAM1": 4, "APB": 5}
TABLE = [ # (base, size, node)
(0x0000_0000, 0x4000, NODES["ROM"]),
(0x2000_0000, 0x1_0000, NODES["SRAM0"]),
(0x2001_0000, 0x1_0000, NODES["SRAM1"]),
(0x4000_0000, 0x1_0000, NODES["APB"]), # slots 7 to 15 (hole H3) answer inside the APB node
]
def route(addr, table):
hits = [n for (b, s, n) in table if b <= addr < b + s]
assert len(hits) <= 1, "overlapping table entries"
return hits[0] if hits else None # None: the interface answers DECERR, no packet is sent
EXPECT = { # sample addresses from the chip map and the node the network must pick
0x0000_0000: 2, 0x0000_3FFF: 2, 0x0000_4000: None, # hole H1
0x2000_FFFF: 3, 0x2001_0000: 4, 0x2002_0000: None, # hole H2
0x4000_0000: 5, 0x4000_6000: 5, 0x4000_7000: 5, # H3 reaches the APB node
0x4001_0000: None, 0x5000_0000: None, # hole H4
}
def mismatches(table):
return [hex(a) for a, want in EXPECT.items() if route(a, table) != want]
def overlaps(table):
s = sorted(table)
return any(s[i][0] + s[i][1] > s[i + 1][0] for i in range(len(s) - 1))
assert mismatches(TABLE) == [] and not overlaps(TABLE)
# a ROM entry one 16 KB too large swallows hole H1: the map check must fail
bad = [(0x0000_0000, 0x8000, 2)] + TABLE[1:]
assert mismatches(bad) == ["0x4000"]
# an extra entry overlapping the ROM entry is caught by the overlap check
assert overlaps(TABLE + [(0x0000_1000, 0x1000, 9)])
print("PASS: 10 map samples route correctly, a swallowed hole and an overlap are both caught")The address-to-node table is generated from, or checked against, the same source as the chip map, with every hole and boundary sampled.
The interface's ID rule is written down: widening, the ID-to-destination restriction or reorder buffer, and the response ordering it guarantees per ID.
Every network link that crosses a clock or power domain is listed with its crossing cell, isolation and quiesce rule; QoS classes are mapped to packet priorities and tested end to end.
Run the map check on the generated table every time the map or the network changes. Send one request per hole and confirm DECERR arrives from the interface, not a timeout from a far node. Issue two requests with the same ID to two destinations and confirm the responses come back in order. Power an island down with requests in flight and confirm nothing hangs. For a QoS check, saturate a link with low-priority traffic and measure a high-priority packet's wait against its bound.
The address matched a table entry or was routed to a node that never answers, instead of matching nothing. A matched-nothing address must be answered by the network interface itself with DECERR; check for an oversized or overlapping entry.