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
    • Controller and Datapath
    • Moore vs Mealy
    • State Encoding
    • Multi-Cycle Sequencers
    • UART Transmitter
    • Memory Controller
      • Objective and Interface
      • State Machine and Bus Behavior
      • Request Latching
      • Design Closure Checklist
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Memory Controller
  1. Topics
  2. RTL Design
  3. FSM & Control
  4. Memory Controller
  5. Request Latching

Part 4 · FSM & Control · Intermediate

Request Latching

Request copying at the accept edge and the scoreboard result of five deliberate bugs.

On this page

    Why this matters

    The requester only holds its request until the handshake. If the controller drives the RAM from the live inputs, the next request's address and data go to the wrong access, and a read can run as a write.

    After this lesson you can: latch the request fields at the accept edge, drive the RAM from the copies, and write an adversarial driver that exposes a missing latch.

    The rule

    The requester only holds its request until the handshake. After the accept it may present the next request or leave garbage on the bus.

    The controller copies req_we, req_addr and req_wdata into we_r, addr_r and wdata_r at the accept edge. It drives the RAM only from those copies.

    Diagram
    IDLE:   if (req_valid) begin
              we_r <= req_we;  addr_r <= req_addr;  wdata_r <= req_wdata;   // copy at accept
              state <= ACCESS;
            end
    ...
    assign mem_addr  = addr_r;       // never req_addr
    assign mem_wdata = wdata_r;      // never req_wdata

    What breaks without it

    Each change below breaks the controller in one place. The table shows what a value-only scoreboard saw (AW=6, DW=8, LAT=2).

    Change

    What the scoreboard saw

    mem_addr = req_addr (use the live address, not the copy)

    Fails: a read returned 0x00 where 0x9e was expected. The RAM was addressed with the next request's address.

    mem_wdata = req_wdata

    Fails: a read returned 0x63 where 0x9e was expected. A write stored the next request's data.

    mem_we uses req_we instead of we_r

    Fails: a read returned 0x31 where 0x00 was expected. A read accepted as a read was executed as a write, or the reverse.

    Read goes straight to RESP, skipping WAIT (data taken one cycle early)

    Fails: data from the previous access was returned.

    WAIT exit uses wait_cnt == 2 instead of == 1 (LAT=2 only)

    A value-only check passes after a longer wait: data stays stable. The exact-edge check must fail. For LAT >= 3, ==2 can instead shorten the wait; this is not a universal slow mutation.

    A correct but slower design can be invisible to a value-only scoreboard. The bench therefore also requires response consumption exactly LAT + 1 edges after acceptance, independently of data matching.

    Warning

    The first three failures happen only because the driver presents a new, different request right after the handshake. A driver that holds the old request until the controller is idle again would hide all three bugs. Make your driver adversarial: change the inputs the moment it is legal to.

    Cost of latching

    Item

    Cost

    we_r + addr_r + wdata_r

    1 + AW + DW flip-flops (15 for this 6-bit address, 8-bit data controller)

    Benefit

    Requester can pipeline its next request; outputs to the RAM come straight from registers (clean timing)

    Different interface alternative

    Require fields to remain stable through read-response consumption or through write commit (explicit write_done, or a documented return of ready after ACCESS). This is incompatible with the current accept-edge release contract and creates a longer requester-to-RAM timing path. Waiting for rsp_valid on a write would hang.

    Check yourself In which clock edge are the request fields copied, and why that one?

    At the edge where req_valid && req_ready is true (IDLE with a valid request). That is the last moment the requester is obliged to hold them, so copying earlier is impossible and copying later is unsafe.

    Remember

    • Copy req_we, req_addr and req_wdata into we_r, addr_r and wdata_r at the accept edge, and use only the copies.

    • A driver that holds the old request until the controller is idle hides all three latch bugs. Change the inputs as soon as that is legal.

    • A correct but slower design can pass a value-only scoreboard, so also check the response edge.

    Rule of thumb: the accept edge is the last moment the requester has to hold its request.

    How this was checked

    The mutation results in the table are value-only observations for AW=6, DW=8, LAT=2 from an earlier version of the bench. They have not been rerun against the current bench.

    The current bench, which requires response consumption exactly LAT + 1 edges after acceptance, passes with Icarus Verilog 13.0 (2026-10-16).

    Keep going

    • Interview: run a mock interview on this topic

    Related topics

    • Design closure checklist

    Previous

    114 / 350

    Next: Design Closure Checklist

    Up next

    Up nextDesign Closure Checklist

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Objective and Interface