0% found this document useful (0 votes)
18 views33 pages

RTL Debugging Guide

The document outlines various RTL debugging problems commonly encountered in VLSI design and interviews, providing detailed scenarios, buggy code examples, observed failures, root causes, fixes, and explanations for each issue. It covers a range of topics including pipeline data corruption, FIFO management, FSM control, clock gating, and handshake protocols. Each problem emphasizes the importance of proper design practices to avoid common pitfalls in digital design.

Uploaded by

rakeshvsworld
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views33 pages

RTL Debugging Guide

The document outlines various RTL debugging problems commonly encountered in VLSI design and interviews, providing detailed scenarios, buggy code examples, observed failures, root causes, fixes, and explanations for each issue. It covers a range of topics including pipeline data corruption, FIFO management, FSM control, clock gating, and handshake protocols. Each problem emphasizes the importance of proper design practices to avoid common pitfalls in digital design.

Uploaded by

rakeshvsworld
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

RTL DEBUGGING GUIDE

MUST TRY PRACTICAL DEBUG


SCENARIOS FOR INTERVIEWS
VLSI Debugging-Based Interview Problems
(Advanced, Interview-Grade)
This document contains moderate-to-large RTL debugging problems similar to what engineers face in
real projects and interviews. Each problem includes a realistic module, observed failure, root cause analysis,
and corrected implementation.

Debug Problem 1: Pipelined Packet Register – Data/Control


Corruption

Problem Statement

You are given a 3-stage pipeline that passes packet data and a valid signal. During simulation, output
packets are sometimes marked valid with wrong data.

Buggy Code

module packet_pipe (
input logic clk,
input logic rst_n,
input logic [31:0] din,
input logic vin,
output logic [31:0] dout,
output logic vout
);

logic [31:0] d1, d2, d3;


logic v1, v2, v3;

always_ff @(posedge clk) begin


if (!rst_n) begin
d1 <= 0; d2 <= 0; d3 <= 0;
v1 <= 0; v2 <= 0; v3 <= 0;
end else begin
d1 <= din;
d2 <= d1;
d3 <= d2;

v1 <= vin;
v2 <= vin; // BUG
v3 <= v2;
end

1
end

assign dout = d3;


assign vout = v3;

endmodule

Observed Failure

Random cycles show vout=1 while dout belongs to an older packet.

Root Cause

v2 is driven directly from vin instead of being pipelined from v1 . Control and data are misaligned.

Fix

v2 <= v1;

Explanation

In pipelines, all control signals must be registered exactly like data. Any mismatch causes packet
corruption.

Debug Problem 2: Synchronous FIFO – Full/Empty and Write


Corruption

Problem Statement

A small FIFO drops data and occasionally overwrites unread entries.

Buggy Code

module sync_fifo #(parameter DEPTH=8, WIDTH=8) (


input logic clk,
input logic rst_n,
input logic wr_en,
input logic rd_en,
input logic [WIDTH-1:0] wdata,
output logic [WIDTH-1:0] rdata,
output logic full,
output logic empty
);

2
logic [$clog2(DEPTH)-1:0] wr_ptr, rd_ptr;
logic [WIDTH-1:0] mem [0:DEPTH-1];

always_ff @(posedge clk) begin


if (!rst_n) begin
wr_ptr <= 0;
rd_ptr <= 0;
end else begin
if (wr_en && !full) begin
mem[wr_ptr] <= wdata;
wr_ptr <= wr_ptr + 1'b1;
end
if (rd_en && !empty) begin
rdata <= mem[rd_ptr];
rd_ptr <= rd_ptr + 1'b1;
end
end
end

assign empty = (wr_ptr == rd_ptr);


assign full = (wr_ptr == rd_ptr); // BUG

endmodule

Observed Failure

FIFO becomes full and empty at the same time, causing overwrites.

Root Cause

full and empty use identical logic. FIFO cannot distinguish wrap-around state.

Fix

logic [$clog2(DEPTH):0] wr_ptr_ex, rd_ptr_ex;

assign empty = (wr_ptr_ex == rd_ptr_ex);


