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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Puzzle Bank
  • SystemVerilog Language Puzzles25
  • Difficulty Guide
  • Full Topic Map
  • Interview-Day Tips
  • Study Route: One Week Before the Interview
  • Study Route: Two Weeks
  • Study Route: Four Weeks (Full Course)4
  • OOP & Testbench Puzzles25
  • Concurrency & Timing Puzzles25
  • Randomization & Constraint Puzzles25
  • SVA Assertion Puzzles25
  • Functional Coverage Puzzles25
  • Logic & Aptitude Puzzles25
  • Scenario & Debug Puzzles25
    • Testbench Debug Puzzles: Why Did the Test Hang?
    • Deadlock Scenario Puzzles: Objection and Phase Stalls
      • Early Return Leaves Objection Raised
      • Background Poller Keeps Running After Traffic Ends
      • Semaphore Token Leak on Error Path
      • join_any Ends the Task with the Mailbox Full
      • Lock Inversion with Two Semaphores
      • disable fork Kills the Thread That Owned the Drop
    • Scoreboard Puzzles: Mismatch or Ordering Bug?
    • Real Interview Rounds: End-to-End Debug Walkthrough
    • Scenario & Debug Puzzles: Tricky Q&A
  • UVM Testbench Puzzles25
  • Clock, Reset & CDC Puzzles25
  • Bus and Interface Protocol Puzzles25
  • Interview Prep: Puzzle Rounds7
  • DV Puzzle Cheatsheet
Loading lesson

←Deadlock Scenario Puzzles: Objection and Phase Stalls
  1. Topics
  2. DV Interview Puzzles
  3. Scenario & Debug Puzzles
  4. Deadlock Scenario Puzzles: Objection and Phase Stalls
  5. join_any Ends the Task with the Mailbox Full
On this page

    VLSI DV Interview PuzzlesAll levels

    join_any Ends the Task with the Mailbox Full

    The test passes and prints PASS, but coverage is tiny and the scoreboard saw almost no packets: 'processed 0 of 100'. There is no hang and no fatal. Find the control-flow bug in the producer/consumer task.

    Puzzle

    Difficulty: Medium · Puzzle 4 of 6 · Topic: Deadlock Scenario Puzzles: Objection and Phase Stalls

    The test passes and prints PASS, but coverage is tiny and the scoreboard saw almost no packets: 'processed 0 of 100'. There is no hang and no fatal. Find the control-flow bug in the producer/consumer task.

    Code

    design.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
    mailbox #(pkt) mbx = new();   // unbounded
    int processed;
    
    // inside a class (methods are automatic); a module-scope static task would share one p
    task run_traffic();
      fork
        begin : producer
          repeat (100) begin
            pkt p;                // class context, or make the task automatic
            p = new();            // a fresh object per iteration
            build_pkt(p);
            mbx.put(p);           // unbounded mailbox: never blocks
          end
        end
        begin : consumer
          forever begin
            pkt q;
            mbx.get(q);
            process_pkt(q);       // takes time (#10)
            processed++;
          end
        end
      join_any
      disable producer;           // no-op: producer already finished
    endtask

    Hint

    Which of the two branches finishes first, and what does join_any do when it does?

    Step-by-step solution

    Diagram
    1) Print time and mbx.num() at the end of run_traffic: the end time is 0 and mbx.num() is 99.
    2) The producer puts all 100 packets at time 0 (an unbounded mailbox put never blocks) and exits. join_any returns as soon as the producer finishes.
    3) disable producer does nothing: it already finished. The consumer is simply idle/busy, it is not deadlocked, and nothing 'died'.
    4) The task returns, the test finishes, and 99 packets are never processed (verified in Verilator with the code inside a class: end time 0, processed=0, left=99). Note: a module-scope task is static, so an initialised declaration like pkt p = new() would run once and every put would be the same handle; declare and construct separately inside a class or an automatic task.
    5) Fix: wait for the work, not the first thread. Use fork..join with a poison pill (producer puts a null packet, consumer breaks on null), or after the producer finishes wait (mbx.num() == 0 && inflight == 0) before returning; and add an end-of-test check that mbx is empty.

    Answer

    Answer: Bug: join_any returns when the producer finishes, ending the task with almost all packets still in the mailbox; the consumer blocking on an empty mailbox is normal idling, nothing is deadlocked. Fix: wait until the mailbox is empty and in-flight work is done (or use a poison pill with fork..join), and check mailbox emptiness at end of test.

    How this was checked

    Checked by running the code in Verilator 5; the printed output supports the answer above. Plain-SV model of the producer/consumer task: join_any returns with the work unprocessed, a poison pill with wait fork finishes it.

    Why candidates get it wrong

    The wrong story (the producer died and the consumer is stuck) sounds plausible but nothing died: the symptom is silent data loss, which is worse than a hang because the test passes.

    Interviewer follow-up

    How would you redesign this with one supervisor thread owning both producer/consumer lifetime? Model answer: A supervisor forks producer and consumer, producer sets a done flag (or sends a poison pill) when finished, the consumer exits when the pill arrives or when done is set and the mailbox is empty, and the supervisor does a plain join then asserts mbx.num()==0; one place decides when the run is over.

    Say it out loud

    I start by asking whether anything is really deadlocked, because a consumer blocked on an empty mailbox is normal idling. The root cause is that join_any returns when the producer finishes, which ends the task with almost all packets still in the mailbox, so data is silently lost and the test passes. I confirm it by checking the mailbox occupancy at the end of the test. The fix is to wait until the mailbox is empty and the in-flight work is done, or to use a poison pill with fork and join.

    Related topics

    • End-of-Test Checks: What Must Be Empty Before a Test Passes

    • End-of-Test Chain: Extract, Check, Report and Final

    • Back to Deadlock Scenario Puzzles: Objection and Phase Stalls

    • Scenario & Debug Puzzles section

    Previous

    195 / 293

    Next: Lock Inversion with Two Semaphores

    Up next

    Up nextLock Inversion with Two Semaphores

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Early Return Leaves Objection Raised
    • Background Poller Keeps Running After Traffic Ends
    • disable fork Kills the Thread That Owned the Drop