← Design Verification Interviews

Interview Question · Code Reading

The Negative Number That Was Not Smaller

L0 FresherL1 Juniorsystemverilogsignednessoperators

The Question

The interviewer asks

What does this print, and why?

systemverilog
logic signed [7:0] s = -1;
logic        [7:0] u = 1;
initial if (s < u) $display("negative is smaller");
        else       $display("surprise");
Reveal the Model Answer

It prints surprise. An expression is signed only if every operand is signed. So the unsigned u makes the whole comparison unsigned, and s is read as 255. Declaring s as signed does not help. Make both operands signed, widening the unsigned one first with $signed({1'b0, u}) so that a large value cannot flip negative.

One Unsigned Operand Makes the Comparison UnsignedHow the snippet decides what kind of comparison s < u is. Arrows show operands feeding a rule and its result.
One unsigned operand makes the comparison unsignedHow the snippet decides what kind of comparison `s < u` is. Arrows show operands feeding a rule and its result. component logic signed [7:0]s = -1 bits 1111_1111 component logic [7:0] u = 1 unsigned component Is any operandunsigned? yes, u is component Unsigned compare:255 < 1 false, so it printssurprise component $signed({1'b0,u}) add a zero sign bit,then make it signed component Signed compare:-1 < 1 true unsignedwhole compareunsignedthe fixboth signed
Read the diagram as text
  • logic signed [7:0] s = -1: bits 1111_1111
  • logic [7:0] u = 1: unsigned
  • Is any operand unsigned?: yes, u is
  • Unsigned compare: 255 < 1: false, so it prints surprise
  • $signed({1'b0, u}): add a zero sign bit, then make it signed
  • Signed compare: -1 < 1: true
  • logic signed [7:0] s = -1 → Is any operand unsigned?
  • logic [7:0] u = 1 → Is any operand unsigned?: unsigned
  • Is any operand unsigned? → Unsigned compare: 255 < 1: whole compare unsigned
  • logic [7:0] u = 1 → $signed({1'b0, u}): the fix
  • $signed({1'b0, u}) → Signed compare: -1 < 1: both signed

Declaring s as signed is not enough. Both operands must be signed for a signed comparison.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Fresher)

A good fresher answer predicts surprise and gives the reason: one unsigned operand makes the whole comparison unsigned.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyWhat IfProve ItBreak ItCompare

  1. The Negative Number That Was Not Smaller
  2. WhyWhy the Operands Decide SignednessL1 JuniorL2 Mid-Level

    Why did the language designers make signedness depend only on the operands, not on where the result goes?

    Reveal the Model Answer

    The standard evaluates an expression in two steps. First, the operands fix the width and signedness of each part. Then the operands are extended to that size. Keeping the result target out of the first pass means one expression has one meaning wherever it appears. The price is the trap you just saw, so the rule is to make operands agree.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Signed arithmetic only matters for division."

    It also changes comparisons, right shifts with >>> and sign extension when a narrow value is widened.

    Source IEEE 1800-2017 §11.4.10

  3. What IfA Negative Literal Next to an Unsigned VectorL1 JuniorL2 Mid-Level

    What if you write a < -1 with a declared logic [7:0]?

    Reveal the Model Answer

    One unsigned operand makes the whole comparison unsigned. Both sides are sized to 32 bits, the width of the literal. So -1 becomes 32'hFFFF_FFFF, and a < -1 is true for every 8-bit a. Make the unsigned side signed and wide enough, as in int'(a) < -1.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "A negative literal keeps the comparison signed."

    Signed arithmetic needs every operand to be signed. One unsigned vector turns the whole comparison unsigned.

    Source IEEE 1800-2017 §11.8.1

  4. CompareSigned Cast vs Widening FirstL1 JuniorL2 Mid-Level

    Compare fixing the comparison with $signed(u) and with $signed({1'b0, u}). When do they differ?

    Reveal the Model Answer

    $signed(u) reinterprets the same 8 bits, so any value of 128 or more becomes negative: 200 turns into -56. $signed({1'b0, u}) first adds a zero sign bit, so 200 stays 200 as a 9-bit signed value. They agree for small values, which is why the plain cast passes a quick test and fails in the field.

    Two casts of u = 200
    ExpressionBitsValues < it, with s = -1
    $signed(u)1100_1000-560
    $signed({1'b0, u})0_1100_10002001

    The values are the output of the run on this question.

    RUNIcarus Verilog · in your browser
    Edit the code, then run it
    Expected: $signed(u) = -56, so s < $signed(u) is 0 $signed({1'b0,u}) = 200, so s < it is 1

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "$signed makes the value signed without changing it."

    It keeps the bits, not the value. For the top half of the unsigned range those bits mean a negative number.

    Source IEEE 1800-2017 §11.7

  5. Break ItThe Down-Counter That Never StoppedL1 Junior

    A test hangs in this loop. What is wrong?

    systemverilog
    for (bit [3:0] i = 3; i >= 0; i--) begin
      drive_lane(i);
    end
    Reveal the Model Answer

    An unsigned counter is never below zero, so i >= 0 is always true; after 0 it wraps to 15 and keeps going. Use a signed loop variable such as int, or count up.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The loop runs four times and stops at 0."

    The twin guards the loop at 20 passes; it hits the guard with the 4-bit counter and runs exactly four times with int.

    Run It

    A 4-bit unsigned down-counter with a guard so the run ends.

    RUNVerilator · simulator service
    Edit the code, then run it
    Expected: iterations=21 FAIL: an unsigned counter is never below 0, so i >= 0 is always true
    See the Fix
    module tb;
      int iters = 0;
      initial begin
        // Visit 3, 2, 1, 0 with a 4-bit counter.
        for (int i = 3; i >= 0; i--) begin
          iters++;
          if (iters > 20) break;  // guard, so the run ends
        end
        $display("iterations=%0d", iters);
        if (iters > 4) $display("FAIL: an unsigned counter is never below 0, so i >= 0 is always true");
        else $display("PASS: the loop ran four times");
      end
    endmodule
    

    Prints: iterations=4 PASS: the loop ran four times

  6. Prove ItSolve 1.4.1: Safe Opcode DecoderSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem 1.4.1

What People Get Wrong

Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.

Misconception

"s is declared signed, so the comparison is signed."

Signedness comes from all the operands, not from one declaration. The twin compares -1 with 1 and takes the unsigned branch.

Run It

A signed -1 compared with an unsigned 1.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: FAIL: one unsigned operand made the compare unsigned, so -1 became 255
See the Fix
module tb;
  logic signed [7:0] s = -1;
  logic        [7:0] u = 1;
  initial begin
    // The fix: both operands signed, u widened first so 255 cannot turn negative.
    if (s < $signed({1'b0, u})) $display("PASS: -1 < 1");
    else $display("FAIL: one unsigned operand made the compare unsigned, so -1 became 255");
  end
endmodule

Prints: PASS: -1 < 1

Misconception

"Assign the result to a signed variable and the compare becomes signed."

The type of the left-hand side never changes how the operands of a relational operator are interpreted. Relational results are always one unsigned bit.

Source IEEE 1800-2017 §11.8.1

Buzzword Without Mechanism

Says "use $signed" without saying what it does to the bits.

$signed only reinterprets the same bits. It does not widen, which is exactly why $signed(8'd200) becomes -56.

Learn It, Then Prove It