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. Expression Width & Evaluation Rules

Part 1 · Language Foundations · Intermediate

Expression Width & Evaluation Rules

Context-determined vs self-determined width, intermediate truncation, the (a+b)>>1 overflow puzzle, and literal width traps.

On this page

    Why this matters

    An average of 200 and 200 came out as 72. The sum was computed at 8 bits because the result variable was 8 bits, so the carry vanished before the shift.

    After this lesson you can: work out the width an expression is computed at, name the positions that keep their own width, and fix an expression that loses its carry.

    Width is decided before evaluation

    Operand widths resolve statically, before values compute. Most positions are context-determined: the widest participant, assignment target included, sets the width for all.

    A few positions stay self-determined no matter the context. The shift amount (the right operand of << or >>), concatenation operands and the replication count keep their own width.

    The assignment target is part of the context, so the same right side can keep or lose its carry.

    Position

    Rule

    Shift amount (right operand of a shift)

    Self-determined: never widens the shift

    Concatenation operands

    Own width only; unsized literals illegal

    Replication count

    Self-determined

    Value being shifted (left operand)

    Context-determined: widened with the rest of the expression

    Comparison result

    Self-determined single bit

    The averaging puzzle

    Predict first a and b are 8-bit and both hold 200. avg = (a + b) >> 1 is assigned to an 8-bit avg. What does avg hold?

    72, not 200. The context is 8 bits, so 400 wraps to 144 before the shift, and 144 halved is 72.

    Averaging with (a+b)>>1 drops the carry before halving. With a = b = 200 the sum 400 wraps to 144, and the average reads 72 instead of 200.

    The 8-bit target makes the whole context 8 bits, so the carry never exists. The shift cannot recover a carry the context never had: widen the context with a cast, a wider temp, or a 9-bit zero operand. With a 16-bit target the same (a+b)>>1 gives 200.

    Literal traps

    Unsized decimals are 32-bit signed, while sized literals are exact-width unsigned. The unbased fills '0 and '1 expand to whatever context they land in.

    A 32-bit all-ones literal zero-extends into 64 bits. A literal wider than its size prefix truncates, often with only a lint note.

    Common pitfall

    Averaging with (a+b)>>1 truncates the carry before the shift halves. Likewise 32'hFFFF_FFFF into 64 bits leaves the upper half zero: use '1 for a full fill.

    Testbench

    One block replays the averaging puzzle with all three fixes. A second block checks fill, extension and truncation.

    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
    module tb_avg_width;
      timeunit 1ns; timeprecision 1ps;
      logic [7:0] a, b;
      logic [7:0] avg_bad, avg_ok1, avg_ok2, avg_ok3;
      logic [8:0] sum;
      int errors = 0;
      initial begin
        a = 8'd200;
        b = 8'd200;
        avg_bad = (a + b) >> 1;
        avg_ok1 = (9'(a) + b) >> 1;
        avg_ok2 = (a + b + 9'd0) >> 1;
        sum = a + b;
        avg_ok3 = sum >> 1;
        #1;
        if (avg_bad !== 8'd72) errors++;
        if (avg_ok1 !== 8'd200) errors++;
        if (avg_ok2 !== 8'd200) errors++;
        if (avg_ok3 !== 8'd200) errors++;
        if (sum !== 9'd400) errors++;
        if (errors != 0) $fatal(1, "FAIL: averaging widths disagree");
        $display("PASS: bad average is 72 while all three wide fixes give 200");
        $finish;
      end
    endmodule
    Terminal
    PASS: bad average is 72 while all three wide fixes give 200
    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
    module tb_literal_traps;
      logic [63:0] mask64;
      logic [15:0] r;
      logic [39:0] big;
      int errors = 0;
      initial begin
        mask64 = ~0;
        if (mask64 !== 64'hFFFF_FFFF_FFFF_FFFF) errors++;
        mask64 = 32'hFFFF_FFFF;
        if (mask64 !== 64'h0000_0000_FFFF_FFFF) errors++;
        mask64 = '1;
        if (mask64 !== 64'hFFFF_FFFF_FFFF_FFFF) errors++;
        r = 16'd70000;
        if (r !== 16'd4464) errors++;
        r = 1 << 16;   // 1 is 32-bit, but the 16-bit target truncates 65536 to 0
        if (r !== 16'h0000) errors++;
        big = 1 << 35; // 40-bit context widens the shift operand, so 2^35 fits
        if (big !== 40'h8_0000_0000) errors++;
        if (errors != 0) $fatal(1, "FAIL: literal width checks disagree");
        $display("PASS: 70000 truncates to 4464 and fill beats zero-extension");
        $finish;
      end
    endmodule
    Terminal
    PASS: 70000 truncates to 4464 and fill beats zero-extension

    Interview

    Likely questions: the wrong average, the reach of the assignment target, and the self-determined short list.

    • `(a+b)>>1` loses the carry first: widen the addition context to fix it.

    • The target participates in context width: the same right-hand side differs under other widths.

    • Self-determined positions are the shift amount, concat operands, replication counts and comparison results.

    Check yourself The third fix stores a + b in a 9-bit sum first. Why does that need no cast?

    The 9-bit target makes the addition's context 9 bits, so 400 fits. Halving sum afterwards gives 200.

    Check yourself big = 1 << 35 works with a 40-bit target, yet r = 1 << 16 gives 0 with a 16-bit target. Why?

    The shift left operand is context-determined. The 40-bit context widens 1 to 40 bits before shifting, so 2^35 fits. The 16-bit case computes 65536 at 32 bits and truncates it to 0.

    Remember

    • Widths are fixed before any value is computed; the assignment target counts.

    • Shift amounts, concatenation operands and replication counts keep their own width.

    • Unsized decimals are 32-bit signed; '0 and '1 fill whatever width they land in.

    Rule of thumb: widen the operation that overflows, not the result after it.

    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.

    Icarus 13 prints a truncation warning for 16'd70000; that truncation is what the bench checks.

    Related topics

    • Next topic: Packages, Typedefs & Compilation

    Previous

    25 / 259

    Next: Packages, Typedefs & Compilation

    Up next

    Up nextPackages, Typedefs & Compilation

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Signed & Unsigned Arithmetic
    • Equality, Case Equality & Wildcards
    • unique, unique0 & priority