Coherence arrives as a one-line requirement, support a caching accelerator, and lands as a chip-wide change touching the fabric, the verification plan and every driver. Pricing it before promising it is the integrator's job, and NanoSoC is small enough that the whole delta fits in one lesson.
The delta has four parts. The fabric gains snoop connectivity to the new master and to every cache it may share with, plus the bandwidth rows and the deadlock re-review for the new traffic class. The map gains a coherency attribute per region, because the same address is treated differently depending on whether the access coherently snoops. Verification gains overlapping-address tests with hot caches, snoop-rate counters, and error-exit maintenance checks. Software gains the maintenance sequences or the port configuration, plus ownership of which buffers are shared. None of this is deep protocol work; all of it is integration surface that must be staffed.
# not-run: coherence migration checklist, NanoSoC teaching values
1. Fabric: snoop paths to the new master; snoop bandwidth rows; deadlock
re-review with three traffic classes; QoS class for snoop traffic.
2. Map: coherency attribute per region; shared regions marked and bounded.
3. Verification: overlapping-address tests, cache-state randomisation,
snoop counters against budget, error-exit maintenance coverage.
4. Software: maintenance sequences with barriers, or port setup; buffer
allocation rules that auditors can check.# Illustrative: the snoop budget row for a coherent accelerator added to NanoSoC.
LINE = 16 # bytes per cache line
QUERY_BYTES = 8 # illustrative: one snoop request plus one answer per line
LINK = 361.4 # MB/s sustained on a 16-beat link
def snoop_row(data_mbs, shared_fraction):
# only the shared part of the traffic needs snoops
return data_mbs * shared_fraction / LINE * QUERY_BYTES
assert abs(snoop_row(361.4, 1.0) - 180.7) < 0.1 # worst case: everything shared
assert abs(snoop_row(361.4, 0.25) - 45.2) < 0.1 # a placement rule keeps most traffic private
# the budget check: data plus snoop traffic must still fit the link it shares
assert 361.4 + snoop_row(361.4, 1.0) > LINK # over budget if all traffic is shared
assert 361.4 * 0.75 + snoop_row(361.4, 0.25) < LINK # fits when only a quarter of it is shared
print("PASS: snoop row 180.7 MB/s fully shared, 45.2 MB/s at a quarter, only the placement rule fits the link")The migration proposal carries all four parts with owners, not just the port wiring.
The snoop bandwidth row exists before the master is added, with the sharing fraction it assumes (above, 25 percent keeps the link within budget; 100 percent does not).
A non-coherent fallback is named: which buffers stay placed and maintained if the port slips.
Sequence the migration: first mark the shared regions and run the maintenance tests against them while everything is still non-coherent, then enable the port and compare snoop counters against the budget. If snoop traffic exceeds the row, the sharing pattern is wider than assumed and the fix is placement, not more bandwidth.
Everything non-coherent: placement plus maintenance covers the marked regions, which is exactly the fallback the review demanded. The port accelerates later without changing the contract.