Transactions: beats, bursts, split and posted
Tell a beat from a burst from a transaction, follow the request, data and response phases, say what a split transaction frees and what a posted write skips, and use the last marker to find where a burst ends.
- Lesson
- P1.4
- Time
- 30 min
- Runs on
- Your browser
- Check
- 5 questions
A monitor reported one item per handshake and a scoreboard expected one item per transaction. The test wrote one 16-byte block as a burst of four beats, and the scoreboard received four items where it expected one, each with a quarter of the data. Every comparison failed, and the design was right.
last marks the final beat, so the sink knows where the transaction ends.Each pill is one beat. The slots collect the burst; last lights on the fourth beat.
- Cycle 1
Beat 1 of 4: one handshake, one beat. A monitor that reports per handshake sends its first item now.
- Cycle 3
Beat 3. The sink has three of the four beats and no idea yet that a fourth is coming.
- Cycle 4
Beat 4 carries
last. The sink knows the burst ended, and a transaction-level monitor reports once, with all four beats.
The scoreboard was built at the level of transactions. The monitor spoke beats. Both were right about the bus and wrong about each other.
Beat, burst, transaction
A beat is one handshake on a data channel: one cycle where valid and ready are both 1, moving one word. A burst is the beats that belong to one request, sent back to back with one address at the start. A transaction is the whole exchange: the request, the beats, and the response that closes it.
| Level | Unit | Example on a 32-bit bus | Who counts it |
|---|---|---|---|
| Beat | One handshake, one word | 4 bytes in one cycle | The wires, a cycle-level monitor |
| Burst | The beats of one request | 4 beats, 16 bytes, one address | The data channel |
| Transaction | Request, beats, response | 16 bytes written and acknowledged | The initiator, a scoreboard |
The three levels nest. A monitor must say which level it reports at, and a scoreboard must expect the same one.
Bursts exist to amortise the request. Sending 16 bytes as four single-beat transactions costs four addresses, four arbitrations and four responses. One burst costs one of each, and the target can fetch the whole block at once. The longer the burst, the smaller the share of the wires spent on bookkeeping.
Request, data and response
A transaction has up to three phases. The request carries the address, the direction and the length. The data phase carries the beats. The response carries a status: the transfer worked, or it failed and why. A read puts its status beside the data; a write gets its status after the last beat.
A slot is a request the target is still working on.
- Cycle 1
Request A is accepted. The target starts fetching; the request wire is free again.
- Cycle 2
Request B is accepted one cycle later. Without split transactions the initiator would still be holding the bus for A.
- Cycle 4
Response A arrives three cycles after its request, with the data and a status.
- Cycle 6
Response B closes the second transaction. Two reads took seven cycles instead of eight, and the gap grows with the latency.
A split transaction accepts the request, releases the wires, and answers later. The response carries an identifier so the initiator knows which request it closes.
A split transaction is one whose response may come later than its request, with the wires free in between. On a bus that holds the wires until the response, a 20-cycle memory takes 21 cycles per read and nobody else can use the bus. With split transactions the same memory can accept a request every cycle, as long as it can hold them, which the next lesson measures.
Posted writes and what complete means
A posted write is complete, from the initiator's point of view, as soon as its request and data are accepted. No response comes back. A non-posted write waits for the response, so the initiator knows the data reached the target and whether it was accepted. Reads are always non-posted: the data is the response.
| Write kind | Complete when | Cost | Risk |
|---|---|---|---|
| Posted | The last beat is accepted | No response wire, no waiting | A read right after it may see the old value unless the path keeps order |
| Non-posted | The response arrives | One response per write, the initiator waits | None of the above; an error is reported |
PCIe posts memory writes; AXI returns a response for every write; AHB completes a write with its data phase.
The response is where errors live. A target that cannot serve the request answers with an error status, and an interconnect that found no target answers with a decode error. The initiator does not retry on its own. It raises an exception or sets a status bit, and software decides. A posted write has no response, so its error has nowhere to go but an interrupt.
The last marker
The request says how long the burst is, but a receiver that only sees the data channel needs a way to find the end without counting. The last signal is set on the final beat of every burst and on no other beat. A stream receiver, a FIFO that packs beats into packets, or a monitor can delimit transactions from last alone.
last is also a check. A burst of four beats whose last arrives on beat three, or never arrives, is a broken burst, and a checker that compares the count with the request catches it. AXI calls the signal WLAST and RLAST, AXI-Stream calls it TLAST, and a packet network marks an end-of-packet flag the same way.
Asked in interviews: transactions
What is a burst and why bother?
A burst is the beats of one request, sent back to back after one address. It spends one request, one arbitration and one response on many words instead of one each.
What is a split transaction?
A transaction whose response may come later than its request, with the wires free in between. It lets the bus carry other requests while a slow target works.
Posted or non-posted writes, what is the difference?
A posted write is complete once its data is accepted and gets no response. A non-posted write waits for a response that confirms it and may carry an error.
What is the difference between a beat and a transaction?
A beat is one handshake moving one word. A transaction is the request, all its beats and the response that closes it.
What does last do?
It marks the final beat of a burst, so a receiver can find the end without counting and a checker can tell a short burst from a complete one.
Try it
The decoder in the testbench counts beats and reports once per last. Run it: three transactions of four, one and two beats, from seven handshakes.
Then make the decoder report at the level of beats instead, by moving the $display out of the if (last) block, and watch the scoreboard's problem appear: seven reports for three transactions.
edge 3: beat a0 taken edge 4: beat a1 taken edge 5: beat a2 taken transaction 1: 4 beats, a0 to a3 edge 6: beat a3 taken (last) transaction 2: 1 beat, c0 to c0 edge 7: beat c0 taken edge 8: beat d0 taken transaction 3: 2 beats, d0 to d1 edge 9: beat d1 taken (last) checker: 7 transfers, 0 stalled edges, 0 rules brokenRead included file: link_sink.sv
// link_sink.sv: the target of the generic link (read-only in this lesson).
// A DEPTH-entry FIFO behind valid/ready. It takes a beat at every rising edge where valid and
// ready are both 1, and hands beats on to its consumer at every edge where drain and out_valid
// are both 1. ready is 1 while the FIFO has room: it never waits for valid.
module link_sink #(parameter int DEPTH = 4, parameter int WIDTH = 8) (
input logic clk,
input logic rst_n,
input logic valid,
input logic [WIDTH-1:0] data,
input logic last,
output logic ready,
input logic drain, // the consumer behind the FIFO takes a beat while 1
output logic out_valid,
output logic [WIDTH-1:0] out_data,
output logic out_last,
output int count // beats held right now
);
logic [WIDTH:0] mem [DEPTH]; // {last, data}
int wr = 0, rd = 0;
assign ready = (count < DEPTH);
assign out_valid = (count > 0);
assign {out_last, out_data} = mem[rd];
wire push = valid && ready;
wire pop = drain && out_valid;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr <= 0; rd <= 0; count <= 0;
end else begin
if (push) begin mem[wr] <= {last, data}; wr <= (wr + 1) % DEPTH; end
if (pop) rd <= (rd + 1) % DEPTH;
count <= count + (push ? 1 : 0) - (pop ? 1 : 0);
end
end
endmodule
Read included file: link_checker.sv
// link_checker.sv: the rules of the generic link, checked at every rising edge (read-only).
// Each broken rule is named with the edge where it was seen:
// reset valid is 0 while reset is active;
// hold once valid is 1 and ready is 0, valid is still 1 at the next edge (an offer is not withdrawn);
// stable while an offer waits, data and last do not change;
// known data is not x while valid is 1.
// The one rule no checker can see on the wires, "valid must not wait for ready", is the stimulus's
// own responsibility (lesson P1.3).
// It also counts transfers and stalled edges, and prints one summary line at the end of the run.
module link_checker #(parameter int WIDTH = 8) (
input logic clk,
input logic rst_n,
input logic valid,
input logic ready,
input logic [WIDTH-1:0] data,
input logic last
);
int cycle = -1, transfers = 0, stalls = 0, broken = 0;
logic pv = 0, pr = 0, pl = 0;
logic [WIDTH-1:0] pd = 'x;
always @(posedge clk) begin
cycle++;
if (!rst_n) begin
if (valid === 1) report("reset", "valid is 1 during reset");
end else begin
if (pv && !pr && valid !== 1) report("hold", "valid dropped before ready: the offer was withdrawn");
if (pv && !pr && valid === 1 && data !== pd) report("stable", "data changed while the offer waited");
if (pv && !pr && valid === 1 && last !== pl) report("stable", "last changed while the offer waited");
if (valid === 1 && $isunknown(data)) report("known", "data is x while valid is 1");
if (valid === 1 && ready === 1) transfers++;
if (valid === 1 && ready !== 1) stalls++;
end
pv = (valid === 1); pr = (ready === 1); pd = data; pl = last;
end
function void report(input string rule, input string what);
broken++;
$display("RULE %s broken at edge %0d: %s", rule, cycle, what);
endfunction
final $display("checker: %0d transfers, %0d stalled edges, %0d rules broken", transfers, stalls, broken);
endmodule
Read included file: link_tasks.svh
// link_tasks.svh: the source side of the generic link, as tasks for the stimulus (read-only).
// Every task returns one time unit after the rising edge where its last beat transferred, so the
// next call starts driving in the following cycle. `cycle` counts rising edges from 0.
int cycle = -1;
always @(posedge clk) cycle = cycle + 1;
// Offer one beat and hold it until the sink takes it.
task automatic send(input logic [7:0] d, input logic is_last = 0);
valid = 1; data = d; last = is_last;
do @(posedge clk); while (!ready);
#1 $display("edge %0d: beat %02h taken", cycle, d);
valid = 0; data = 'x; last = 0;
endtask
// A burst of n beats with consecutive values; last marks the final beat.
task automatic send_burst(input logic [7:0] first, input int n);
for (int i = 0; i < n; i++) begin
valid = 1; data = first + i[7:0]; last = (i == n - 1);
do @(posedge clk); while (!ready);
#1 $display("edge %0d: beat %02h taken%s", cycle, first + i[7:0], last ? " (last)" : "");
end
valid = 0; data = 'x; last = 0;
endtask
// Drive nothing for n cycles.
task automatic idle(input int n);
repeat (n) @(posedge clk);
#1;
endtask
Check yourself
Remember
- A beat is one handshake, a burst is the beats of one request, and a transaction is the request, its beats and its response.
- Bursts amortise the request: one address, one arbitration and one response for many words.
- A split transaction frees the wires between the request and the response; the response carries an identifier to say which request it closes.
- A posted write is complete once accepted and gets no response; a non-posted write waits for a status that may carry an error.
lastmarks the final beat of a burst, so a receiver can delimit transactions without counting and a checker can spot a short burst.
Practice
- Beats into transactions Implement · Easy