assign full = (wr_ptr_ex[$clog2(DEPTH)] != rd_ptr_ex[$clog2(DEPTH)]) &&
(wr_ptr_ex[$clog2(DEPTH)-1:0] == rd_ptr_ex[$clog2(DEPTH)-1:0]);

Explanation

FIFO pointers need an extra MSB to differentiate full vs empty after wrap-around.

3
Debug Problem 3: FSM Controller – Missing Defaults and X
Propagation

Problem Statement

An FSM-based controller sometimes jumps to illegal states after reset.

Buggy Code

typedef enum logic [1:0] {IDLE, LOAD, EXEC, DONE} state_t;


state_t state, next;

always_ff @(posedge clk or negedge rst_n) begin


if (!rst_n)
state <= IDLE;
else
state <= next;
end

always_comb begin
if (start)
next = LOAD;
else if (state == LOAD)
next = EXEC;
else if (state == EXEC)
next = DONE;
end

Observed Failure

Simulation shows next = X and FSM locks up.

Root Cause

Not all paths assign next . Also, logic ignores current state in IDLE properly.

Fix

always_comb begin
next = state;
case (state)
IDLE : if (start) next = LOAD;
LOAD : next = EXEC;
EXEC : next = DONE;
DONE : next = IDLE;

4
default: next = IDLE;
endcase
end

Explanation

FSM combinational logic must cover all states and assign defaults to prevent latch and X-propagation.

Debug Problem 4: Clock Enable vs Gated Clock Timing Failure

Problem Statement

Design fails STA with large skew and glitches.

Buggy Code

always @(posedge (clk & en)) begin


q1 <= d1;
end

always @(posedge (clk & en)) begin


q2 <= q1;
end

Observed Failure

Timing reports show unconstrained paths and glitches on gated clock.

Root Cause

RTL clock gating produces glitchy clocks and breaks STA assumptions.

Fix

always_ff @(posedge clk) begin


if (en) begin
q1 <= d1;
q2 <= q1;
end
end

5
Explanation

Never gate clocks in RTL. Use clock enable logic or proper clock-gating cells inserted by synthesis.

Debug Problem 5: CDC Handshake – Lost Pulse

Problem Statement

A pulse generated in clkA domain is randomly missed in clkB domain.

Buggy Code

always_ff @(posedge clkA)


pulseA <= req;

always_ff @(posedge clkB)


pulseB <= pulseA;

Observed Failure

Some requests never reach clkB domain.

Root Cause

Single-flop CDC of a pulse causes metastability and pulse shrinking.

Fix

logic s1, s2;

always_ff @(posedge clkB) begin


s1 <= pulseA;
s2 <= s1;
end
assign pulseB = s2;

Explanation

CDC requires two-flop synchronizers or proper handshake / toggle logic for pulses.

6
Debug Problem 6: Pipeline Stall / Flush Logic Bug

Problem Statement

A 2-stage pipeline supports stall and flush. During branch flush, wrong data leaks to output.

Buggy Code

always_ff @(posedge clk) begin


if (!rst_n) begin
s1_data <= 0; s1_v <= 0;
s2_data <= 0; s2_v <= 0;
end else begin
if (!stall) begin
s1_data <= din;
s1_v <= vin;
end

if (flush) begin
s2_v <= 1'b0; // BUG: data not cleared
end else if (!stall) begin
s2_data <= s1_data;
s2_v <= s1_v;
end
end
end

Observed Failure

After flush, s2_v=0 but old s2_data propagates later incorrectly.

Root Cause

Flush clears only valid but not data, allowing stale data reuse.

Fix

if (flush) begin
s2_data <= '0;
s2_v <= 1'b0;
end

Explanation

Flush must invalidate both control and data to avoid ghost packets.

7
Debug Problem 7: Scoreboard Compare Mismatch (Verification
Logic Bug)

Problem Statement

Scoreboard reports mismatches even when DUT is correct.

Buggy Code

always @(posedge clk) begin


if (mon_valid)
exp_q.push_back(mon_data);

if (dut_valid) begin
if (exp_q.pop_front() != dut_data)
$error("Mismatch");
end
end

