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

  • How to Use This Course
  • Reading Paths
  • Quick Start: Your First Green Build
  • Part 1: How Chips Get Made6
  • Part 2: Your Workstation5
  • Part 3: Specify and Architect3
  • Part 4: The Design Loop6
  • Part 5: The Verification Ladder6
  • Part 6: Automation and Reproducibility5
  • Part 7: Hand-Off to the Back-End3
  • Part 8: When Things Go Wrong3
    • Troubleshooting Table
    • Debug Habits
    • Beginner Mistakes Checklist
  • Part 9: Project and Next Steps3
  • Reference4
Loading lesson

‹Part 8: When Things Go Wrong
  1. Topics
  2. Start Here: RTL and Verification
  3. Part 8: When Things Go Wrong
  4. Debug Habits

Part 8 · When Things Go Wrong · Foundation (F)

Debug Habits

Three habits for a test that used to pass: shrink the failure to one command, read the first error, and bisect history to the first bad commit.

On this page

    Note

    Level: Foundation (F) · Answers: A test that passed last week fails today. How do you find what changed without guessing?

    Before this: Troubleshooting Table

    A test that passed last week fails today. How do you find what changed without guessing?

    The build was green on Friday and red on Monday, and nobody remembers touching the counter. Reading a week of commits one by one feels thorough and mostly produces guesses. There is a shorter path: shrink the failure until one command repeats it, read the first error in its output, and let Git search the history for you.

    Three habits replace guessing

    The first habit is a minimal reproduction: shrink the failing flow until a single command fails every time you run it. When make all fails, try make sim alone; when that fails, you have one stage, one log, and one message instead of three. Never debug from the biggest failing command when a smaller one fails too.

    The second habit is reading the first error, not the last. A log ends with the stop and begins with the cause: fix the first complaint, rerun, and watch how many later lines vanish with it. The troubleshooting table from the previous lesson is organized this way, one stage at a time.

    The third habit is bisecting with Git, which means a binary search through history: Git checks out commits for you, runs your failing command on each, and halves the range until one commit remains. You supply the range and the test; the search does the walking. The rest of this lesson watches one run of that search.

    One bad commit hides among five

    The figure below shows the history this lesson searches: five commits, with the newest at the right failing. Bisect tests the middle commit first, then halves toward the side the verdict points at. Look at the badges above the boxes: the middle goes first, and the first bad commit is the one every later failure traces back to.

    Five commits c1 to c5 in a row. The middle commit c3 carries the badge tested 1st and the verdict first bad; c2 carries tested 2nd and good. The range halves with every test.

    Keep the verdict rule beside you while you read the run below. Git runs your command on a checked-out commit and reads only its exit status: 0 means good and anything nonzero means bad. Two verdicts are enough to corner one bad commit among five, because each test throws away half the range.

    Git finds the bad commit in two tests

    The history below holds five commits, from the baseline to the failing head. Read the short messages: only one of them touches the counter, but the search does not know that yet.

    Diagram
    # captured terminal output
    $ git log --oneline
    6ffa46c c5 readme
    ed1c07c c4 docs again
    ed7e36f c3 tidy counter
    7af7bb3 c2 docs
    d025be2 c1 baseline

    The output above lists the range bisect will search: five commits ending at the failing head. Commit hashes in your own run will differ, and the date line was left out of this transcript.

    Before searching, confirm the failure repeats with one command at the head:

    Diagram
    # captured terminal output
    $ make sim > /dev/null 2>&1; echo "head: make sim exit status $?"
    head: make sim exit status 2

    The output above was measured on macOS. Exit status 2 from make sim is the same run-time failure signature as case 7 of the troubleshooting table: everything compiles and the testbench reports a mismatch. A repeatable one-command failure is the entry ticket for bisect; without it the search has nothing reliable to test.

    Diagram
    # captured terminal output
    $ git bisect start HEAD $(git rev-list --max-parents=0 HEAD) > /dev/null && git bisect run make sim 2>&1 | grep -v -E '^(iverilog|cd build|VCD|ERROR|FATAL|make:|mkdir)|Time:|finish called|^$' | grep -v 'Date:' | cut -c1-100; git bisect reset > /dev/null 2>&1
    running 'make' 'sim'
    Bisecting: 0 revisions left to test after this (roughly 0 steps)
    [7af7bb3ee49578147cdc81d01e4edf10679a4612] c2 docs
    running 'make' 'sim'
    PASS: mini_controller
    ed7e36fef72fa1b0dc79785f59f055debcec7f53 is the first bad commit
    commit ed7e36fef72fa1b0dc79785f59f055debcec7f53
    Author: You <you@example.com>
        c3 tidy counter
     rtl/mini_controller.sv | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    bisect found first bad commit

    The output above was measured on macOS. Bisect checked out the middle commit, c3, and ran make sim there: that run failed, so the bad change sits in the older half. It then tested c2 and saw the PASS verdict, so the good side ends at c2 and c3 is reported as the first bad commit. Two tests cornered one commit out of five, which matches the arithmetic: each test halves the range, so five commits take about two or three tests (illustrative). The offending change replaced the increment 4'd1 with 4'd2: a counts-by-two change like the one First Simulation makes on purpose.

    Predict before you look

    Predict first Bisect tests the middle commit c3 and it fails, while c1 is known good. Which commit does it test next, and why?

    It halves toward the good end and tests c2 next. The failing c3 means the bad change sits in the range c1 to c3, and c2 is the middle of that range. A PASS at c2 leaves only c3 standing, which is exactly how this run ends after two tests.

    Break it on purpose: add two instead of one

    Cause this lesson's bug yourself. Change the counter increment from 4'd1 to 4'd2, run make sim, and confirm the exit status 2 you saw above. The failure lines match the count-by-two run in First Simulation, where the same change breaks every counting edge. Restore the increment and rerun to green before you continue: bisect needs a clean tree, and this edit has served its purpose.

    Three ways bisect points at the wrong commit

    • Bisecting with a test that is flaky. A test that sometimes passes on broken code sends the search down the wrong half, and the verdict names an innocent commit. Run your one command twice before trusting it, and only bisect a failure that repeats every time.

    • A history where one commit changes many things. Bisect points at the commit, not the line: a commit that tidies the counter, renames signals and edits the docs in one step still needs reading. Keep commits small so the first bad commit is also a small story.

    • Continuing to edit while bisecting. Uncommitted edits travel with every checkout the search makes, so every verdict tests your edits plus the commit. Commit or stash your work first, and leave the tree alone until the search reports.

    Rule: repeat it with one command, shrink it, then ask history what changed.

    Check yourself Six commits stand in a row and the bug arrived in commit 4. Which commits does bisect test first, and how many tests at most?

    It tests the middle first, then halves the range with every verdict: a bad verdict keeps the older half, a good verdict keeps the newer half. At most three tests for six commits (illustrative): the first test rules out three commits, the second rules out most of the rest, and the third isolates the one.

    Check yourself make all fails. Running the steps alone, lint passes and make sim fails. What is your reproduction, and why not debug from make all?

    The reproduction is make sim: one command that fails every time. make all mixes lint, simulation and synthesis into one log, so its output hides which stage complains. Shrink to the smallest failing step, then read that step's first error.

    Check yourself A log ends with a build stop but its first error is an unknown module type. Where do you start, and why?

    At the first error: a file is missing from the compile line, which is case 2 of the troubleshooting table. The last lines only report that the build stopped; they vanish once the missing file is added. Fix causes in order, never stops.

    Interview

    Asked: "How would you find which commit broke the build?"

    Weak answer: I would read through last week's commits and guess which one looks suspicious.

    Strong answer: I confirm the failure repeats with one command, note the last green commit to bound the range, and run git bisect run with the failing command. Exit status 0 counts as good and nonzero as bad; each test halves the range, so even a week of commits takes a handful of runs. The report names the first bad commit, which I read and fix, then rerun the full flow to green.

    Follow-ups: (1) What do you do when the test is flaky? (2) The culprit commit changed fifty files. What then?

    Key takeaways

    • Shrink a red build to one command that fails every time before changing anything.

    • Read the first error: later lines report the stop, only the first reports the cause.

    • Bisect halves history with every test, so the first bad commit takes a handful of runs, not a week of reading.

    Keep going

    Next: Lesson 8.3, Beginner Mistakes Checklist. You can find what changed. Next is the gallery of mistakes every beginner makes once, so you can make each of them zero times.

    Related topics

    • Hardware Debug: The Debug Loop

    • Troubleshooting Table

    • First Simulation

    Previous

    39 / 47

    Next: Beginner Mistakes Checklist

    Up next

    Up nextBeginner Mistakes Checklist

    Sign in to save progress

    Practice this lessonReport an error