Design a small register file with one write port and two combinational read ports that show the new word when it is read in the cycle it is written.
Hardware context: A 16 x 8 register file with one write port and two combinational read ports for a small processor. Design the two-read, one-write register file with write-first reads.
A small processor reads two operands and writes one result in every cycle. Its register file is 16 words of 8 bits, small enough to build from flops, with one write port and two read ports (lesson R5.1). The pipeline reads a register in the same cycle an older instruction writes it, so the brief asks for write-first reads: the read shows the word being written, not the old one.
Brief: parameters DEPTH (2 to 16, a power of two) and WIDTH (1 to 16); ports clk and rst_n (asynchronous, active-low), we, waddr, wdata, raddr_a, raddr_b in, rdata_a and rdata_b out. A write lands at the rising edge when we is 1. Both reads are combinational: rdata follows its address in the same cycle. When we is 1 and a read address equals waddr, that port shows wdata. Reset clears every word to 0. Corner cases: both ports on the address being written, a matching address while we is 0, a word written twice. The tests run copies with DEPTH 16 and 8. Synthesis check: the design must build on the site's synthesis flow with no latch. The cell and level counts come from an open-source flow and are a proxy for area and depth, not a foundry result.
Signals in the timeline
| Signal | Width | Role |
|---|---|---|
sel | 1 | input of each check |
writes | 104 | input of each check |
reads | 105 | input of each check |
Full specification
module regfile2r #(parameter int DEPTH = 16, parameter int WIDTH = 8) (
input logic clk,
input logic rst_n, // asynchronous, active-low
input logic we,
input logic [$clog2(DEPTH)-1:0] waddr,
input logic [WIDTH-1:0] wdata,
input logic [$clog2(DEPTH)-1:0] raddr_a,
output logic [WIDTH-1:0] rdata_a,
input logic [$clog2(DEPTH)-1:0] raddr_b,
output logic [WIDTH-1:0] rdata_b
);
// your design
endmodule
| In the cycle | rdata_a (rdata_b the same with raddr_b) |
|---|---|
| we = 1 and waddr = raddr_a | wdata: the word being written |
| otherwise | the word at raddr_a: the last value written there, or 0 since reset |
| rst_n = 0 (at any time) | every word is 0 at once |
The tests reset the file, apply up to eight writes, then run five cycles in which a write may happen while both ports read. Both ports are sampled 1 ns before each of those five edges: c0_rdata_a and c0_rdata_b to c4_rdata_a and c4_rdata_b.
Examples
Example 1
Four words, two ports- Input
DEPTH 16. Writes: 3<-3c, 9<-99, 12<-c5, 15<-f0. Then: no write, read 3 and 9; no write, read 12 and 15; no write, read 15 and 3; no write, read 0 and 12; no write, read 9 and 9
- Output
c0_rdata_a = 0x3c, c0_rdata_b = 0x99, c1_rdata_a = 0xc5, c1_rdata_b = 0xf0, c2_rdata_a = 0xf0, c2_rdata_b = 0x3c, c3_rdata_a = 0x0, c3_rdata_b = 0xc5, c4_rdata_a = 0x99, c4_rdata_b = 0x99
Words 3, 9, 12 and 15 hold what was written; word 0 was never written and reads 00 after reset. Both ports may read the same word in one cycle.
Example 2
Read the word being written- Input
DEPTH 16. Writes: 5<-55, 6<-66. Then: no write, read 5 and 6; write 5<-a5, read 5 and 6; no write, read 5 and 6; write 6<-b6, read 2 and 6; write 2<-22, read 6 and 2
- Output
c0_rdata_a = 0x55, c0_rdata_b = 0x66, c1_rdata_a = 0xa5, c1_rdata_b = 0x66, c2_rdata_a = 0xa5, c2_rdata_b = 0x66, c3_rdata_a = 0x0, c3_rdata_b = 0xb6, c4_rdata_a = 0xb6, c4_rdata_b = 0x22
In cycle 1 word 5 is written with a5 while port A reads it: port A shows a5 at once, and a5 stays in later cycles. In cycles 3 and 4 port B reads the word being written (6, then 2) and forwards it, while port A reads stored words.
Example 3
A matching address with we low- Input
DEPTH 16. Writes: 4<-44, 8<-88. Then: no write, read 8 and 4; we 0 (4, ee), read 4 and 8; we 0 (8, 11), read 8 and 4; write 4<-4f, read 4 and 8; we 0 (4, 00), read 4 and 8
- Output
c0_rdata_a = 0x88, c0_rdata_b = 0x44, c1_rdata_a = 0x44, c1_rdata_b = 0x88, c2_rdata_a = 0x88, c2_rdata_b = 0x44, c3_rdata_a = 0x4f, c3_rdata_b = 0x88, c4_rdata_a = 0x4f, c4_rdata_b = 0x88
In cycles 1 and 2 waddr and wdata change but we is 0, so nothing is written and nothing is forwarded: the ports show the stored 44 and 88. In cycle 3 port A forwards 4f as it is written; in cycle 4, with we 0 again, it shows the stored 4f, not the 00 on wdata.
Constraints and contract
- Inputs change 1 ns after an edge and are known 0/1 values; addresses are below DEPTH.
- Keep the module name, the parameters and the ports.
Hints
Hint 1
The storage is an unpacked array of DEPTH words. Which block writes it, and what does that block do while rst_n is 0?
Hint 2
Each read port is one continuous assignment from its address. What must it show when the same word is being written in this cycle?
Hint 3
The new word is on wdata before the edge, while the array still holds the old one. A compare of waddr with the read address, taken only when we is 1, picks between them.
What the grader checks: Eight directed and eight seeded scripts run on copies with DEPTH 16 and 8 (WIDTH 8): up to eight writes, then five cycles that mix writes with reads on both ports, sampled before every edge and compared with an independent model of the brief. Other DEPTH and WIDTH values follow the same code but are not simulated. The synthesis check adds the latch count.
The submitted design must synthesize with 0 latches and no combinational loop. Counts come from the pinned open-source synthesis flow. They are proxies for logic size and depth, not physical area or timing.
16 checks on Icarus Verilog + Yosys · server. Run the examples first, then check the full regression. The suite is local and inspectable. A passing result establishes the tested cases, not a proof for all possible inputs.
See all 1 steps and the final capstone