Observed Failure

Under backpressure, compare happens with empty queue.

Root Cause

No protection for queue empty condition.

Fix

if (dut_valid) begin
if (exp_q.size()==0)
$error("Unexpected DUT data");
else if (exp_q.pop_front() != dut_data)
$error("Mismatch");
end

Explanation

Verification logic must handle ordering and backpressure correctly.

8
Debug Problem 8: AXI Write Channel Protocol Bug

Problem Statement

AXI master violates protocol during burst writes.

Buggy Code

if (awvalid) begin
wvalid <= 1'b1;
wdata <= data;
wlast <= 1'b1; // BUG
end

Observed Failure

Slave errors because every beat asserts WLAST .

Root Cause

WLAST must assert only on final beat of burst.

Fix

wlast <= (beat_cnt == burst_len-1);

Explanation

Protocol control must follow burst counters, not single-cycle assumptions.

Debug Problem 9: Reset Sequencing Across Clock Domains

Problem Statement

Two clock domains share reset but sometimes one domain comes up earlier and misbehaves.

Buggy Code

always_ff @(posedge clkA or negedge rst_n)


if (!rst_n) qa <= 0; else qa <= da;

9
always_ff @(posedge clkB or negedge rst_n)
if (!rst_n) qb <= 0; else qb <= db;

Observed Failure

CDC paths break during reset release.

Root Cause

Async reset deassertion is unsynchronized per domain.

Fix

Synchronize reset per clock domain using two flops before usage.

Explanation

Reset must be asynchronously asserted but synchronously released per domain.

Debug Problem 10: Arbitration Deadlock Bug

Problem Statement

Two requesters and one grant logic cause system hang.

Buggy Code

always_comb begin
if (req0 && !busy) grant0 = 1;
if (req1 && !busy) grant1 = 1; // BUG: both can assert
end

Observed Failure

Both grants assert simultaneously, corrupting shared resource.

Root Cause

Mutual exclusion not enforced.

10
Fix

always_comb begin
grant0 = 0; grant1 = 0;
if (req0 && !busy) grant0 = 1;
else if (req1 && !busy) grant1 = 1;
end

Explanation

Arbitration must guarantee single winner per cycle.

Debug Problem 11: Multi-Cycle Path Treated as Single Cycle

Problem Statement

A datapath fails timing although logically correct.

Buggy Code

always_ff @(posedge clk)


stage2 <= stage1 + stage0;

Observed Failure

STA shows negative slack though design intended 2 cycles.

Root Cause

No pipeline register or multicycle constraint defined.

Fix

Insert extra register or declare multicycle path in constraints.

Explanation

RTL intent and STA constraints must match pipeline behavior.

11
Debug Problem 12: Width Truncation in Accumulator

Problem Statement

Accumulator saturates early.

Buggy Code

reg [7:0] sum;


always_ff @(posedge clk)
sum <= sum + data;

Observed Failure

Overflow silently truncates.

Root Cause

No extended precision.

Fix

reg [15:0] sum;

Explanation

Datapaths must allocate sufficient bit growth.

Debug Problem 13: Blocking in Sequential Pipeline

Buggy Code

always_ff @(posedge clk) begin


a = b;
b = c;
end

Issue

Pipeline collapses into same-cycle behavior.

12
Fix

always_ff @(posedge clk) begin


a <= b;
b <= c;
end

Explanation

Sequential logic must use non-blocking assignments.

Debug Problem 14: Async FIFO Pointer Sync Bug

Buggy Code

always_ff @(posedge rd_clk)


rd_sync <= wr_ptr;

Issue

Binary pointer CDC breaks full/empty.

Fix

Use Gray-coded pointer + two-flop sync per bit.

Explanation

Async FIFOs require gray pointer synchronization.

Debug Problem 15: X-Propagation in Comparator

Buggy Code

if (a == b)
hit = 1;

Issue

If X exists, comparison yields X and no assignment.

13
Fix

hit = (a === b);

Explanation

Case equality avoids X optimism issues.

Debug Problem 16: Handshake Ready/Valid Violation

