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 Here6
  • SystemVerilog Language Foundations28
    • SystemVerilog Data Types
    • Arrays: Packed, Dynamic, Queues & Associative
    • Procedural Code & Scheduling
    • Operators, Expressions & Width Rules
      • Signed & Unsigned Arithmetic
      • Equality, Case Equality & Wildcards
      • unique, unique0 & priority
      • Shifts, Concatenation & Streaming
      • Expression Width & Evaluation Rules
    • Packages, Typedefs & Compilation
    • Interfaces, Modports & Clocking Blocks
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Operators, Expressions & Width Rules
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Language Foundations
  4. Operators, Expressions & Width Rules
  5. Signed & Unsigned Arithmetic

Part 1 · Language Foundations · Intermediate

Signed & Unsigned Arithmetic

Sign extension rules, mixed signed/unsigned expression poisoning, $signed/$unsigned casts, and the classic signed-vs-unsigned compare bug.

On this page

    Why this matters

    A temperature alarm set to fire below 10 never fired at -5. The temperature was signed and the threshold was not, so the comparison read -5 as 251.

    After this lesson you can: predict whether an expression is signed, spot the operand that makes it unsigned, and fix the comparison without changing its meaning.

    How signedness is decided

    Every expression gets its signedness before any value is computed. The context is signed only when every operand is signed.

    A single unsigned operand makes the whole context unsigned. The signed operands then zero-extend, so negative values read as large positives.

    Operand A

    Operand B

    Context result

    signed

    signed

    SIGNED (both sign-extend)

    signed

    unsigned

    UNSIGNED (both zero-extend)

    unsigned

    unsigned

    UNSIGNED

    Example: byte s = -1 (8'hFF) against logic [15:0] u = 16'h10. s < u goes unsigned, s zero-extends to 255, and the result is FALSE.

    The compare bug and its fix

    A signed temperature against an unsigned threshold is the classic case. The mixed comparison goes unsigned, so -5 reads as 251 and the branch never fires.

    One unsigned operand makes the whole comparison unsigned.

    The fix keeps every operand signed. Cast the unsigned side through one extra zero bit, or declare it signed at the source.

    design.sv
    1
    2
    3
    4
    5
    // snippet
    logic signed [15:0] a16, b16;
    logic ok1 = (a16 > -16'sd4);    // sized signed literal: signed compare
    logic ok2 = (a16 > -4);         // unsized decimal is signed: signed compare
    logic bad2 = (a16 > 16'hFFFC);  // sized hex defaults unsigned: unsigned compare

    Only sized based literals default to unsigned. Unsized decimals are signed 32-bit. The unbased fills '0 and '1 adapt to their context width, but they are unsigned, so s < '0 is never true: compare against 0 for sign tests.

    Common mistakes

    Common pitfall

    Comparing a signed counter with an unsigned bound silently goes unsigned for negative values. Widen the unsigned side with {1'b0, u} before $signed, since $signed alone reinterprets the top bit as a sign bit.

    Interview

    Likely questions: why a signed comparison fails, what $signed really does, and whether a part-select of a signed vector stays signed.

    • Poisoning explains the failure: one unsigned operand makes the context unsigned.

    • `$signed` reinterprets bits for extension and comparison; it never modifies them.

    • Part-selects and concatenations are always unsigned, even from signed vectors.

    Check yourself s8 is a signed byte holding -1. Is s8[7:0] < 0 true?

    No. A part-select is unsigned even when it covers the whole signed vector, so it reads 255. The second bench checks exactly this.

    Check yourself What is the difference between $signed(b) and $signed({1'b0, b}) for an 8-bit unsigned b?

    $signed only reinterprets bits. On values above 127 it turns the top bit into a sign bit, while the zero-padded form keeps the magnitude.

    Testbench

    Predict first In the first bench temp is -5 and bound is 10. What do lt_mixed and lt_fixed read?

    lt_mixed is 0 and lt_fixed is 1. The mixed compare goes unsigned and sees 251 < 10; the fixed one stays signed and sees -5 < 10.

    One block checks the mixed compare and the cast fix; a second checks that selects and concatenations are unsigned.

    signed_cmp.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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    module signed_cmp (
      input  logic signed [7:0] temp,
      input  logic        [7:0] bound,
      output logic lt_mixed,
      output logic lt_fixed
    );
      always_comb begin
        lt_mixed = temp < bound;
        lt_fixed = temp < $signed({1'b0, bound});
      end
    endmodule
    
    module tb_signed_cmp;
    
      timeunit 1ns; timeprecision 1ps;
      logic signed [7:0] temp;
      logic        [7:0] bound;
      logic lt_mixed, lt_fixed;
      int errors = 0;
      signed_cmp dut (.temp(temp), .bound(bound), .lt_mixed(lt_mixed), .lt_fixed(lt_fixed));
      initial begin
        temp = -8'sd5; bound = 8'd10; #1;
        if (lt_mixed !== 1'b0) errors++;
        if (lt_fixed !== 1'b1) errors++;
        temp = 8'sd7; bound = 8'd10; #1;
        if (lt_mixed !== 1'b1) errors++;
        if (lt_fixed !== 1'b1) errors++;
        temp = -8'sd5; bound = 8'd251; #1;
        if (lt_fixed !== 1'b1) errors++;
        if (errors != 0) $fatal(1, "FAIL: signed compare checks disagree");
        $display("PASS: mixed compare goes unsigned while the zero-extended cast holds");
        $finish;
      end
    endmodule
    Terminal
    PASS: mixed compare goes unsigned while the zero-extended cast holds
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    module tb_select_unsigned;
      timeunit 1ns; timeprecision 1ps;
      logic signed [7:0] s8;
      logic r1, r2, r3;
      int errors = 0;
      initial begin
        s8 = -8'sd1;
        r1 = (s8[7:0] < 0);
        r2 = ({s8} < 0);
        r3 = (s8 < 0);
        #1;
        if (r1 !== 1'b0) errors++;
        if (r2 !== 1'b0) errors++;
        if (r3 !== 1'b1) errors++;
        if (errors != 0) $fatal(1, "FAIL: select signedness checks disagree");
        $display("PASS: part-select and concat stay unsigned while the plain signal stays signed");
        $finish;
      end
    endmodule
    Terminal
    PASS: part-select and concat stay unsigned while the plain signal stays signed

    Remember

    • An expression is signed only if every operand is signed.

    • Part-selects, concatenations and sized based literals are unsigned.

    • $signed reinterprets bits; widen with {1'b0, u} first to keep the value.

    Rule of thumb: one unsigned operand makes the whole expression unsigned.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. Icarus 13 (-g2012) runs them too, with the same output.

    Related topics

    • Next: Equality, Case Equality & Wildcards

    Previous

    21 / 259

    Next: Equality, Case Equality & Wildcards

    Up next

    Up nextEquality, Case Equality & Wildcards

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • unique, unique0 & priority
    • Shifts, Concatenation & Streaming
    • Expression Width & Evaluation Rules