Supreeth's picture
Upload folder using huggingface_hub
2b4ae64 verified

Task 6: Dual-Read / Single-Write Register File (Medium)

Objective

Implement the standard RISC-style register file: two independent asynchronous read ports and one synchronous write port, with register 0 hard-wired to zero. This is the core state element in every processor and AI-accelerator sequencer.

Interface

module register_file #(
    parameter ADDR_W = 5,    // 2^5 = 32 registers
    parameter DATA_W = 32
) (
    input  wire              clk,
    input  wire              we,           // write enable
    input  wire [ADDR_W-1:0] wr_addr,
    input  wire [DATA_W-1:0] wr_data,
    input  wire [ADDR_W-1:0] rd_addr_a,   // read port A
    input  wire [ADDR_W-1:0] rd_addr_b,   // read port B
    output wire [DATA_W-1:0] rd_data_a,
    output wire [DATA_W-1:0] rd_data_b
);

Specification

  • Synchronous write: on the rising edge of clk, if we is high, write wr_data to regs[wr_addr].
  • Asynchronous read: rd_data_a and rd_data_b are combinational outputs of regs[rd_addr_a] and regs[rd_addr_b].
  • Register 0 hardwired: reading address 0 always returns 0, regardless of any write. Writing to address 0 has no effect.
  • No reset port: registers may contain x on startup (testbench initialises via writes).
  • Both read ports are fully independent and may be read simultaneously.
  • Write-then-read hazard: the testbench reads one cycle after writing, so no forwarding path is required.

Scoring

  • Correct compilation: 10%
  • Passing simulation tests: 70%
  • Formal verification (SymbiYosys, if available): 10%
  • Area efficiency vs reference: 10%

Notes

  • Declare as reg [DATA_W-1:0] regs [0:(1<<ADDR_W)-1];
  • Guard every write: if (we && wr_addr != 0) regs[wr_addr] <= wr_data;
  • Guard every read: assign rd_data_a = (rd_addr_a == 0) ? 0 : regs[rd_addr_a];
  • Using initial blocks to clear registers is acceptable and synthesizable in modern flows.