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
    • Valid/Ready Handshake
    • AXI-Lite
    • AXI-Stream
    • Bus Integration
      • Address Decode
      • Mux Trees and Read Paths
      • Error Paths and Timeouts
      • Arbitration
      • Integration Checklist
      • An End-to-End Bus Example
    • 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

‹Bus Integration
  1. Topics
  2. RTL Design
  3. Memory & Buses
  4. Bus Integration
  5. Mux Trees and Read Paths

Part 7 · Memory & Buses · Intermediate

Mux Trees and Read Paths

A qualified AND-OR read mux with registered output, byte lanes and endianness, checked against a reference.

On this page

    Why this matters

    OR the slave read buses without qualifying each one and the words merge into garbage. A register after the mux fixes timing, but it loses which master owns the reply unless the whole response is captured.

    After this lesson you can: build an AND-OR read mux qualified by a one-hot select, register it, and keep data, status and owner together under a stall.

    A mux qualified by the select

    Many slaves share one master read path, so their read data buses meet in a mux. Each slave word is ANDed with its select replicated across the width, and the terms are ORed together.

    The qualification is the point. Only the selected slave drives the result, so two slaves can never fight on the shared bus.

    bus_rmux: rdata0, rdata1 and rdata_d each pass an AND gate with their select, the three results are ORed, and a register on clk drives rdata

    Read it left to right: gate, combine, register. Each AND gate is 32 gates wide, one per bit, all driven by the same select bit.

    • One select high: the other two AND gates output 0, so the OR passes the selected word unchanged.

    • No select high: every AND outputs 0 and rdata parks at zero. That is not an error response; the default slave supplies one.

    • Two selects high: the OR merges two words into garbage, which is why the selects must be one-hot.

    Registered output

    Register the mux output when many slaves fan into one master read path. The register breaks a long combinational path that grows with every added slave.

    bus_rmux is an unconditional datapath fragment, not a response channel: it has no valid, owner, error status or stalled-response storage. Adding that register alone does not preserve transaction association. Capture the complete {data,status,owner} tuple only when its response slot is free or consumed; hold the tuple and valid while the receiver stalls. The response slice in the next section supplies this contract.

    Response ownership under a stall

    A response is a tuple: data, status and the owner it belongs to. Under a stall the whole tuple must wait together, and a new tuple may enter only on the edge where the old one leaves.

    Each slot is one clock cycle and ends with the edge of the same number in the testbench. A tuple moves in at the end of a slot where s_valid and s_ready are high, and out where m_valid and m_ready are high. m_status 2 is SLVERR (2'b10).

    • Slot 0: the slot is empty, so A (owner 0, OKAY, 0x11223344) is accepted at edge 0.

    • Slots 1 to 3: the receiver stalls. s_ready is low, A's owner and status stay unchanged, and B's source must hold its offer.

    • Slot 4: m_ready rises. Edge 4 consumes A and accepts B together, so m_owner and m_status switch to B's 1 and SLVERR in slot 5.

    • Slot 5: B is consumed at edge 5 and the slot is empty in slot 6.

    The standalone slice takes an already-selected response tuple. It neither arbitrates targets nor tracks outstanding requests; those are upstream responsibilities. Its reverse ready path is combinational (the pass-through stage in Skid Buffer and Pipeline Stages); use that page's skid buffer when the ready path must be registered. The listing below is the slice with its directed bench.

    bus_rsp_slice.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    module bus_rsp_slice (
      input logic clk, rst_n, s_valid, m_ready,
      output logic s_ready, m_valid,
      input logic [31:0] s_data,
      input logic [1:0] s_status, s_owner,
      output logic [31:0] m_data,
      output logic [1:0] m_status, m_owner
    );
      assign s_ready = !m_valid || m_ready;
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) begin m_valid<=0; m_data<=0; m_status<=0; m_owner<=0; end
        else if (s_ready) begin
          m_valid <= s_valid;
          if (s_valid) begin m_data<=s_data; m_status<=s_status; m_owner<=s_owner; end
        end
    endmodule
    

    Byte lanes and endianness

    Byte lanes need care at the same boundary. The mux must preserve lane order, and both sides must agree on endianness.

    Bytes 11, 22, 33, 44 at addresses A to A+3 travel on lanes 0 to 3; the same lanes read as 0x44332211 little-endian or 0x11223344 big-endian

    Read the crossing lines as the bus wiring: address A always rides lane 0, whatever the CPU's endianness. Only the interpretation of the four lanes as one number changes.

    • Little-endian: lane 3 is the most significant byte, so the word is 0x44332211.

    • Big-endian: lane 0 is the most significant byte, so the same lanes read 0x11223344.

    • Byte access: a byte read of address A returns 0x11 under both, which is what byte-invariant means.

    Case

    Lane 3

    Lane 2

    Lane 1

    Lane 0

    Little-endian word 0x44332211

    0x44

    0x33

    0x22

    0x11

    Byte write to lane 0

    kept

    kept

    kept

    new byte

    Byte addresses (byte-invariant bus)

    A+3 holds 0x44

    A+2 holds 0x33

    A+1 holds 0x22

    A holds 0x11

    CPU load/store conversion or a bridge may already normalise the peripheral's word convention; do not infer a lane swap from CPU endianness alone. Check byte addresses, strobes, bus lanes and software-visible word interpretation independently.

    Check yourself Does a big-endian CPU always need a lane swap before this peripheral?

    No. Determine what its bus interface and bridge already do and what the peripheral defines. Convert only when those contracts disagree; another swap may double-convert words or corrupt byte writes. Test bytes at A..A+3 as well as a fixed word.

    Common mistakes

    Common pitfall

    ORing the slave buses without qualifying each with its select merges all the words into garbage. Every term of the OR needs its select, and the selects must be one-hot.

    Check yourself The selects glitch so that two are high for a moment. What does the mux output?

    The bitwise OR of two slave words, which is garbage. The one-hot select invariant belongs in an assertion, not just in the testbench.

    Testbench

    The registered mux plus a testbench that drives six fixed patterns to each select in turn, with the unselected slaves carrying inverted garbage, and checks the registered result one cycle later.

    bus_rmux.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // bus_rmux: AND-OR read-data mux qualified by one-hot selects, with registered output.
    module bus_rmux #(
      parameter int W = 32
    )(
      input  logic [W-1:0] rdata0, rdata1, rdata_d,
      input  logic         sel0, sel1, sel_d,
      input  logic         clk, rst_n,
      output logic [W-1:0] rdata
    );
      logic [W-1:0] mux_comb;
      assign mux_comb = ({W{sel0}} & rdata0) | ({W{sel1}} & rdata1) | ({W{sel_d}} & rdata_d);
      always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) rdata <= '0;
        else        rdata <= mux_comb;
      end
    endmodule
    

    Remember

    • AND each slave word with its select replicated across the width, then OR the terms. The one-hot select belongs in an assertion.

    • Capture the {data, status, owner} tuple only when the response slot is free, and hold it while the receiver stalls.

    • Byte lanes must keep their order, and both sides must agree on endianness. Do not infer a lane swap from CPU endianness alone.

    Keep going

    • Interview: run a mock interview on this topic

    Related topics

    • Address Decode

    • Error Paths and Timeouts

    Previous

    211 / 350

    Next: Error Paths and Timeouts

    Up next

    Up nextError Paths and Timeouts

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Arbitration
    • Integration Checklist
    • An End-to-End Bus Example
    s_valids_readym_validm_readym_owner01m_status02
    bus_rsp_slice holding response A (owner 0, OKAY) through a three-cycle receiver stall, then consuming A and accepting B (owner 1, SLVERR) on the same edge