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
    • Digital Logic for RTL Engineers
    • Number Systems for RTL Engineers
    • Verilog Fundamentals
    • Verilog Operators and Types
    • SystemVerilog for RTL
    • Simulation and Testbenches
      • Minimal but Robust Testbench Structure
      • Clock and Reset Best Practices
      • Self-Checking and Observability
      • Reference Model Plus Checks
      • Useful Runtime Instrumentation
      • Verification Quality Gates
      • First Project: a 4-bit ALU
      • Interview-Ready Simulation Patterns
      • Simulation TB Summary
      • Worked Examples and Waveforms
  • Combinational Design58
  • Sequential Design61
  • FSM & Control66
  • Timing & Clocking64
  • Reset & CDC58
  • Memory & Buses94
  • Advanced RTL60
  • Senior & Interview175
  • Glossary and Notation
Loading lesson

‹Simulation and Testbenches
  1. Topics
  2. RTL Design
  3. RTL Foundations
  4. Simulation and Testbenches
  5. Clock and Reset Best Practices

Part 1 · Foundations · Beginner

Clock and Reset Best Practices

One clock source, race-free input timing, and reset asserted, held and released safely.

On this page

    Why this matters

    If clk starts as x then clk = ~clk stays x forever, and the simulation looks dead. Driving an input exactly at the clock edge is a race whose result depends on the simulator.

    After this lesson you can: generate a clock from one period definition, drive inputs away from the active edge, and apply and release reset with a real edge.

    Clock generation

    One generator drives the whole testbench. The period lives in a single definition.

    design.v
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // snippet: two common ways to generate a clock
    timeunit 1ns; timeprecision 1ps;      // first items in the testbench module
    
    // Keep the period in ONE place (a parameter or localparam),
    // declared before use, and derive any related delays from it.
    localparam time PERIOD = 10ns;
    
    always #5 clk = ~clk;                 // simplest; needs 'clk' initialized to 0
    
    initial begin                         // explicit form, easy to change
      clk = 0;
      forever #(PERIOD/2) clk = ~clk;
    end

    Tip

    Initialize the clock to a known value. A logic clk; that starts as x makes clk = ~clk stay x forever, and your simulation will appear dead.

    Check yourself Why is always #5 clk = ~clk; without an initial value a bug?

    clk starts as x, and ~x is x, so the clock never toggles. Initialize it (logic clk = 0;) before the always block runs.

    When to drive inputs

    Changing an input at the clock edge creates a race. Whether the DUT sees the old or the new value then depends on simulator ordering.

    That ordering can differ between simulators, so drive inputs away from the active edge.

    Strategy

    How

    Notes

    Drive after a small delay

    @(posedge clk); #1 en = 1;

    Simple and clear; matches the sampling rule from the last lesson.

    Drive on the opposite edge

    @(negedge clk) en = 1;

    Inputs are stable half a period before the active edge.

    Non-blocking in the testbench

    @(posedge clk) en <= 1;

    Update lands after the DUT samples, like a real flop.

    Clocking block (SystemVerilog)

    clocking cb @(posedge clk); output en; endclocking

    The standard solution in larger environments; sampling skew is built in.

    Each slot is half a clock period, sampled just after a clock edge: clk high means the slot follows a rising edge, low means it follows a falling edge.

    • Slot 1: the bench changes d to A5 at the falling edge. q still holds 00, because nothing samples d on a falling edge.

    • Slot 2: the rising edge captures A5. d has been stable for half a period, so there is no race about which value the flop sees.

    • Slots 3 and 4: the same pattern for 3C: d changes in slot 3, q follows one slot later in slot 4.

    Reset: assert, hold, release

    • Assert reset at the start, and hold it for several clocks so every flop (including slow deep ones) sees it.

    • Create a real 1 → 0 transition for an asynchronous active-low reset. A declaration initialiser such as logic rst_n = 0 holds 0 from time 0, so no falling edge occurs. A first procedural assignment of 0 to a signal that is still x is an x-to-0 transition and does count as a falling edge, but it hides the intent. Start from a known 1 (logic rst_n = 1) and drive to 0 after #1 so the edge is explicit and visible in the waveform.

    • Release reset away from the clock edge, as you would in hardware: after a #1 or on the opposite edge.

    • Do not apply stimulus while reset is asserted unless the test is about reset itself.

    • Test reset in the middle of operation too, not only at power-up.

    Common pitfall

    Releasing reset exactly on the active clock edge can violate the flop's recovery time in real hardware, and in simulation it creates the same race as any other input. In RTL, synchronize the release (see the Reset and CDC section) and in the testbench release it between edges.

    Testbench

    Inputs driven on the opposite edge are captured without races.

    reg8.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
    module reg8 (input logic clk, input logic [7:0] d, output logic [7:0] q);
      always_ff @(posedge clk) q <= d;
    endmodule
    
    module tb_drive_timing;
      timeunit 1ns; timeprecision 1ps;
      logic clk = 0;
      logic [7:0] d, q;
      int errors = 0;
      reg8 dut (.clk, .d, .q);
      always #5 clk = ~clk;
    
      initial begin
        d = 8'h00;
        @(negedge clk) d = 8'hA5;                 // driven half a cycle before the edge
        @(posedge clk); #1;
        if (q !== 8'hA5) errors++;                // captured exactly once, no race
    
        @(negedge clk) d = 8'h3C;
        @(posedge clk); #1;
        if (q !== 8'h3C) errors++;
    
        if (errors != 0) $fatal(1, "FAIL: opposite-edge drive errors=%0d", errors);
        $display("PASS: inputs driven on the opposite edge are captured cleanly");
        $finish;
      end
    endmodule

    Remember

    • Initialise the clock to a known value, and keep the period in one place.

    • Drive inputs on the opposite edge or a short delay after the active edge, never on it.

    • Hold reset for several clocks, start from a known 1 so the falling edge is real, and release away from the clock edge.

    Rule of thumb: never change an input at the instant the DUT samples it.

    Keep going

    • Interview: run a mock interview on this topic

    Related topics

    • Reset strategies in RTL

    Previous

    49 / 694

    Next: Self-Checking and Observability

    Up next

    Up nextSelf-Checking and Observability

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Reference Model Plus Checks
    • Useful Runtime Instrumentation
    • Verification Quality Gates
    clkd0a53cq0a53c
    reg8 from the testbench below, with d driven on the falling edge: d is stable for half a period before each rising edge, and q updates once per rising edge