Only when nothing in the system depends on it. Two things depend on a cache: the dirty values only it holds, and every record at H that names it. The rule: before power-off, every M or O line is written back with PutM and its Put-Ack received, every clean S or E line is released with PutS or PutE (or a bulk Leave, next lesson) and acknowledged, and every outstanding miss, writeback and forward response has completed. Only then does the cache hold nothing H knows about.
Course-model extension for this topic: C0's cache can be powered down. A powered-off or retained controller answers nothing, not even 'I do not have it'. The topic adds four course-defined messages, Leave, Leave-Ack, Join and Join-Ack, for leaving and rejoining the coherence domain; they are not messages of any named protocol. Everything else follows the course model.
Structure | Job during power-down |
|---|---|
Core interface gate | Stops new loads and stores from the core before the flush starts, so the flush is not chasing refills. |
Flush engine | Walks every set and way (or a list of dirty lines), and turns each valid line into PutM, PutE or PutS through the normal eviction path. |
Writeback (WB) entries | Hold each dirty value in MI^A until its Put-Ack, and answer any Inv or Fwd for it meanwhile. |
Outstanding counter | Counts Puts sent minus Put-Acks received, plus busy MSHRs. |
Snoop port | Stays powered and answers Inv and Fwd for the whole flush. |
Power controller | Removes the supply only when the done signal below is true. |
Done = core stopped, every line I, MSHRs free, WB entries free, outstanding counter zero. A Put-Ack is the only evidence that H has taken a line back. Sending the Put proves nothing (see Writeback Buffers).
MSI. This case adds a second line Y, which like X starts at 0 in memory and in no cache. Warm-up: C0 acquired M on X and stored 1, then read Y and holds it S / 0. C0 is told to power down. The flush engine issues both Puts at once. With no FIFO order, the Put-Acks may come back in either order; here Y's arrives first. The single PutM steps are those of Dirty Writeback; what is new is that power-off waits for the last acknowledgement, not the first.
Step | C0 state / value | C1 state / value | H directory | Memory | Newest data |
|---|---|---|---|---|---|
0. Warm-up | X: M / 1, Y: S / 0 | X: I / -, Y: I / - | X: exclusive(C0), Y: S{C0} | X = 0, Y = 0 | C0: X = 1, C0 and memory: Y = 0 |
Power-down request, core stopped | X: M / 1, Y: S / 0 | X: I / -, Y: I / - | X: exclusive(C0), Y: S{C0} | X = 0, Y = 0 | C0: X = 1, C0 and memory: Y = 0 |
Flush engine evicts both | X: MI^A / 1 (in WB), Y: SI^A / - | X: I / -, Y: I / - | X: exclusive(C0), Y: S{C0} | X = 0, Y = 0 | C0 WB: X = 1, PutM(1) in flight to H, memory: Y = 0 |
1. PutM(X) at H | X: MI^A / 1 (in WB), Y: SI^A / - | X: I / -, Y: I / - | X: exclusive(C0), pend PutM C0, Y: S{C0} | X = 0, Y = 0 | C0 WB and H buffer: X = 1, memory: Y = 0 |
2. PutS(Y) at H | X: MI^A / 1 (in WB), Y: SI^A / - | X: I / -, Y: I / - | X: exclusive(C0), pend PutM C0, Y: U | X = 0, Y = 0 | C0 WB and H buffer: X = 1, memory: Y = 0 |
3. Put-Ack(Y) at C0 | X: MI^A / 1 (in WB), Y: I / - | X: I / -, Y: I / - | X: exclusive(C0), pend PutM C0, Y: U | X = 0, Y = 0 | C0 WB and H buffer: X = 1, memory: Y = 0 |
H writes memory | X: MI^A / 1 (in WB), Y: I / - | X: I / -, Y: I / - | X: U, Y: U | X = 1, Y = 0 | C0 WB, H buffer and memory: X = 1, memory: Y = 0 |
4. Put-Ack(X) at C0 | X: I / -, Y: I / - | X: I / -, Y: I / - | X: U, Y: U | X = 1, Y = 0 | memory: X = 1, Y = 0 |
C0 powers off | off, all lines I | X: I / -, Y: I / - | X: U, Y: U | X = 1, Y = 0 | memory: X = 1, Y = 0 |
After row 3 the counter is 1, not 0: X's WB entry still holds the only cached copy, and until H writes memory it is one of only two copies. Powering off on the first acknowledgement would be the same mistake as freeing a WB entry when PutM is sent.
Shortcut | What H still records | Next event | Result |
|---|---|---|---|
Power off with X in M / 1 | X: exclusive(C0) | C1 sends GetS(X), H sends Fwd-GetS to C0 | Lost data and a hang: the only 1 is gone, and the Fwd is never answered, so C1 waits in IS^D for ever. |
Power off with Y in S, no PutS | Y: S{C0} | C1 sends GetM(Y), H sends Inv to C0 | Hang: no Inv-Ack ever comes. The data is safe; progress is not. |
Power off with a miss outstanding | Y: S{C0} once H grants it | H sends Data to a powered-off cache | H records a sharer that has nothing; the next Inv to it hangs. |
Power off when the Puts are sent | X: exclusive(C0) until PutM arrives | A Fwd-GetM crossing the PutM | Hang, traced in A Power-Down Race. |
Note the second row. With silent clean eviction, a stale sharer bit is normally harmless, because a live cache answers an Inv for a line it no longer has. A powered-off cache cannot, so a design with silent eviction must still tell H, line by line or in bulk, before power-off. The same holds for any home that over-approximates holders, such as the snoop filters of Filter Eviction and Back-Invalidation.
Not in this design. The WB entry must also answer any Inv or Fwd for X until Put-Ack, and the controller logic that does that is what loses power. Saving the bits without the logic that answers requests keeps the value but still hangs the next forward.