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

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
    • Sequence Items and the Driver Handshake
    • Sequence Mechanics: Lifecycle, Sequencer, Responses and Control
      • Sequence Lifecycle and Hooks: What start() Does, Step by Step
      • Inside the Sequencer: Arbitration, Priority and Relevance
      • Responses and Pipelined Drivers
      • Stopping Sequences and Reset
      • Sequences and Objections: Who Keeps the Phase Open
      • The uvm_do Macro Family: What It Expands To
    • Virtual Sequences and Coordination
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Sequence Mechanics: Lifecycle, Sequencer, Responses and Control
  1. Topics
  2. UVM Verification
  3. UVM Sequences & Stimulus
  4. Sequence Mechanics: Lifecycle, Sequencer, Responses and Control
  5. Stopping Sequences and Reset

Part 5 · Sequences · Intermediate

Stopping Sequences and Reset

Three ways to stop stimulus and what each leaves behind: kill(), stop_sequences() and disable fork. A reset-aware driver that abandons the current item and still calls item_done, and restarting stimulus after reset.

On this page

    A DMA test asserts reset in the middle of a job to check that the design recovers. The first attempt calls stop_sequences() on the stream sequencer and asserts reset. The simulation dies at once with a fatal from the sequencer. The second attempt drops stop_sequences() altogether, and after reset the old packet sequence carries on where it left off, sending the rest of a packet the DMA no longer expects.

    Stopping stimulus cleanly takes three cooperating pieces: the right call to stop the sequence, a driver that lets go of its item properly, and a test that restarts traffic once reset is over. This lesson shows each, with the fatal and the clean version side by side.

    The idea: cancel the order, and tell the cook

    When a restaurant closes mid-service, two things must happen. The waiters cancel the open orders, and each cook who is holding a slip puts it back on the pass as finished, even though no plate goes out. If the waiters cancel and the cook keeps working, food comes out for nobody. If the cook drops the slip in the bin without telling anyone, the waiter waits forever. And if the manager throws away every slip on the pass while a cook is still holding one, the cook comes back to a pass that has no record of their order.

    That last case is precisely the fatal from the opening. UVM gives you three ways to cancel, and they differ in what they clean up:

    The picture: what each stop leaves behind

    seq.kill()

    sqr.stop_sequences()

    disable fork

    Stops

    This sequence and its children

    Every sequence on this sequencer

    Every child process of the calling thread

    Removes its waiting requests and locks

    Yes

    Yes

    No (reported later as zombie requests or locks)

    The item the driver already holds

    Left in place: the driver must call item_done

    Flushed: the driver must not call item_done

    Left in place

    Sequence state afterwards

    UVM_STOPPED

    UVM_STOPPED

    Unchanged, often stuck at UVM_BODY

    Automatic objection

    Dropped

    Not dropped: the phase stays open

    Not dropped

    Build it

    A driver that survives reset

    The driver is the piece every scheme needs. It races each transfer against the reset edge, abandons the transfer if reset wins, and always calls item_done for the item it took. The inner fork ... join_any; disable fork; is wrapped in its own fork begin ... end join, so the disable fork kills only the two racing threads and not other children of the driver (why the guard matters):

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // snippet: seq_lab_tb.sv, a reset-aware driver
    task run_phase(uvm_phase phase);
      forever begin
        wait (vif.rst_n === 1'b1);             // no new item while in reset
        seq_item_port.get_next_item(req);
        `uvm_info("DRV", {"got  ", req.get_name()}, UVM_LOW)
        fork
          begin                                // guard: disable fork below stays in here
            fork
              repeat (4) @(posedge vif.clk);   // stands in for driving the transfer
              @(negedge vif.rst_n);
            join_any
            disable fork;
          end
        join
        if (vif.rst_n !== 1'b1)
          `uvm_info("DRV", {"reset: abandoning ", req.get_name()}, UVM_LOW)
        seq_item_port.item_done();             // exactly once, reset or not
      end
    endtask

    A real driver also puts the pins into their reset values in the abandon path. The monitor and scoreboard need their own reset handling (flushing half-seen transfers and expected queues); Agent architecture covers those.

    The test: kill, wait out reset, start fresh

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // snippet: seq_lab_tb.sv
    task run_phase(uvm_phase phase);
      lab_burst_seq s1 = lab_burst_seq::type_id::create("s1");
      lab_burst_seq s2 = lab_burst_seq::type_id::create("s2");
      phase.raise_objection(this);
      s1.n = 20;
      fork s1.start(env.sqr); join_none
      #95ns;
      vif.rst_n = 1'b0;                        // reset in the middle of item s1_1
      s1.kill();
      `uvm_info("TEST", $sformatf("reset: s1 state=%s", s1.get_sequence_state().name()), UVM_LOW)
      #30ns;
      vif.rst_n = 1'b1;
      s2.n = 2;                                // a new sequence object after reset
      s2.start(env.sqr);
      `uvm_info("TEST", "s2 finished after reset", UVM_LOW)
      phase.drop_objection(this);
    endtask

    kill() removes the sequence's waiting requests and locks from the sequencer, calls the sequence's do_kill() hook (override it to release anything the sequence holds, such as a semaphore), kills its children and its body thread, and sets the state to UVM_STOPPED. It does not remove the item the driver is holding, which is why the driver's item_done is still correct, and still needed: until it is called, that stale item stays at the head of the sequencer's request queue. Starting a new sequence object after reset avoids any question about the old one's state.

    stop_sequences(): everything, including the item in hand

    stop_sequences() kills every sequence registered with the sequencer and then flushes the request FIFO and clears the driver-side bookkeeping. It is not graceful. Its use is a sequencer-wide reset: when the driver itself is told about the reset first and has already called item_done, or when the driver does not call item_done for an item that was flushed under it. Calling stop_sequences() and then letting the driver call item_done is the fatal from the opening.

    There is a second trap. stop_sequences() kills each sequence through the sequencer's internal kill, which skips the objection drop that the public kill() performs. A sequence that held an automatic phase objection is stopped, but its objection stays raised, and the phase can no longer end. If a sequence objects, stop it with kill().

    disable fork: the blunt tool

    Killing a sequence's thread with disable fork skips all of kill()'s bookkeeping. The sequence never reaches UVM_STOPPED; a request it left in the arbitration queue is found and removed later with a SEQREQZMB error, and a lock it held with SEQLCKZMB. If you must time-box a sequence, use the guarded idiom and then call kill() on it, so the sequencer is told.

    Watch it run: kill, then the fatal

    The lab test above, with the reset-aware driver. Reset lands at 95 ns, while the driver holds s1_1:

    Diagram
    UVM_INFO seq_lab_tb.sv(111) @ 22000: uvm_test_top.env.drv [DRV] got  s1_0
    UVM_INFO seq_lab_tb.sv(111) @ 55000: uvm_test_top.env.drv [DRV] got  s1_1
    UVM_INFO seq_lab_tb.sv(437) @ 95000: uvm_test_top [TEST] reset: s1 state=UVM_STOPPED
    UVM_INFO seq_lab_tb.sv(122) @ 95000: uvm_test_top.env.drv [DRV] reset: abandoning s1_1
    UVM_INFO seq_lab_tb.sv(111) @ 125000: uvm_test_top.env.drv [DRV] got  s2_0
    UVM_INFO seq_lab_tb.sv(111) @ 165000: uvm_test_top.env.drv [DRV] got  s2_1
    UVM_INFO seq_lab_tb.sv(442) @ 205000: uvm_test_top [TEST] s2 finished after reset
    UVM_ERROR :    0
    UVM_FATAL :    0

    Now the same driver with env.sqr.stop_sequences() called just before reset instead of s1.kill():

    Diagram
    UVM_INFO seq_lab_tb.sv(111) @ 22000: uvm_test_top.env.drv [DRV] got  s1_0
    UVM_INFO seq_lab_tb.sv(111) @ 55000: uvm_test_top.env.drv [DRV] got  s1_1
    UVM_INFO @ 95000: uvm_test_top.env.sqr [uvm_test_top.env.sqr] Sequences stopped.  Removing request from sequencer fifo
    UVM_INFO seq_lab_tb.sv(122) @ 95000: uvm_test_top.env.drv [DRV] reset: abandoning s1_1
    UVM_FATAL @ 95000: uvm_test_top.env.sqr [SQRBADITMDN] Item_done() called with no outstanding requests. Each call to item_done() must be paired with a previous call to get_next_item().
    UVM_FATAL :    1

    And the objection trap: a sequence with set_automatic_phase_objection(1) is the only thing holding the run phase, with a 1 us timeout. Stopping it with stop_sequences() at 60 ns, and then with kill() in a second run:

    Diagram
    UVM_INFO seq_lab_tb.sv(601) @ 60000: uvm_test_top [TEST] stop_sequences() at 60ns: g state=UVM_STOPPED
    UVM_FATAL uvm_phase_hopper.svh(598) @ 1000000: reporter [PH_TIMEOUT] Explicit timeout of 1000000 hit, indicating a probable testbench issue
    
    UVM_INFO seq_lab_tb.sv(601) @ 60000: uvm_test_top [TEST]           kill() at 60ns: g state=UVM_STOPPED
    UVM_INFO seq_lab_tb.sv(604) @ 60000: uvm_test_top [END] run phase over at 60000

    Both runs leave the sequence UVM_STOPPED. Only kill() lets the phase end.

    What breaks: the fatal, and the zombie

    The first attempt in the opening is the second log. stop_sequences() flushed s1_1 from the request FIFO; a moment later the driver, doing the right thing for a reset, called item_done for it, and the sequencer had nothing to pair it with. The team's first instinct was to delete item_done from the reset path, which made the fatal go away and broke every test that reset without calling stop_sequences(). The real fix was to pick one owner for the cleanup: kill() the sequences that were running, and let the driver release its item.

    The second attempt, with no stop at all, is the zombie case. The packet sequence was blocked in finish_item; the driver's reset path called item_done, which released it, and it simply sent its next beat after reset. Nothing failed in the testbench; the DMA reported ERR for a packet that started mid-way. A reset test has to stop the old stimulus explicitly, then start new stimulus that begins with reprogramming the registers, because the DUT forgot them.

    Interview

    "How do you handle a reset in the middle of a sequence?"

    A weak answer: "Call stop_sequences and restart," or "disable fork on the sequence." The first fatals with a driver that releases its item; the second leaves zombie requests and a sequence that never reaches STOPPED.

    A strong answer: "Three parts. The driver races each transfer against reset with a guarded fork, drives reset values, and always calls item_done for the item it took. The test or virtual sequence kills the running sequences with kill(), which removes their requests and locks and calls do_kill, but leaves the driver's item for the driver to release. After reset, new sequence objects reprogram the DUT and resume traffic; monitors and scoreboards flush. stop_sequences also flushes the item the driver holds, so it must never be followed by the driver's item_done, and it leaves automatic objections raised."

    Follow-ups: Why is `disable fork` dangerous? (it kills every child of the current process, and skips the sequencer's cleanup). What is do_kill for? (releasing resources the sequence holds when it is killed).

    Check yourself A virtual sequence calls child.kill() on a sequence that is waiting in start_item (not yet granted). Does the driver need to do anything?

    No. The child has no item at the driver; its waiting request is removed from the arbitration queue by kill(). Only an item the driver has already received needs the driver's item_done.

    Check yourself Why does the reset-aware driver wait (vif.rst_n === 1'b1) at the top of its loop rather than right after item_done?

    It guards every path into get_next_item, including the first one at time 0 and the one right after an abandoned item. Taking a new item while reset is still asserted would start a transfer the DUT cannot see and could pull an item from a sequence that is about to be killed.

    Check yourself A test time-boxes a sequence with fork seq.start(sqr); #10us; join_any disable fork; and starts another sequence on the same sequencer later. Sometimes the log shows SEQREQZMB. Why?

    When the timeout wins while seq is waiting for a grant, disable fork kills its thread but leaves its request in the arbitration queue. The next arbitration finds a request whose process is dead and removes it with that error. Calling seq.kill() after the disable fork (and guarding the fork) tells the sequencer properly.

    Next: a sequence that can be killed may also need to keep the run phase open while it runs. Sequences and objections covers when a sequence should hold an objection, and why the bench lets the test do it.

    Related topics

    • Agent architecture

    • Objection counting rules (guarded disable fork)

    • Hang triage

    Previous

    82 / 280

    Next: Sequences and Objections: Who Keeps the Phase Open

    Up next

    Up nextSequences and Objections: Who Keeps the Phase Open

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Sequence Lifecycle and Hooks: What start() Does, Step by Step
    • Inside the Sequencer: Arbitration, Priority and Relevance
    • The uvm_do Macro Family: What It Expands To