A byte enable masks the write lane by lane. Only the enabled bytes change; the rest keep their old values.
That turns partial-word writes into one masked access. Without enables, a partial write needs a read-modify-write around it.
Word | Enables | Effect |
|---|---|---|
32-bit, 4 enables | All set | Full-word write |
32-bit, 4 enables | One set | One byte written, three kept |
16-bit, 2 enables | Low set only | Low byte written, high byte kept |
A sync read returns data one cycle late, so a read of the address just written gets stale data. A bypass path forwards the write data when the addresses match.
// not-run: illustrative excerpt, not a complete testbench
always @(posedge clk) begin
if (we)
mem[waddr] <= wdata;
if (we && (waddr == raddr))
rdata <= wdata;
else
rdata <= mem[raddr];
endThe listing above is an illustrative excerpt, not a runnable testbench.
The comparison costs little next to the memory itself. Skipping it corrupts every read-after-write to the same address.
The script below collects an 8-entry memory with per-byte write enables into one memory cell, then maps it to 15 flop cells plus the read muxes. Masked writes still infer one cell.
read_verilog <<EOV
module top (
input wire clk,
input wire [1:0] we,
input wire [2:0] waddr,
input wire [2:0] raddr,
input wire [15:0] wdata,
output reg [15:0] rdata
);
reg [15:0] mem [0:7];
always @(posedge clk) begin
if (we[0])
mem[waddr][7:0] <= wdata[7:0];
if (we[1])
mem[waddr][15:8] <= wdata[15:8];
rdata <= mem[raddr];
end
endmodule
EOV
hierarchy -check -top top
proc
memory_collect
select -assert-count 1 t:$mem*
memory_map
select -assert-none t:$mem*
select -assert-count 15 t:$dff
select -assert-count 269 t:$mux
log PASS: 8x16 byte-enabled memory collects to one memory cell and maps to flopsByte 1 only. The other three bytes keep their old values.
Stale data from the previous contents. The new data is still one read latency away.