CHI is a packet protocol between nodes, not a set of point-to-point buses. Each node has a type; the course actors map onto three of them. Names and rules here are from the AMBA 5 CHI Architecture Specification, Arm IHI 0050, Issue E.a, section 1.6 (component naming).
Node | Role in the specification | Course actor |
|---|---|---|
RN-F | Fully coherent request node: has a hardware-coherent cache, may issue every transaction, receives every snoop | C0, C1 |
RN-D | IO-coherent request node with no coherent cache, receives DVM transactions | None (an IO device) |
RN-I | IO-coherent request node with no coherent cache, no DVM, no snoops | None (for example a DMA engine) |
HN-F | Fully coherent home node: the point of coherence, snoops RN-Fs, consolidates responses, expected to be the point of serialization, may hold a snoop filter or directory | H |
HN-I | Non-coherent home for IO, no point of coherence | None |
SN-F | Memory-side node for Normal memory, answers requests from the home | Memory (shown as a Note at H) |
MN | Miscellaneous node that handles DVM operations | None |
Channel | Carries | Course message class |
|---|---|---|
REQ | Requests: ReadShared, ReadUnique, CleanUnique, WriteBackFull, and requests from home to SN-F | Requests (GetS, GetM, Put) |
SNP | Snoops from home to RN-F: SnpShared, SnpSharedFwd, SnpCleanInvalid and others | Forwards (Fwd-GetS, Fwd-GetM, Inv) |
RSP | Responses without data: snoop responses, CompAck, Comp, CompDBIDResp, RetryAck, PCrdGrant | Responses (Inv-Ack, Put-Ack, completion) |
DAT | Responses with data: CompData, SnpRespData, CopyBackWrData, NCBWrData | Responses (Data) |
The split is the deadlock rule from Message Classes and Virtual Networks: responses must always sink. Each channel has its own link-layer credits (L-Credits, one per flit buffer per hop), so a full REQ queue cannot block a snoop response or CompAck behind it. CHI states the endpoint half of the rule too: section 4.11.1 requires an RN-F to answer snoops promptly with no protocol-layer dependency on the completion of its own outstanding requests. Only REQ has a rejection mechanism, which is what keeps the request class from clogging.
Every request carries SrcID and TgtID for routing and a TxnID that must be unique among that requester's outstanding requests. In Issue E.a TxnID is 12 bits and a requester may have at most 1024 transactions outstanding (section 2.5). The completer can hand back its own identifier, DBID; the requester then uses it as the TxnID of its CompAck or write data, so the home can find its tracker entry without searching. Forwarding snoops also carry FwdNID and FwdTxnID, so the snooped RN-F can address CompData to the original requester with the requester's own TxnID.
Section 2.11 defines request retry. A first attempt is sent with AllowRetry set. A completer that cannot accept it returns RetryAck with a PCrdType and records the requester. When resources free up it sends PCrdGrant, a protocol credit of that type. The requester resends with AllowRetry deasserted and the same PCrdType, and that attempt is guaranteed to be accepted. A PCrdGrant may arrive before the RetryAck it belongs to, so the requester must remember a credit it cannot yet use. This is the reserved-retry remedy for starvation from Retries, NACKs and Credits: the credit, not luck, guarantees the second attempt.
Worked case on the course model: X started at 0 in memory and in no cache. H's trackers are all busy with other lines when C1 reads X.
Step | C0 state / value | C1 state / value | H directory | Memory | Newest data |
|---|---|---|---|---|---|
(assumed) H trackers busy with other lines | I / - | I / - | U | 0 | memory: 0 |
1. ReadShared at H, refused | I / - | IS^D / -, request kept for retry | U | 0 | memory: 0 |
2. RetryAck at C1 | I / - | IS^D / -, request kept for retry | U | 0 | memory: 0 |
3. PCrdGrant at C1 | I / - | IS^D / -, credit held | U | 0 | memory: 0 |
4. ReadShared at H, accepted | I / - | IS^D / - | U, pend ReadShared C1 | 0 | memory: 0 |
H reads memory | I / - | IS^D / - | U, pend ReadShared C1 | 0 | memory: 0, Data(0) in flight to C1 |
5. CompData_UC at C1 | I / - | UC / 0 | U, pend ReadShared C1 | 0 | C1 and memory: 0 |
6. CompAck at H | I / - | UC / 0 | exclusive(C1) | 0 | C1 and memory: 0 |
UC is the CHI name for E: with no other holder, a ReadShared may be answered unique clean, as in the spec's Figures 5-1 and 5-2 (there the data comes straight from SN-F by Direct Memory Transfer (DMT: the memory node sends CompData straight to the requester)). The check below models the requester side, including the early credit.
def run(events, keep_early_credit=True):
st = {"pending": True, "retry": False, "credit": None, "accepted": False}
for ev, arg in events:
if ev == "RetryAck":
st["retry"] = True
if st["credit"] == arg:
st["accepted"] = True # resend with AllowRetry=0
elif ev == "PCrdGrant":
if st["retry"]:
st["accepted"] = True
elif keep_early_credit:
st["credit"] = arg # remember it for the RetryAck
return st["accepted"]
normal = [("RetryAck", 0), ("PCrdGrant", 0)]
early = [("PCrdGrant", 0), ("RetryAck", 0)]
assert run(normal)
assert run(early)
assert not run(early, keep_early_credit=False) # credit dropped, request starves
assert not run([("RetryAck", 0)]) # no credit yet, must wait
print("PASS: a resend needs a credit, and an early PCrdGrant must be kept")No. Retry is defined only for requests. A snoop response completes work the home already accepted. If the home could refuse it, a full request queue could stop the response that would free a tracker, which is the protocol deadlock from Part 5. The home must reserve space for the responses its outstanding snoops can cause.