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
    • STA Foundations
    • Setup, Hold and Slack
    • Skew and Variation
    • Timing Exceptions
    • Clock Gating
    • RTL for Timing
      • Practice
      • Fanout
      • Patterns
      • RTL Hooks
      • Checklist
      • Cutting Logic Depth: a Timing-Closure Story
      • Mini Project: Close Timing on a FIR Filter
    • Clock Architecture
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹RTL for Timing
  1. Topics
  2. RTL Design
  3. Timing & Clocking
  4. RTL for Timing
  5. Patterns

Part 5 · Timing & Clocking · Intermediate

Patterns

Measured shallow structures for selects and priorities, with the assumptions each relies on.

On this page

    Why this matters

    An 8-way priority select written as an if-else chain is 10 levels and 240 cells. The parallel AND-OR form is 4 levels and 201 cells, but it gives wrong data whenever two requests are active.

    After this lesson you can: pick a shallower structure for a mux or priority select, say what assumption it relies on, and guard that assumption with an assertion.

    Same function, different depth

    Many circuits can be written in more than one way. The versions differ in depth, area and the assumption they rely on.

    Structure

    Levels

    Cells (incl. flops)

    Assumption

    16:1 mux, binary select (d[s])

    6

    43

    None

    16:1 mux, one-hot select (|(d & s))

    5

    65

    Exactly one select bit set; needs 16 select flops

    8-way priority select (if-else chain, 8-bit data)

    10

    240

    None (lowest index wins)

    8-way select, parallel AND-OR

    4

    201

    At most one request active

    A 4-input priority select written as an if-else chain and as a parallel AND-OR. Each gate stands for one 8-bit slice.

    Read each panel from the inputs on the left to y on the right; the highlighted path is the longest one.

    • The chain puts req0 at the last mux, so the lowest index wins. Every extra input adds one mux to the path.

    • The AND-OR has the same depth for any input once its OR tree is built: one AND, then about log2 N OR levels.

    • With two requests high, the chain still returns d0, but the AND-OR returns the OR of both words.

    The one-hot mux is only slightly shallower and bigger: the tool already builds a good tree for d[s].

    The priority example is the strong one: the chain is 10 levels and 240 cells, while the parallel form is 4 levels and 201 cells, but it can change the result when several requests are active.

    Shallow patterns

    Six replacements trade assumptions or latency for depth. Each row names what makes it shallower and what to watch.

    Pattern

    Why it is shallower

    Watch for

    One-hot or AND-OR selects

    Parallel instead of chained decisions

    The one-hot guarantee must hold; add an assertion

    Balanced operator trees

    log N depth instead of N

    Tools often balance plain sums, but not when mixed with enables or width changes

    Late signal to the last mux

    The late path sees one gate instead of several

    Write it so; synthesis may keep that order

    Registered control (decode in the previous cycle)

    Moves decode off the data path

    One cycle of extra latency

    Parallel prefix adders / comparators

    log N carry/compare depth

    More area (see the Kogge-Stone example)

    Replace a < b chains with a registered compare

    Cuts a 20-level path

    Pipeline stage added

    Common mistakes

    Common pitfall

    Do not apply a shallow pattern without its assumption. A priority chain written as AND-OR that is occasionally fed two requests gives wrong data only in rare cycles.

    Add an assertion that the one-hot guarantee holds ($onehot0(req)), and keep it in simulation.

    Interview

    Strong answer to “How would you speed up a priority encoder?”: Notice whether requests can overlap. If not, one-hot AND-OR.

    If yes, a tree of 2:1 priority combiners (log depth). Mention the area trade-off and the assertion.

    Check yourself The AND-OR form is smaller and shallower in the table. Why not use it everywhere?

    It computes the OR of selected data rather than the priority winner. onehot0 guarantees equivalence for arbitrary data; overlapping requests can agree for particular data but that observation does not establish an interface guarantee.

    Testbench

    The chain and parallel forms plus a testbench that compares them on random requests.

    prio_chain.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // Priority select as an if-else chain (lowest index wins) versus a parallel AND-OR.
    module prio_chain (input logic [7:0] req, input logic [7:0] [7:0] data, output logic [7:0] y);
      always_comb begin
        y = '0;
        for (int i = 7; i >= 0; i--) if (req[i]) y = data[i];     // later iterations (lower index) override
      end
    endmodule
    
    // Valid only when at most one request is active (guaranteed by the caller).
    module prio_onehot (input logic [7:0] req, input logic [7:0] [7:0] data, output logic [7:0] y);
      always_comb begin
        y = '0;
        for (int i = 0; i < 8; i++) y |= data[i] & {8{req[i]}};
      end
    endmodule
    

    Equivalence is guaranteed under known onehot0 requests, not if and only if onehot0 holds. Multiple requests can agree when the extra data contributes no new bits (including identical or zero data). The directed source checks all 256 request masks with distinct-bit data, then equal-data agreement and 01/02 disagreement, followed by the existing random experiment. In the random run, 18845 of 19284 multi-request cases gave different results. Keep $onehot0(req) asserted at the real caller boundary and reject unknown requests; the experiment deliberately includes illegal overlaps.

    Remember

    • A one-hot 16:1 mux is only slightly shallower than d[s], because the tool already builds a good tree.

    • AND-OR selects need at most one request. Registered control and prefix structures trade latency or area for depth.

    • A shallow pattern is valid only with its assumption. Keep $onehot0(req) asserted at the caller boundary.

    Rule of thumb: never apply a shallow pattern without writing down and checking the assumption it relies on.

    How this was checked

    Directed bench, run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It checks all 256 request masks with distinct-bit data, then equal-data agreement and 01/02 disagreement. In the random run, 18845 of 19284 multi-request cases differed; that count depends on the run.

    The levels and cell counts in the first table are measured mapping results.

    Keep going

    • Practice: Pipelined arbiter for timing closure

    • Interview: run a mock interview on this topic

    Related topics

    • RTL hooks

    • Cutting logic depth: a timing-closure story

    Previous

    143 / 350

    Next: RTL Hooks

    Up next

    Up nextRTL Hooks

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Practice
    • Checklist
    • Cutting Logic Depth: a Timing-Closure Story