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 Foundations57
  • Combinational Design58
  • Sequential Design61
  • FSM & Control66
  • Timing & Clocking64
  • Reset & CDC58
  • Memory & Buses94
  • Advanced RTL60
    • Parameterization and Generate Hub
    • Top-Level Integration Hub
    • Synthesis Intent Hub
    • Style and Static Checks Hub
      • Style conventions that reduce review friction
      • Lint, RDC, and waiver policy
      • Waiver rules that scale
      • Key takeaways
      • Common pitfalls
      • Worked Examples and Waveforms
      • Corner Cases and Stress Tests
      • Integration and Sign-off Notes
    • Assertions in RTL Hub
    • Formal Verification Basics Hub
    • Reusable SystemVerilog for RTL
  • Senior & Interview175
  • Glossary and Notation
Loading lesson

‹Style and Static Checks Hub
  1. Topics
  2. RTL Design
  3. Advanced RTL
  4. Style and Static Checks Hub
  5. Lint, RDC, and waiver policy

Part 8 · Advanced · Advanced

Lint, RDC, and waiver policy

What lint and RDC each check, the essential rules, and fixing findings before waiving.

On this page

    Why this matters

    Two registers on the same clock but with independent resets are not safe just because the clock matches: asserting the source reset can change data near the live receiver's setup-hold aperture. Lint cannot see that; RDC can.

    After this lesson you can: separate what lint and RDC each check, fix a finding in the RTL first, and decide when an isolation scheme is needed for a reset crossing.

    Lint and RDC check different things

    Lint reads the RTL as written. It catches width, latch, unused-logic, and style findings without knowing the clocks.

    RDC reads the RTL plus clock and reset intent. It checks crossings and reset structure that lint cannot see.

    Check

    Needs

    Catches

    Lint

    Only the RTL

    Truncation, latches, unused or undriven signals, naming and style breaks

    RDC

    RTL plus clock/reset definitions and sequencing intent

    Data transitions caused by independent resets, reset interactions and unsafe release into live receivers

    Both are design feedback, not noise. A finding is either a bug to fix or an intent to document.

    Same clock, different reset domains

    Take two flops on the same clock, src_q and dst_q, with independent asynchronous resets. Same-clock timing covers changes that happen at a clock edge. An asserted source reset is not an edge: it changes src_q whenever it arrives, possibly just before the receiver samples. That is the case RDC looks for and lint cannot see.

    The trace below runs the page's reset_crossing block. raw_dst_q is an unprotected receiver; safe_dst_q is the same receiver with an isolation clamp, iso, asserted before the source reset.

    Each slot is one clock edge, numbered from 0: even slots are just after a rising edge (clk high), odd slots just after a falling edge (clk low).

    • Slots 1 and 2: iso asserts in the low phase, and the next rising edge loads the clamp, so safe_dst_q is 0 before anything is reset.

    • Slot 3: src_rst_n falls between edges and src_q drops at once. No clock edge caused it; in silicon this change can land inside the receiver's setup/hold window.

    • Slot 4: raw_dst_q captures the reset-induced 0. safe_dst_q never saw it.

    • Slots 5 to 8: reset releases in slot 5, a source edge restores src_q in slot 6, and only then does iso drop (slot 7), so safe_dst_q resumes with valid data in slot 8.

    What to do with an RDC finding

    1. Find the crossing: a flop whose reset can assert while a receiver on a different reset is still live.

    2. Choose the remedy from the architecture: coordinate the resets so the receiver is also in reset; or isolate the receiver before the source reset and hold the clamp until a source edge after release; or synchronise a single-bit level if latency and lost events are acceptable. Multi-bit data and events need a protocol, not per-bit synchronisers.

    3. Show the evidence: a trace like the one above for the chosen sequence, plus an RDC run with the reset intent described. A reset-release synchroniser alone is not RDC signoff, because it does nothing about the asynchronous assertion.

    Warning

    Limits: this trace checks digital sequencing only. Isolation-control timing, recovery and removal, reset-tree skew and metastability remain physical checks. The isolation controller itself must survive the source reset. This example carries a level with no pending transaction; for request and data crossings, drain or cancel accepted work and start a fresh recovery epoch before reopening. See the reset isolation contract and the RDC signoff checklist.

    The block and its self-checking bench:

    reset_crossing.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    module reset_crossing (
      input logic clk, src_rst_n, dst_rst_n, isolate,
      output logic src_q, raw_dst_q, safe_dst_q
    );
      always_ff @(posedge clk or negedge src_rst_n)
        if (!src_rst_n) src_q <= 1'b0;
        else src_q <= 1'b1;
      // raw_dst_q intentionally illustrates an unprotected live receiver.
      always_ff @(posedge clk or negedge dst_rst_n) begin
        if (!dst_rst_n) begin
          raw_dst_q <= 1'b0;
          safe_dst_q <= 1'b0;
        end else begin
          raw_dst_q <= src_q;
          safe_dst_q <= isolate ? 1'b0 : src_q;
        end
      end
    endmodule
    

    Essential rules

    Rule names differ between tools, so the names in the table are illustrative; check that your tool has each rule.

    Rule (illustrative)

    Catches

    Fix

    WIDTH-TRUNC

    An assignment that drops bits, such as a 9-bit sum assigned to 8 bits (carry loss)

    Size the expression and keep the carry

    LATCH-INF

    A combinational output with no value on some path

    Default assignments plus a full case

    UNUSED-SIG

    A signal that is written but never read

    Remove it, or connect the missing load

    ASYNC-RST

    An asynchronous reset in logic meant to be synchronous

    Synchronise, or waive with a rationale

    Real output for the two listings below, from Verilator with all warnings on, trimmed to the finding lines. The latch is reported twice, once as an incomplete case and once as a latch. The carry loss is not reported at all: 8-bit = 8-bit + 8-bit is sized to 8 bits, so it is not a width mismatch to this tool. Some commercial linters have a separate carry-loss rule; if yours does not, a review or a test has to catch it.

    Diagram
    $ verilator --lint-only -Wall width_bug.sv
    (no findings)
    
    $ verilator --lint-only -Wall latch_bug.sv
    %Warning-CASEINCOMPLETE: latch_bug.sv:7:5: Case values incompletely covered (example pattern 0x3)
    %Warning-LATCH: latch_bug.sv:6:3: Latch inferred for signal 'y' (not all control paths of combinational always assign a value)

    Fix first, then waive

    Most findings have a one-line RTL fix. The two listings below trip the first two rules; the Testbench section shows the fixed pair. The fixed adder there is shrunk to 4 bits, with a separate carry output, so the bench can sweep every input pair quickly.

    width_bug.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // bad-example: drops the carry (illustrative rule WIDTH-TRUNC)
    module width_bug (
      input  logic [7:0] a_i,
      input  logic [7:0] b_i,
      output logic [7:0] sum_o
    );
      assign sum_o = a_i + b_i;
    endmodule
    latch_bug.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad-example: missing select value infers a latch (illustrative rule LATCH-INF)
    module latch_bug (
      input  logic [1:0] sel,
      input  logic [7:0] a, b, c,
      output logic [7:0] y
    );
      always_comb begin
        case (sel)
          2'd0: y = a;
          2'd1: y = b;
          2'd2: y = c;
        endcase
      end
    endmodule

    Common pitfall

    Waiving a finding without fixing the cause hides the next instance too. Blanket waivers turn the rule off for new code as well as old.

    Interview

    Likely follow-up: is this finding a bug or an intent. Show the smallest RTL change that removes it, then argue why a waiver is still needed, if it is.

    Check yourself Lint flags a truncated sum and RDC flags an independently reset source driving a live receiver. What fixes each?

    Resize the sum to preserve carry. For RDC, identify whether reset can change source data asynchronously while the receiver is live, then choose justified sequencing, isolation or a protocol-appropriate synchronizer. A release synchronizer does not solve every reset-domain data crossing; a waiver needs evidence for the actual architecture.

    Check yourself The same lint finding appears in ten places. What does that pattern say?

    The cause is shared: a style habit, a macro, or a template. Fix the source once instead of touching ten sites.

    Testbench

    The fixed adder and mux, plus the testbench that checks all 256 input pairs and all four select values.

    width_safe_add.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
    module width_safe_add (
      input  logic [3:0] a_i,
      input  logic [3:0] b_i,
      output logic [3:0] sum_o,
      output logic       carry_o
    );
      logic [4:0] full;
      always_comb begin
        full = {1'b0, a_i} + {1'b0, b_i};
        sum_o = full[3:0];
        carry_o = full[4];
      end
    endmodule
    
    module latch_free_mux (
      input  logic [1:0] sel_i,
      input  logic [7:0] a_i, b_i, c_i, d_i,
      output logic [7:0] y_o
    );
      always_comb begin
        case (sel_i)
          2'd0:    y_o = a_i;
          2'd1:    y_o = b_i;
          2'd2:    y_o = c_i;
          default: y_o = d_i;
        endcase
      end
    endmodule
    

    Remember

    • Lint reads the RTL as written; RDC also reads clock and reset intent.

    • A source reset can change data under a live receiver. Coordinate resets, isolate before reset, or synchronise a single-bit level.

    • Fix first, then waive. A waiver without the fix hides the next instance.

    Rule of thumb: a reset-release synchroniser alone is not RDC signoff.

    How this was checked

    Isolation trace run with Icarus Verilog 13.0 (2026-10-16): prints PASS; the waveform is generated from the same block by Icarus Verilog 13.0. It checks digital sequencing only. Isolation-control timing, recovery and removal, reset-tree skew and metastability are not modelled.

    Lint output from Verilator 5.048 (2026-10-17), --lint-only -Wall, trimmed to the finding lines: no finding for width_bug, CASEINCOMPLETE and LATCH for latch_bug. No RDC tool was run.

    Fixed adder and mux testbench run with Icarus Verilog 13.0 (2026-10-16): prints PASS; it checks all 256 input pairs and all four select values. Rule names in the table are illustrative; check which rules your tool has.

    Keep going

    • Practice: Debugging metastability from asynchronous reset deassertion

    • Interview: run a mock interview on this topic

    Related topics

    • Style conventions that reduce review friction

    • Waiver rules that scale

    • Latch avoidance and case semantics

    Previous

    486 / 694

    Next: Waiver rules that scale

    Up next

    Up nextWaiver rules that scale

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Key takeaways
    • Common pitfalls
    • Worked Examples and Waveforms
    clksrc_rst_nisosrc_qraw_dst_qsafe_dst_q
    Source reset asserting between clock edges: the raw receiver captures the reset-induced 0, the isolated receiver stays clamped until a source edge restores valid data