Buggy Code

if (valid)
data <= next_data;

Issue

Data updates without ready, breaking protocol.

Fix

if (valid && ready)


data <= next_data;

Explanation

Transfers occur only when both ready and valid assert.

Debug Problem 17: Register Enable Missing Hold

Buggy Code

always_ff @(posedge clk)


if (en) q <= d;

Issue

When en=0, q becomes X in some sims.

14
Fix

always_ff @(posedge clk)


if (en) q <= d; else q <= q;

Explanation

Explicit hold avoids unintended latch/X behavior in some tools.

Debug Problem 18: Memory Read-During-Write Ambiguity

Buggy Code

if (wr_en) mem[addr] <= wdata;


rdata <= mem[addr];

Issue

Read-during-write undefined behavior.

Fix

Add bypass logic when wr_en && rd_en && same addr.

Explanation

Memories need explicit RAW forwarding logic.

Debug Problem 19: Counter Rollover Bug

Buggy Code

if (cnt == MAX) cnt <= 0;


else cnt <= cnt + 1;

Issue

If MAX mismatches width, overflow happens early.

15
Fix

Ensure MAX matches counter width and range.

Explanation

Counters must align constant sizing with register width.

Debug Problem 20: Pipeline Backpressure Loss

Buggy Code

if (in_valid)
buf <= in_data;

Issue

Overwrites buffer when downstream not ready.

Fix

if (in_valid && in_ready)


buf <= in_data;

Explanation

Backpressure must gate storage updates.

Debug Problem 21: Pipeline Flush With Branch Redirect Bug

Problem Statement

A CPU-like pipeline redirects PC on branch, but wrong instruction commits after flush.

Buggy Code

always_ff @(posedge clk) begin


if (!rst_n) begin
pc1<=0; v1<=0;
pc2<=0; v2<=0;
end else begin

16
if (!stall) begin
pc1 <= pc_in;
v1 <= vin;
end

if (flush) begin
v2 <= 1'b0; // BUG: pc2 not cleared
end else if (!stall) begin
pc2 <= pc1;
v2 <= v1;
end
end
end

Observed Failure

After branch, stale pc2 is committed with new valid later.

Root Cause

Flush invalidates only control, not datapath.

Fix

if (flush) begin
pc2 <= '0;
v2 <= 1'b0;
end

Explanation

Flush must reset both data and valid to avoid ghost instructions.

Debug Problem 22: Credit-Based Flow Control Leak

Problem Statement

Transmitter uses credits but overflows receiver buffer.

Buggy Code

always_ff @(posedge clk) begin


if (!rst_n) credit <= 4'd8;

17
else begin
if (send) credit <= credit - 1'b1;
if (recv) credit <= credit + 1'b1; // BUG
end
end

Observed Failure

Credits exceed maximum and allow illegal sends.

Root Cause

No saturation and simultaneous send/recv mishandled.

Fix

if (send && !recv) credit <= credit - 1'b1;


else if (!send && recv) credit <= credit + 1'b1;

Explanation

Flow control counters must be mutually exclusive and bounded.

Debug Problem 23: AXI Read Data Channel Reordering Bug

Problem Statement

AXI master returns data out of order under multiple outstanding reads.

Buggy Code

if (rvalid && rready) begin


resp_q.pop_front();
end

Observed Failure

Returned data mismatches original address order.

Root Cause

No transaction ID tracking for multiple outstanding reads.

18
Fix

Track AXI IDs and reorder using ID-based queues.

Explanation

AXI requires maintaining ordering per ID, not simple FIFO pop.

Debug Problem 24: Multi-Clock Handshake Toggle Loss

Problem Statement

Toggle-based CDC occasionally misses events.

Buggy Code

always_ff @(posedge clkA)


if (send) toggleA <= ~toggleA;

always_ff @(posedge clkB)


toggleB <= toggleA; // BUG

Observed Failure

Some toggles not detected in clkB.

Root Cause

No 2-flop synchronizer on toggle signal.

Fix

always_ff @(posedge clkB) begin


s1 <= toggleA;
s2 <= s1;
end
assign toggleB = s2;

