56 / 63 · Concept
A two-read, one-write register file with bypass
Implement two independent read ports and synchronous writes, then verify per-port collision bypass and initialization/valid policies.
Lessons are free to read. Enroll to save your learning progress.
Storage that supplies two values at once
An arithmetic unit needs both operands in the same cycle to compute A+B. A 2-read/1-write registerRegister A circuit that stores multiple bits of state. The synchronous registers in this course store their specified inputs at a clock edge. Learn more file provides two read addresses and one write address. Here it stores eight 16-bit words, with both reads also registered at the rising edge. The address width is 3 bits and storage capacity is 8×16=128 bits. Address 0 is ordinary storage; there is no special constant-register rule.
Each read port has its own enable and valid. One port can read while the other is idle, and both may read the same address. Reading two different addresses simultaneously has a cost distinct from simply increasing capacity. A small circuit can use a flip-flopFlip-flop An element that captures its input at a specified clock edge and holds it until the next edge. The input must meet setup and hold requirements. Learn more array and two selection networks. For a particular RAM resource, another option is to replicate storage and broadcast writes to provide enough read ports. An RTL declaration alone does not establish which resource synthesis will use.
When the value being written is the value being read
The contract here returns the new write data when an address is read and written at the same edge. Instead of waiting for the storage array to update, a bypass sends wr_data directly to the input of the read output register. Define the collision condition for port as:
When , the response is:
is the stored value immediately before the edge. compares each read address independently. Sending write data to both outputs because just one port collides would also change the operand from a different address. Omitting wr_en would wrongly bypass on an accidental wr_addr match even when no write is taking place.
module regfile8x16_2r1w (
input logic clk, rst, wr_en,
input logic [2:0] wr_addr,
input logic [15:0] wr_data,
input logic rd_en0, rd_en1,
input logic [2:0] rd_addr0, rd_addr1,
output logic [15:0] rd_data0, rd_data1,
output logic rd_valid0, rd_valid1
);
timeunit 1ns;
timeprecision 1ps;
logic [15:0] mem [0:7];
always_ff @(posedge clk) begin
if (rst) begin
rd_valid0 <= 1'b0;
rd_valid1 <= 1'b0;
end else begin
rd_valid0 <= rd_en0;
rd_valid1 <= rd_en1;
if (wr_en)
mem[wr_addr] <= wr_data;
if (rd_en0)
rd_data0 <= (wr_en && wr_addr == rd_addr0)
? wr_data : mem[rd_addr0];
if (rd_en1)
rd_data1 <= (wr_en && wr_addr == rd_addr1)
? wr_data : mem[rd_addr1];
end
end
endmoduleThis bypass changes input selection but retains the output register stage. Present the address and request before the edge and observe the response after it. A downstream register can capture the response at the next rising edge. Changing to a register file with combinational reads also changes the address-to-output path and latency contract; it is not simply a code simplification.
Example: a collision on only one port
Suppose M[2]=12 and M[5]=7 have already been written. Just before E0, enable both reads and set rd_addr0=2, rd_addr1=5, wr_en=1, wr_addr=2, and wr_data=20. After E0, port 0 returns the bypassed value 20, and port 1 returns the stored value 7. Adding these two valid responses gives 27, as opposed to 19 from read-first without bypass.
At E1, set both read addresses to 2 and write 30 to that address. Both outputs are 30. At E2, set wr_en=0, wr_addr=2, and wr_data=99 while reading address 2: both outputs must be 30. This one cycle exposes a bypass erroneously based on address equality alone. At E3, with rd_en0=0 and rd_en1=1, port 0 has valid=0 and port 1 has valid=1.
Each column records one rising edgeRising edge The instant at which the clock changes from 0 to 1. Distinguish it from a level, which refers to the entire interval during which CLK=1. Learn more. Inputs are sampled just before the edge; states whose names include “after” are shown immediately after the update. Column alignment indicates sample order, not physical propagation delayPropagation delay The time from an input change until the output settles to the correct value. Logical equivalence and timing behavior are separate properties. Learn more. M[2]=12 and M[5]=7 were stored in advance, and both read enables remain 1. The write address is 2. In the last column, wr_en=0, so wr_data=99 must not be bypassed.
View waveform data
| Signal | Wave | Bus values |
|---|---|---|
| edge | 234 | E0 → E1 → E2 |
| wr_en | 1.0 | |
| wr_data | 234 | 20 → 30 → 99 |
| rd_addr0 | 2.. | 2 |
| rd_addr1 | 23. | 5 → 2 |
| out0 after | 23. | 20 → 30 |
| out1 after | 23. | 7 → 30 |
What needs initialization?
An active-high synchronous reset prevents writes and clears both valid signals. It does not reset the array or output data. An output bus retaining its old value after reset is therefore normal when valid=0. Previously stored array contents are not erased by this RTL reset. If the system allows only new work after reset, define the initialization sequence for that work separately.
Reading an address on the same edge as its first write produces a defined new value in this implementation because of bypass. However, reading an address that has never been initialized, without a simultaneous write, remains undefined. rd_valid marks request-response timing; it is not an initialized bit for each address. Before constructing the expected value for an ordinary read, the checker must establish whether the address contains a known value and handle same-edge bypass separately.
Verification must cover all four combinations of read enables, equal and different read addresses, collisions with neither port, port 0, port 1, or both, consecutive writes, and reset priority. Multiple write ports are outside this lesson's scope. Adding them would first require a new priority rule for two writes to the same address. Port count changes both the functional contract and resource cost.
Try it yourself
Initially M[1]=4 and M[4]=9. With both read enables set to 1 and read addresses 1 and 4, what output pair results from writing 13 to address 4? At the next edge, what happens if wr_en=0, wr_addr=1, wr_data=99, and both read addresses are 1? Finally, explain valid and data-consumption rules when only port 0 is disabled.
Read the explanation
The first response is (4,13). Only port 1 collides with the write address, so it bypasses the new value 13. The next response is (4,4); wr_en=0 prevents the use of 99. In the last cycle, rd_valid0=0 and rd_valid1=1. Port 0 holds its previous data-register value, but it must not be consumed as a valid response. Address 0 can be written and read like any other address.