Addresses, widths, byte lanes and alignment
Place a byte, a halfword or a word on a wide data bus, say which lanes carry it and which strobes are set, and explain why alignment matters.
- Lesson
- P1.8
- Time
- 25 min
- Runs on
- Your browser
- Check
- 5 questions
A driver wrote one status byte into a control word with a plain 32-bit write, data in the low lane and every strobe set. The byte landed, and the three neighbouring bytes of that word became zero. The interrupt mask next to it vanished, and the system stopped answering an hour later, three functions away from the write that caused it.
Lit slots are the lanes this beat writes; a lane with its strobe at 0 keeps its byte.
- Cycle 1
An aligned word at address 10: all four strobes set, every lane written.
- Cycle 2
A byte at address 12. The address ends in 2, so the byte rides in lane 2 and only strobe 2 is set.
- Cycle 3
A halfword at address 21, an odd address inside the word at 20: lanes 1 and 2, strobes 0110.
- Cycle 4
A halfword at address 27 crosses the end of its word. The first beat writes lane 3 of the word at 24.
- Cycle 5
The second beat writes lane 0 of the word at 28. Two beats for two bytes: the cost of an unaligned start.
The data bus is 32 bits wide in every beat. The strobes say which of its bytes are real.
Address map, bus width and transfer size
An address map assigns every target a range of byte addresses, and the interconnect decodes the upper address bits to pick the target. Within a target, the address names a byte, even on a bus that moves four bytes at a time.
Three sizes meet on a bus and they are different numbers. The data width is the number of wires, 32 here, so one beat can carry four bytes. The transfer size is how many bytes the initiator means to move in this beat: one, two or four. The address says where those bytes start.
| Transfer | Start address | Lanes used | Strobes | Beats |
|---|---|---|---|---|
| word | 10 | 0, 1, 2, 3 | 1111 | 1 |
| byte | 12 | 2 | 0100 | 1 |
| halfword | 21 | 1, 2 | 0110 | 1 |
| halfword | 27 | 3, then 0 of the next word | 1000, then 0001 | 2 |
| word | 22 | 2, 3, then 0, 1 of the next word | 1100, then 0011 | 2 |
The same bus, five transfers. The lane is the low two address bits; the strobe pattern follows from it.
Byte lanes and little-endian placement
Lane i of a 32-bit bus carries the byte at address 4n + i, in bits 8i to 8i+7 of the data signal. Lane 0 is the lowest address and the lowest bits. That is little-endian placement, and every AMBA bus, PCIe and most on-chip links use it.
So the word 44332211 written at address 10 puts byte 11 at address 10 and byte 44 at address 13. Reading address 12 as a byte gives 33. The order looks reversed on paper only because we write numbers with the big end first.
A byte at address 12 must travel in lane 2, bits 16 to 23. Placing it in lane 0 and setting all four strobes writes it to address 10 and clears the other three bytes, which is the opening story. The byte-write task in the bench puts the byte in every lane and lets the strobe pick, so the data bus never has to be shifted by hand.
Byte enables keep the neighbours
A byte enable, or write strobe, is one bit per lane that says "write this byte". A lane whose strobe is 0 carries nothing the target may use, and the target must leave that byte alone. Without strobes, a bus can only write whole words, and software must read, merge and write back to change one byte.
Strobes are the difference between a byte write and a word write with junk. The address picks the lane; the strobe pattern is that lane's bit, shifted into place. For a halfword starting at lane 1 the pattern is 0011 shifted left by one: 0110.
A byte write with every strobe set
beat(8'h12, 32'h000000aa, 4'b1111);
// bytes 10..13: aa 00 00 00
The byte lands at the wrong address and three neighbours are erased.
The byte in its lane with one strobe
beat(8'h12, {4{8'haa}}, 4'b0100);
// bytes 10..13: 11 22 aa 44
Only lane 2 is written. The other bytes keep their values.
Aligned and unaligned starts
A transfer is aligned when its start address is a multiple of its size: a halfword at an even address, a word at a multiple of four. An aligned transfer always fits inside one word, so it takes one beat.
An unaligned start may cross the end of a word. A halfword at address 27 needs the byte at 27, lane 3 of one word, and the byte at 28, lane 0 of the next. The initiator either splits it into two beats, as the bench does, or the bus forbids it. Many buses allow an unaligned first beat and then continue aligned; some register buses allow only aligned words.
A narrow transfer is a beat that uses fewer lanes than the bus has, such as a byte on a 32-bit bus. Narrow bursts walk through the lanes: a burst of bytes from address 11 uses lanes 1, 2, 3, then 0 of the next word, and the strobe pattern rotates with it.
Asked in interviews: byte lanes and alignment
What is a byte enable?
One bit per byte lane of the data bus that says whether this beat writes that lane. A lane with its enable at 0 keeps its byte in the target.
How does a 16-bit transfer travel on a 64-bit bus?
In the two lanes named by the low three address bits, with only those two enables set. The other six lanes carry nothing.
What does alignment mean and why does it matter?
A transfer is aligned when its address is a multiple of its size, so it fits in one word and one beat. An unaligned transfer may cross a word and need two beats, or be forbidden.
Which lane carries the byte at address 4n+1?
Lane 1, bits 8 to 15 of the data signal, on a little-endian 32-bit bus. The lane number is the low two address bits.
What goes wrong with a word write used as a byte write?
The byte lands in lane 0, so at the wrong address unless the address was aligned, and every other byte of the word is overwritten.
Try it
The first pair of lines is the opening bug: a byte written as a whole word. The second pair writes the same byte through its lane. Then a halfword lands inside one word at an odd address, and a halfword at 27 takes two beats because it crosses into the word at 28.
Change the halfword address to 26 and it takes one beat with strobes 1100. Then write a word at address 22 with two beat calls of your own and dump bytes 20 to 27.
edge 1: word 10 strobe 1111 data 44332211 edge 2: word 10 strobe 1111 data 000000aa bytes 10..13: aa 00 00 00 edge 3: word 10 strobe 1111 data 44332211 edge 4: word 10 strobe 0100 data aaaaaaaa bytes 10..13: 11 22 aa 44 edge 5: word 20 strobe 0110 data 00beef00 bytes 20..23: 00 ef be 00 edge 6: word 24 strobe 1000 data 34343434 edge 7: word 28 strobe 0001 data 12121212 bytes 24..2b: 00 00 00 34 12 00 00 00Read included file: lane_mem.sv
// lane_mem.sv: 256 bytes behind a 32-bit data bus with one write strobe per byte lane (read-only).
// A write happens at an edge where we is 1: for every lane i whose strobe bit wstrb[i] is 1, byte i
// of wdata is stored at byte address {addr[7:2], i}. Lane 0 is the lowest address of the word and
// the lowest 8 bits of wdata (little-endian placement). A lane whose strobe is 0 keeps its byte.
// rdata always shows the four bytes of the word that holds addr, lane 3 on the left.
module lane_mem (
input logic clk,
input logic we,
input logic [7:0] addr,
input logic [31:0] wdata,
input logic [3:0] wstrb,
output logic [31:0] rdata
);
logic [7:0] mem [256];
wire [7:0] base = {addr[7:2], 2'b00}; // the word that holds addr
initial for (int i = 0; i < 256; i++) mem[i] = 8'h00;
always_ff @(posedge clk)
if (we) for (int i = 0; i < 4; i++)
if (wstrb[i]) mem[base + i[7:0]] <= wdata[8 * i +: 8];
assign rdata = {mem[base + 8'd3], mem[base + 8'd2], mem[base + 8'd1], mem[base]};
endmodule
Read included file: lane_tasks.svh
// lane_tasks.svh: the initiator's tasks on the 32-bit bus, and a byte dump (read-only).
// `cycle` counts rising edges from 0. One beat is one edge with we = 1.
int cycle = -1;
always @(posedge clk) cycle = cycle + 1;
bit quiet = 0; // 1: print nothing but what the stimulus prints itself
// One beat: the word that holds a, with the lanes named in strobe written from d.
task automatic beat(input logic [7:0] a, input logic [31:0] d, input logic [3:0] strobe);
we = 1; addr = a; wdata = d; wstrb = strobe;
@(posedge clk); #1;
if (!quiet) $display("edge %0d: word %02h strobe %b data %08h", cycle, {a[7:2], 2'b00}, strobe, d);
we = 0; addr = 'x; wdata = 'x; wstrb = 4'b0000;
endtask
// A whole word at an aligned address.
task automatic write_word(input logic [7:0] a, input logic [31:0] d);
beat(a, d, 4'b1111);
endtask
// One byte at any address: it rides in lane a[1:0], and only that lane's strobe is 1.
task automatic write_byte(input logic [7:0] a, input logic [7:0] b);
beat(a, {4{b}}, 4'b0001 << a[1:0]);
endtask
// A halfword at any address, low byte at a: one beat inside a word, two beats across a word end.
task automatic write_half(input logic [7:0] a, input logic [15:0] h);
if (a[1:0] == 2'd3) begin
write_byte(a, h[7:0]);
write_byte(a + 8'd1, h[15:8]);
end else beat(a, {16'h0000, h} << (8 * a[1:0]), 4'b0011 << a[1:0]);
endtask
// The byte stored at address a (straight from the memory array).
function automatic logic [7:0] byte_at(input logic [7:0] a);
return mem_i.mem[a];
endfunction
// Print n bytes from address a, lowest address first.
task automatic dump(input logic [7:0] a, input int n);
string line = "";
for (int i = 0; i < n; i++) line = {line, $sformatf(" %02h", byte_at(a + i[7:0]))};
$display("bytes %02h..%02h:%s", a, a + n[7:0] - 8'd1, line);
endtask
Check yourself
Remember
- A 32-bit bus has four byte lanes. Lane i carries the byte at address 4n+i in bits 8i to 8i+7: little-endian placement.
- The low address bits pick the lane; the transfer size picks how many lanes; the strobes say which lanes are written.
- A lane with its strobe at 0 keeps its byte. A byte written as a whole word clears its neighbours.
- A transfer is aligned when its address is a multiple of its size. An unaligned transfer may cross a word and need two beats.
Practice
- Fix the strobe Fix · Easy