Explanation

CDC toggles must be synchronized before edge detect.

19
Debug Problem 25: DMA Descriptor Fetch Corruption

Problem Statement

DMA engine fetches wrong descriptor under backpressure.

Buggy Code

if (desc_valid)
cur_desc <= mem_desc;

Observed Failure

Descriptor updates even when downstream not ready.

Root Cause

No ready/valid handshake protection.

Fix

if (desc_valid && desc_ready)


cur_desc <= mem_desc;

Explanation

Storage updates must respect handshake to prevent overwrite.

Debug Problem 26: Speculative Execute Not Squashed

Problem Statement

Speculative operations commit after mispredict.

Buggy Code

if (issue)
exe_reg <= op;

Observed Failure

Wrong-path instructions modify state.

20
Root Cause

No squash control on mispredict.

Fix

if (flush)
exe_reg <= '0;
else if (issue)
exe_reg <= op;

Explanation

Speculative pipelines must squash state on redirect.

Debug Problem 27: Cache Line Fill Partial Write Bug

Problem Statement

Cache line fill overwrites valid words incorrectly.

Buggy Code

if (fill_en)
cache[line_idx] <= fill_data;

Observed Failure

Unrelated words lost during partial fills.

Root Cause

Whole line overwritten instead of masked update.

Fix

cache[line_idx][word_sel] <= fill_data;

Explanation

Caches must update per-word with masks, not whole-line blindly.

21
Debug Problem 28: Timer Interrupt Double Trigger

Problem Statement

Timer generates two interrupts per expiry.

Buggy Code

if (cnt == 0)
irq <= 1'b1;

Observed Failure

IRQ stays high for multiple cycles.

Root Cause

No one-shot or clear mechanism.

Fix

if (cnt == 0 && !irq_sent)


irq <= 1'b1;

Explanation

Interrupt logic must be edge-like, not level stuck.

Debug Problem 29: Register File Write-After-Read Hazard

Problem Statement

Read returns old data when read and write same address.

Buggy Code

if (we) rf[waddr] <= wdata;


rdata <= rf[raddr];

Observed Failure

RAW hazard not forwarded.

22
Root Cause

No bypass for same-cycle read/write.

Fix

if (we && waddr==raddr) rdata <= wdata;


else rdata <= rf[raddr];

Explanation

Register files require forwarding to resolve hazards.

Debug Problem 30: Performance Counter Overflow Bug

Problem Statement

Performance counter wraps silently and reports wrong stats.

Buggy Code

cnt <= cnt + event;

Observed Failure

Overflow corrupts measurement.

Root Cause

No saturation or width planning.

Fix

if (!cnt_max)
cnt <= cnt + event;

Explanation

Counters for metrics should saturate or widen to avoid overflow.

23
Debug Problem 31: Out-of-Order Completion Queue Bug

Problem Statement

A completion queue returns responses to software, but occasionally responses mismatch the issued
transaction.

Buggy Code

typedef struct packed {logic [7:0] id; logic [31:0] data;} resp_t;
resp_t cq[$];

always_ff @(posedge clk) begin


