Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
    • SRAM and ROM
    • Register Files
      • Multi-Port Register File Fundamentals
      • 2R1W Register File with Bypass
      • Read-During-Write Hazards and Assertions
    • Valid/Ready Handshake
    • AXI-Lite
    • AXI-Stream
    • Bus Integration
    • Synchronous FIFO
    • Register-Map and CSR RTL
    • Memory Reliability
    • Peripheral RTL Integration
    • Full AXI4: RTL Roadmap
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Register Files
  1. Topics
  2. RTL Design
  3. Memory & Buses
  4. Register Files
  5. 2R1W Register File with Bypass

Part 7 · Memory & Buses · Intermediate

2R1W Register File with Bypass

A 2R1W flop-array register file with write-through bypass, checked against a reference model.

On this page

    Why this matters

    Without a bypass, a decode-stage read in the same cycle as a write returns the stale word, and the instruction computes with old data. The fix is one 2-to-1 mux per read port, and its select has to include the write enable.

    After this lesson you can: build a 2R1W register file with write-through bypass and x0 hardwired to zero, and read a waveform that compares the plain and bypassed lanes.

    The module

    The file below implements the contract from the previous lesson. One flop array holds 32 words, two combinational muxes read it, and one clocked port writes it.

    This gives write-through semantics on address collisions, avoiding stale operand hazards in single-cycle execute paths. Register x0 is hardwired to zero: writes to it are dropped and reads of it return zero.

    regfile_2r1w.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    module regfile_2r1w #(
      parameter int DW = 32,
      parameter int AW = 5
    ) (
      input  logic          clk,
      input  logic          rst_n,
      input  logic          we_i,
      input  logic [AW-1:0] waddr_i,
      input  logic [DW-1:0] wdata_i,
      input  logic [AW-1:0] raddr0_i,
      input  logic [AW-1:0] raddr1_i,
      output logic [DW-1:0] rdata0_o,
      output logic [DW-1:0] rdata1_o
    );
      logic [DW-1:0] rf [2**AW];        // flop array: resettable, unlike an SRAM bank
      logic bypass0, bypass1;
    
      always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
          integer i;
          for (i = 0; i < 2**AW; i++) rf[i] <= '0;
        end else if (we_i && (waddr_i != '0)) begin
          rf[waddr_i] <= wdata_i;        // writes to x0 are dropped
        end
      end
    
      assign bypass0 = we_i && (waddr_i != '0) && (waddr_i == raddr0_i);
      assign bypass1 = we_i && (waddr_i != '0) && (waddr_i == raddr1_i);
    
      always_comb begin
        rdata0_o = (raddr0_i == '0) ? '0 : rf[raddr0_i];
        rdata1_o = (raddr1_i == '0) ? '0 : rf[raddr1_i];
        if (bypass0) rdata0_o = wdata_i;  // same-cycle write wins on port 0
        if (bypass1) rdata1_o = wdata_i;  // ... and on port 1
      end
    endmodule
    Read port 0 of regfile_2r1w: the x0 select picks zero or the stored word, then the bypass mux picks the write data when a same-cycle write targets the same nonzero register

    Follow the word from left to right. The first mux is the x0 rule from the always_comb block; the second is the bypass. Port 1 has the same pair with raddr1.

    • The select needs all three terms. Without the write enable, idle cycles forward stale write data. Without the nonzero check, a write to x0 leaks through.

    • The bypass is on the read timing path: the address comparator and one 2-to-1 mux follow the 32-to-1 array mux.

    The bypass keys off the same-cycle write enable. It covers only a read and write asserted in the same cycle; a writeback one stage later needs forwarding in the pipeline, covered in the next lesson.

    Same-cycle timing

    The top lanes are the stimulus. The bottom two lanes read the same address two ways, both combinational with the same latency: rdata_nb has no bypass, rdata_byp has the write-through bypass. Each slot is one clock cycle, and a write commits at the rising edge that ends its slot. So in a write slot the plain lane still shows the stored word, while the bypass lane already shows the write data.

    • Slot 0: reset held (rst_n 0), so both lanes read 0x0.

    • Slot 1: reset released with a write of 0xAA to register 3 (we 1, waddr 3). The bypass lane already shows 0xAA; the plain lane still shows 0x0.

    • Slot 2: idle read (we 0). Both lanes show 0xAA.

    • Slot 3: write of 0x11 to register 3 with a same-cycle read. The bypass lane shows the new 0x11; the plain lane still shows the old 0xAA.

    • Slot 4: idle read. Both lanes show 0x11.

    • Slot 5: read of register 0 (raddr0 0). Both lanes show 0x0: x0 is hardwired to zero.

    Common mistakes

    Common pitfall

    Bypass keyed on the write address alone, without the write enable, forwards stale write data on idle cycles. The mux select must include we_i, so it fires only while a write is actually asserted.

    Interview

    Likely question: where does the bypass mux sit, and what does it cost?

    • Position: one 2-to-1 mux per read port, after the array read mux, selecting between the stored word and the write data.

    • Select: write enabled, write address nonzero, write address equals read address.

    • Cost: a comparator plus a mux on each read port, in the read timing path.

    Check yourself The write enable is low and the write address equals read address 0. What does read port 0 return?

    Zero from the x0 hardwire (equal to rf[0] only because writes to x0 are dropped).

    Check yourself Both read ports read register 7 while a write to register 7 is enabled. What does each port return?

    Both return the write data. Each port has its own bypass comparator and mux, so both fire on the same match.

    Testbench

    The register file, a no-bypass sibling that exhibits the stale word, and a testbench that checks both against a reference model.

    regfile_2r1w.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    module regfile_2r1w #(
      parameter int DW = 32,
      parameter int AW = 5
    ) (
      input  logic          clk,
      input  logic          rst_n,
      input  logic          we_i,
      input  logic [AW-1:0] waddr_i,
      input  logic [DW-1:0] wdata_i,
      input  logic [AW-1:0] raddr0_i,
      input  logic [AW-1:0] raddr1_i,
      output logic [DW-1:0] rdata0_o,
      output logic [DW-1:0] rdata1_o
    );
      logic [DW-1:0] rf [2**AW];
      logic bypass0, bypass1;
      always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
          integer i;
          for (i = 0; i < 2**AW; i++) rf[i] <= '0;
        end else if (we_i && (waddr_i != '0)) begin
          rf[waddr_i] <= wdata_i;
        end
      end
      assign bypass0 = we_i && (waddr_i != '0) && (waddr_i == raddr0_i);
      assign bypass1 = we_i && (waddr_i != '0) && (waddr_i == raddr1_i);
      always_comb begin
        rdata0_o = (raddr0_i == '0) ? '0 : rf[raddr0_i];
        rdata1_o = (raddr1_i == '0) ? '0 : rf[raddr1_i];
        if (bypass0) rdata0_o = wdata_i;
        if (bypass1) rdata1_o = wdata_i;
      end
    endmodule
    
    module regfile_2r1w_nobypass #(
      parameter int DW = 32,
      parameter int AW = 5
    ) (
      input  logic          clk,
      input  logic          rst_n,
      input  logic          we_i,
      input  logic [AW-1:0] waddr_i,
      input  logic [DW-1:0] wdata_i,
      input  logic [AW-1:0] raddr0_i,
      output logic [DW-1:0] rdata0_o
    );
      logic [DW-1:0] rf [2**AW];
      always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
          integer i;
          for (i = 0; i < 2**AW; i++) rf[i] <= '0;
        end else if (we_i && (waddr_i != '0)) begin
          rf[waddr_i] <= wdata_i;
        end
      end
      always_comb rdata0_o = (raddr0_i == '0) ? '0 : rf[raddr0_i];
    endmodule
    

    Remember

    • Each read port selects the write data when write enable is high and the write address matches.

    • A bypass keyed on the address alone forwards stale write data on idle cycles.

    • The bypass covers only the same-cycle case. A writeback one stage later needs pipeline forwarding.

    Keep going

    • Practice: Register file with two read ports and one write port

    • Practice: 2R1W register file: read-first vs write-first semantics

    • Code lab: 2x8 register file

    • Interview: run a mock interview on this topic

    Related topics

    • Read-during-write hazards and assertions

    Previous

    196 / 350

    Next: Read-During-Write Hazards and Assertions

    Up next

    Up nextRead-During-Write Hazards and Assertions

    Sign in to save progress

    Practice this lessonReport an error
    clkrst_nwewaddr03wdata0aa11raddr030rdata_nb0aa110rdata_byp0aa110
    A write and a same-cycle read of register 3, with and without bypass