A processor reads a register in a peripheral. While the read is in flight, a watchdog resets the peripheral. The peripheral comes back clean, with no memory of the read. The processor is still waiting for its answer, and nothing in the system will ever send it. A reset is supposed to make things safe; on one side of an interface it made the other side hang.
What must an interface guarantee during and after reset, and what happens to transactions caught by one?
The obvious view is that reset returns a block to a known state, so it is always safe. For the block itself, it is. But an interface has two sides, and a reset usually reaches only one. Whatever the reset side "forgets" (outstanding requests, credits it owed, data half-sent), the other side still remembers and is still waiting for. Reset semantics are about the boundary, not the block.
Question | Rule |
|---|---|
What does an interface drive during reset? | Inactive values: no valid asserted, no requests; ready may be low. A block in reset must not create transfers. |
When may a block start after reset? | Only when its partner is out of reset and ready, and any training or credit initialisation has completed (lessons 4.4 and 11.4). |
What happens to requests outstanding at the reset side? | They are lost. The requester must learn this: a timeout, an error response from a wrapper, or a reset of the requester too. |
What happens to credits, sequence numbers, tags? | Both sides must reset them together, or re-initialise them by an exchange; one-sided resets corrupt them (lesson 4.8). |
The requester issues a read in cycle 1. The target would answer four cycles later, but it is reset in cycle 3 and forgets the read. The requester has a 6-cycle completion timeout.
// A read caught by a reset. The requester issues a read in cycle 1; the target
// would answer 4 cycles later, but the target is reset in cycle 3 and forgets the
// read. The requester has a completion timeout of 6 cycles: when no response has
// arrived it completes the read with an error, so it does not wait for ever. The
// checker confirms no stale response ever arrives after the reset, and that the
// timeout turned the lost read into an error.
module tb;
logic clk = 0;
always #5 clk = ~clk;
initial begin $dumpfile("wave.vcd"); $dumpvars(0, tb); end
int cyc = 0;
always @(posedge clk) cyc <= cyc + 1;
wire req = (cyc == 1); // the requester's read
wire rst_t = (cyc == 3); // target reset pulse
// Target: remembers a pending read and answers 4 cycles after it arrived
logic pending = 0;
logic [2:0] age = 0;
logic rsp = 0;
always @(posedge clk) begin
rsp <= 0;
if (rst_t) begin pending <= 0; age <= 0; end // reset: the read is forgotten
else if (req) begin pending <= 1; age <= 0; end
else if (pending) begin
age <= age + 1;
if (age == 3) begin rsp <= 1; pending <= 0; end
end
end
// Requester: waits for the response, with a 6-cycle completion timeout
logic waiting = 0, timeout_err = 0;
logic [3:0] wait_cnt = 0;
always @(posedge clk) begin
timeout_err <= 0;
if (req) begin waiting <= 1; wait_cnt <= 0; end
else if (waiting) begin
if (rsp) waiting <= 0;
else if (wait_cnt == 5) begin waiting <= 0; timeout_err <= 1; end
else wait_cnt <= wait_cnt + 1;
end
end
int responses = 0, errors_seen = 0;
always @(posedge clk) begin
if (rsp) responses++;
if (timeout_err) errors_seen++;
end
initial begin
#160;
if (responses == 0 && errors_seen == 1) $display("PASS: the reset dropped the read; the requester's timeout completed it with an error instead of hanging");
else $display("FAIL: responses %0d timeout errors %0d", responses, errors_seen);
$finish;
end
endmoduleThe target's reset branch clears pending: the read simply ceases to exist on that side. Only the requester's timer turns that into something software can see.
How to read it, slot by slot:
Slot 0: the read goes out (req); from slot 1 the target holds it (pending) and the requester is waiting (waiting).
Slot 2: the target is reset (rst_t). In slot 3 pending is 0: the read is gone.
Slots 3 to 6: nothing happens. rsp never rises. Without a timeout, waiting would stay high for ever.
Slot 7: the timeout fires (timeout_err); the requester completes the read with an error and stops waiting.
Approach | How |
|---|---|
Timeouts in requesters | Lost requests become errors after a bounded wait (lesson 9.9) |
A reset-aware wrapper | Sits in front of the target; while the target is in reset or has just come out of it, answers outstanding and new requests with errors |
Quiesce before reset | Drain outstanding traffic before resetting a block (lesson 12.5) |
Reset both sides together | Where the interface allows, a shared reset clears both sides' state at once |
Protocol | Reset rules |
|---|---|
AXI | During reset a manager must drive ARVALID, AWVALID and WVALID low and a subordinate RVALID and BVALID low; transactions outstanding across a reset of one side are not defined by the protocol, so systems add timeouts or wrappers. |
PCI Express | A link reset retrains the link and re-initialises flow control; requests that never get a completion end in a completion timeout. |
The opening system (illustrative) reset a peripheral from a watchdog without telling the interconnect. The processor's read hung, the core stopped, and a second watchdog reset the whole chip. Adding a reset-aware wrapper that answered any request to the peripheral with an error while it was resetting kept the processor alive; the driver saw the error and re-initialised the peripheral.
So it cannot create a transfer from undefined state while it is not working; its partner would otherwise accept garbage as a real transaction.
The sender may believe it has credits for buffer slots the receiver no longer considers occupied, or vice versa: overflow or a permanently reduced count. Credits must be re-initialised on both sides.
Reset clears one side's state; the other side still remembers outstanding requests, credits and tags.
During reset, drive interfaces inactive; start traffic only when both sides are out of reset and initialised.
Handle one-sided resets with timeouts, reset-aware wrappers, quiescing, or joint resets.