A chip is reused with a different partner. The new partner has half the receive buffering of the old one, but the sender's credit counter still starts at the old value, hard-wired in its RTL. The first heavy burst overflows the partner's buffer. Nothing in the credit mechanism was wrong except the number it started from.
How does a sender learn how many credits it starts with, and what must happen to them on reset?
The obvious answer is to build the initial credit count into the sender as a constant equal to the receiver's buffer size. That couples the two sides: every receiver must have exactly that buffer, forever, or the constant must be changed and the sender rebuilt. It breaks the moment two independently designed devices meet, which is the normal case for any protocol between chips.
Credit protocols between independently designed parts let the receiver tell the sender how many credits it is granting, during start-up and before any data flows:
Advertise. After the link comes up, each receiver sends a message stating its credit limit for each credit type.
Confirm. The protocol repeats or acknowledges the advertisement so both sides know initialisation is complete; a lost message must not leave one side with no credits.
Then send. Only after initialisation may data flow; from then on credits are returned as slots free.
This keeps the contract minimal (lesson 0.1): the receiver's buffer size is the receiver's own business, stated at run time, not a constant both sides must share.
Some receivers can promise to accept a type of traffic always, for example because they only receive responses to requests they issued themselves and reserved space for in advance. Such a receiver can advertise infinite credits for that type: the sender stops counting and never waits for that traffic. It is a strong promise, and protocols use it where a stall would be dangerous: a requester that refuses its own responses can deadlock the system (lesson 9.5).
Credits are state at both ends, so every event that resets one end must reset the other's view of it:
Link reset or retraining: both sides discard their credit counts and repeat initialisation. Credits in flight at the time are forgotten, and so are the words they covered, which is why a link reset is also a transaction-layer event.
One side resets alone: its partner still holds credits for buffers that were just emptied (or that no longer exist). The protocol must force the partner back through initialisation, or credits and buffers disagree from then on.
Approach | What it costs | When it wins |
|---|---|---|
Fixed initial credits in the sender | Couples sender to one receiver design | Both ends designed together, on one chip |
Advertised at start-up | An initialisation step before data; messages that carry limits | Independently designed devices |
Infinite credits for a type | The receiver must truly always accept it | Responses to the receiver's own requests |
Re-advertise after every reset | A start-up delay after each reset | Always, for any link where one side can reset alone |
Protocol | Its choice |
|---|---|
PCI Express | Credits are advertised during flow-control initialisation (InitFC1 and InitFC2 data link layer packets) and returned during operation with UpdateFC packets. A receiver may advertise infinite credits for a credit type, turning flow control off for it; endpoints must advertise infinite completion credits, so they can never refuse a completion for lack of credit. |
PCIe's two initialisation phases are the "advertise, then confirm" pattern: the second phase makes sure both sides know the first one completed before data is allowed to flow.
A device resets its receive logic after an internal error without telling its link partner (illustrative). Its buffers are now empty, but the partner still believes some slots are occupied and holds fewer credits than it should: the link runs slowly forever. The opposite case is worse: a partner that believes it holds credits for buffers the device no longer has will overflow them. The fix is a protocol rule, not a counter tweak: any reset of the credit-holding logic must force both sides back through credit initialisation.
Only the receiver knows its own buffer sizes. Advertising them at start-up keeps the two designs independent.
Both sides discard their credit state and re-initialise; the in-flight words and credits are lost from the link layer's view, so higher layers must recover any lost transactions.
The sender never waits for that traffic. If the receiver ever cannot accept it, there is no flow control to stop the sender, and data is lost or the receiver must stall something else.
Between independently designed parts, the receiver advertises its credit limits at start-up, before any data flows.
Infinite credits mean "I will always accept this type"; protocols require it where refusing would deadlock.
Any reset of either side's credit state must send both back through initialisation.