if (issue_valid)
cq.push_back('{issue_id, issue_data});

if (resp_valid && resp_ready)


cq.pop_front(); // BUG
end

Observed Failure

Returned response does not match original ID.

Root Cause

Completion pops FIFO order, but responses may return out-of-order by ID.

Fix

int idx;
if (resp_valid && resp_ready) begin
idx = cq.find_index(x) with ([Link] == resp_id);
if (idx >= 0) [Link](idx);
end

Explanation

OOO systems must match by transaction ID, not FIFO position.

24
Debug Problem 32: Write Buffer Merge Corruption

Problem Statement

Write buffer merges stores to same cache line but corrupts data.

Buggy Code

if (wr_en) begin
buf_line <= wr_data; // BUG: overwrites whole line
end

Observed Failure

Partial writes destroy unrelated bytes.

Root Cause

No byte-enable masking when merging.

Fix

buf_line <= (buf_line & ~wr_mask) | (wr_data & wr_mask);

Explanation

Write buffers must merge using byte masks, not full overwrite.

Debug Problem 33: Load-Store Queue Dependency Bug

Problem Statement

A load reads stale data when a prior store targets same address.

Buggy Code

if (ld_issue)
ld_data <= mem[ld_addr];

Observed Failure

Load bypass from store queue not applied.

25
Root Cause

No store-to-load forwarding.

Fix

if (stq_hit)
ld_data <= stq_data;
else
ld_data <= mem[ld_addr];

Explanation

Modern pipelines must forward from store queue before memory.

Debug Problem 34: NoC Router Credit Underflow

Problem Statement

Network router sends flits even when downstream buffer is full.

Buggy Code

if (send)
credit <= credit - 1'b1;

Observed Failure

Credit counter underflows and wraps.

Root Cause

Send allowed without checking credit availability.

Fix

if (send && credit != 0)


credit <= credit - 1'b1;

Explanation

Credit-based systems must never transmit when credit is zero.

26
Debug Problem 35: Reorder Buffer Commit Bug

Problem Statement

CPU commits wrong instruction after exception.

Buggy Code

if (commit_en) begin
arch_reg <= rob_head_data;
rob_head <= rob_head + 1;
end

Observed Failure

Instructions after fault still commit.

Root Cause

No exception check before commit.

Fix

if (commit_en && !rob_head_exc) begin


arch_reg <= rob_head_data;
rob_head <= rob_head + 1;
end

Explanation

ROB must stop commit on exception and flush younger entries.

Debug Problem 36: AXI Burst Counter Desync

Problem Statement

AXI master ends burst early causing protocol violation.

27
Buggy Code

if (wvalid && wready)


beat_cnt <= beat_cnt + 1'b1;

Observed Failure

WLAST asserted at wrong time.

Root Cause

Counter not reset on new address phase.

Fix

if (aw_fire)
beat_cnt <= 0;
else if (w_fire)
beat_cnt <= beat_cnt + 1'b1;

Explanation

Burst counters must align with AW channel handshake.

Debug Problem 37: Pipeline Register Enable Skew

Problem Statement

One pipeline stage advances while next stage stalls.

Buggy Code

if (!stall1) s1 <= din;


if (!stall2) s2 <= s1;

Observed Failure

Data duplication and loss.

Root Cause

Stages use independent stall without coupling.

28
Fix

if (!stall2) begin
if (!stall1) s1 <= din;
s2 <= s1;
end

Explanation

Pipeline control must propagate backpressure consistently.

Debug Problem 38: Power Gating State Loss

Problem Statement

After power gating, block resumes with corrupted state.

Buggy Code

always_ff @(posedge clk)


state <= next_state;

Observed Failure

State undefined after power-up.

Root Cause

No save/restore around power collapse.

Fix

if (!pwr_good)
state <= RESET_STATE;
else
state <= next_state;

Explanation

Power-aware designs must reinitialize or restore state after gating.

29
Debug Problem 39: Watchdog Timer False Trigger

Problem Statement

Watchdog fires even when system is alive.

Buggy Code

if (kick)
wdt_cnt <= 0;
else
wdt_cnt <= wdt_cnt + 1'b1;

Observed Failure

Kick missed when asserted near clock edge.

Root Cause

Kick not synchronized to watchdog clock.

Fix

kick_sync1 <= kick;


kick_sync2 <= kick_sync1;

Explanation

Control inputs to timers must be synchronized before use.

Debug Problem 40: Bus Bridge Width Adaptation Bug

Problem Statement

Bridge converts 64-bit bus to 32-bit, but upper data lost.

Buggy Code

out_data <= in_data[31:0]; // BUG

30
Observed Failure

Upper half of payload never transferred.

Root Cause

No beat-splitting for width conversion.

Fix

if (beat_sel==0) out_data <= in_data[31:0];


else out_data <= in_data[63:32];

Explanation

Bus width adapters must serialize wide words across multiple beats.

End of Advanced Debugging Set (Large-Code Style).

31
Excellence in World class
VLSI Training & Placements

Do follow for updates & enquires

+91- 9182280927

You might also like