A requester writes 5 to an address, gets the write response, then reads the same address and gets the old value, 3. Every component did its job. The write was accepted by a write buffer that answered early. The read took a separate read path that did not look in the write buffer, reached memory first, and returned what memory held. One address, one requester, wrong answer.
Can a read return data older than a write you already got a response for, and how do designs prevent it?
The obvious answer is no: accesses to the same address are always kept in order (lesson 7.2). That is the promise, but it holds only if the two accesses meet at one point of serialisation (lesson 7.3) in issue order. Whenever a write and a later access to the same address can take different paths (a write buffer and a read bypass, two links, two interconnect routes), they can arrive in the wrong order, and the promise has to be kept by extra hardware.
Hazard | What goes wrong | Typical cause |
|---|---|---|
Read after write (RAW) | A read returns the value before an earlier write to the same address | The write waits in a buffer that the read does not check; the read takes a faster path |
Write after write (WAW) | An older write lands after a newer one, leaving the old value | Two writes take different paths, or a buffer drains out of order |
Technique | How it works | Cost |
|---|---|---|
Check the buffer (forwarding) | A read compares its address with buffered writes; on a match it returns the buffered data (or waits for it to drain) | An address comparison per buffer entry |
Same path for same address | Route all accesses to an address through one path (for example by hashing the address to choose a link) | Less freedom to balance load |
Block on match | A read that matches a buffered write waits until that write has drained | Latency for that read only |
In-order buffers | Write buffers drain in arrival order, and two writes to one address never take different paths | Prevents WAW only |
Requester waits | Issue the read only after the write's final acknowledgement | A round trip, paid by software |
Processors do the first one constantly: store-to-load forwarding lets a load read a value still in the store buffer. Interconnects and bridges do the second and third.
Protocol | Same-address order |
|---|---|
AXI | No ordering between the read and write channels, even for the same ID and address: a requester that needs a read to see its write must wait for the write response; components that buffer writes and answer early must make later reads to the same address see them. |
PCI Express | A read may not pass earlier posted writes in the same direction (lesson 7.6), so a read to the same address sees writes sent before it on that path. |
The opening system (illustrative): an interconnect added a write buffer to speed up memory writes and answered them early, but its read path went straight to memory without checking the buffer. Single-threaded code that wrote and then read a variable failed under load. Adding an address check that held matching reads until the write drained fixed it, and cost almost nothing, because matches were rare.
A write-after-write hazard: the older write may land last. Routing every access to an address over the same link (or waiting for the first write's acknowledgement) prevents it.
Reads that match a still-buffered write are rare, so only those few reads pay the extra latency.
Same-address order holds only if both accesses meet one point of serialisation in issue order.
Buffers that answer early and separate paths create read-after-write and write-after-write hazards.
Prevent them with forwarding, blocking on address match, address-based routing, or waiting for final acknowledgement.