HomeSystemVerilog VerificationDay 2 — Data Types
DAY 2 · SV FUNDAMENTALS

SystemVerilog Data Types Explained — logic, bit, int, byte, real

By EcrioniX · Updated Jun 12, 2026

SystemVerilog data types are the foundation of everything you write — RTL and testbench alike. The choice between logic and bit, between 4-state and 2-state, between int and integer, has real consequences for simulation correctness and speed. This lesson covers every SV data type with concrete examples and tells you exactly when to use each one.

4-STATE TYPES 2-STATE TYPES logic wire reg integer time Values: 0, 1, X (unknown), Z (high-Z) Use for: RTL signals, DUT ports, interfaces bit byte shortint int longint Values: 0 and 1 only — no X or Z Use for: TB data structures, loop counters, rand vars

What are the two categories of SystemVerilog data types?

Every SV type falls into one of two categories: 4-state (can hold 0, 1, X, Z) or 2-state (can hold only 0 or 1). This distinction matters far more than it might seem. In a real chip simulation, an uninitialized register has the value X — unknown. If you use 2-state types everywhere in your testbench, that X silently becomes 0, hiding initialization bugs. Professional verification engineers use logic for anything connected to the DUT and bit only for pure testbench constructs.

What is X (unknown) and why does it matter in simulation?

An X value in SystemVerilog means the simulator cannot determine whether the signal is 0 or 1. X appears when:

The critical insight: X should propagate. If a register is never reset and you use it in logic, the output should become X — making the bug visible in simulation. If you use bit types, X silently snaps to 0 and the bug is hidden. This is called the X-pessimism vs X-optimism debate, and using logic in RTL is the professional standard.

How do you check for X or Z values with $isunknown?

Use $isunknown(expr) to test whether any bit of a value is X or Z. Use $isxunknown(expr) to test for X only (ignoring Z). Both return 1 if true, 0 if false. They are invaluable in testbench checkers:

isunknown_demo.sv
logic [7:0] dut_data;

// Check before sampling in scoreboard
task check_output(logic [7:0] actual, logic [7:0] expected);
  if ($isunknown(actual)) begin
    $error("DUT output contains X or Z: %0b", actual);
    return;
  end
  if (actual !== expected)  // !== is 4-state inequality (X != X is false)
    $error("Mismatch: got %0h, expected %0h", actual, expected);
  else
    $display("PASS: %0h", actual);
endtask

// In assertions: flag if data bus goes X during valid transfer
assert property (@(posedge clk) (valid && !$isunknown(data)))
  else $error("Data bus is X during valid transfer!");

Common pitfall: == vs ===

In SystemVerilog, == is 2-state equality (X == X returns X, which is treated as false). === is 4-state equality (X === X returns 1). Use === when you want to check for exact X/Z matches. Use !== for 4-state inequality. In testbench checkers, !== is usually what you want — it will flag mismatches even when one value is X.

Complete SystemVerilog data types reference table

TypeStatesWidthSigned?Typical Use
logic4-state (0,1,X,Z)1 bit (scalar or vector)UnsignedRTL signals, DUT ports, interfaces
wire4-state1 bit or vectorUnsignedNets driven by continuous assign; legacy
reg4-state1 bit or vectorUnsignedLegacy Verilog — replaced by logic in SV
integer4-state32 bitsSignedVerilog loop counters; use int in SV
time4-state64 bitsUnsignedSimulation time storage
bit2-state (0,1)1 bit or vectorUnsignedTB data structures, rand variables
byte2-state8 bitsSignedASCII characters, byte-wide data
shortint2-state16 bitsSignedSmall signed integers
int2-state32 bitsSignedLoop counters, indices, counts in TB
longint2-state64 bitsSignedTimestamps, large counters
real2-state64-bit IEEE 754SignedFloating-point math, timing calculations
shortreal2-state32-bit IEEE 754SignedSingle-precision floating point
stringN/ADynamicN/AMessages, filenames, TB logging
voidN/A0 bitsN/AFunction return type when no value returned

When should you use logic vs bit in a testbench?

The rule is simple and consistent in industry: use logic for everything that touches the DUT, and use bit/int/byte for pure testbench data structures that never connect to RTL signals.

The professional rule of thumb

How does the SystemVerilog string type work?

The string type in SystemVerilog is a dynamically-sized sequence of characters. Unlike C strings, SV strings are not null-terminated arrays — they are first-class objects with built-in methods. They are simulation-only (not synthesizable).

String methods every verification engineer needs

MethodReturnsDescription
.len()intNumber of characters in the string
.toupper()stringConvert all characters to uppercase
.tolower()stringConvert all characters to lowercase
.substr(i,j)stringExtract substring from index i to j (inclusive)
.itoa(i)voidConvert integer i and store in string
.atoi()integerConvert string to integer (stops at first non-digit)
.getc(i)byteGet the ASCII value of character at index i
.putc(i,c)voidSet character at index i to ASCII value c
== / !=bitString comparison (case-sensitive)
{s1,s2}stringConcatenation using {} operator

How does type casting work in SystemVerilog?

Static casting with the type'() syntax

Static casting converts between compatible types at compile time. The syntax is TargetType'(expression). If the target type is narrower, the value is truncated. If wider, it is zero-extended (or sign-extended for signed types).

Dynamic casting with $cast

Dynamic casting with $cast(dest, src) is used primarily for polymorphic class handles. It returns 1 on success and 0 on failure, letting you handle the failure gracefully. You must use $cast when downcasting a base class handle to a derived class handle — static casting will not work.

What are signed vs unsigned pitfalls in SystemVerilog?

This is one of the most common sources of subtle verification bugs. The key rules are:

Signed/unsigned mixing bug

If you compare byte b = -1 (which is 8'hFF = 255 unsigned) with bit [7:0] x = 8'hFF using ==, they compare equal. But if you promote both to int first and one is signed (-1) and one is unsigned (255), you get different values. Always be explicit: use int for signed arithmetic in testbenches and bit [N:0] for unsigned counters with a guard bit.

Complete data types demonstration code

data_types_demo.sv
// ============================================================
// data_types_demo.sv — SystemVerilog Data Types Demonstration
// EcrioniX · SV Verification Course · Day 2
// ============================================================
module data_types_demo;

  // ----- 4-STATE TYPES -----
  logic        flag;          // 1-bit 4-state, starts as X
  logic [7:0]  data_byte;     // 8-bit vector, starts as 8'bXXXXXXXX
  logic [31:0] data_word;     // 32-bit vector
  integer      legacy_int;    // 32-bit 4-state signed (Verilog legacy)

  // ----- 2-STATE TYPES -----
  bit          b_flag;        // 1-bit 2-state, starts as 0
  bit  [7:0]   b_byte;        // 8-bit 2-state vector
  byte         ascii_char;    // 8-bit signed (byte = shortint restricted)
  shortint     s_int;         // 16-bit signed
  int          counter;       // 32-bit signed — use for loop counters
  longint      timestamp;     // 64-bit signed
  int unsigned u_count;       // explicitly unsigned int

  // ----- FLOATING POINT -----
  real         freq_hz;       // 64-bit double-precision float
  shortreal    duty_cycle;    // 32-bit single-precision float

  // ----- STRING TYPE -----
  string       msg;           // dynamically-sized, simulation-only
  string       logfile;

  initial begin
    // ---- 4-state: observe initial X ----
    $display("flag initial    = %b  (X)", flag);    // prints x
    $display("data_byte init  = %h  (XX)", data_byte); // prints xx

    flag      = 1'b1;
    data_byte = 8'hA5;
    $display("flag after assign = %b", flag);       // 1
    $display("data_byte = 8'h%0h", data_byte);      // A5

    // ---- $isunknown / $isxunknown ----
    logic [3:0] partial;
    partial = 4'bXX10;
    if ($isunknown(partial))
      $display("partial has X or Z bits: %b", partial);
    if ($isxunknown(partial))
      $display("partial has X bits (not just Z): %b", partial);

    // ---- 2-state: starts at 0, not X ----
    $display("b_flag initial  = %b  (NOT X — silently 0)", b_flag);

    // ---- Integer arithmetic ----
    counter   = 0;
    timestamp = 64'd1_000_000;
    for (counter = 0; counter < 5; counter++) begin
      timestamp += 100;
    end
    $display("timestamp after loop = %0d", timestamp); // 1000500

    // ---- Floating point ----
    freq_hz    = 100.0e6;   // 100 MHz
    duty_cycle = 0.5;       // 50%
    $display("Period = %0.2f ns", 1.0e9 / freq_hz);  // 10.00 ns
    $display("High time = %0.2f ns", (1.0e9 / freq_hz) * duty_cycle); // 5.00 ns

    // ---- String methods ----
    msg = "Hello SystemVerilog";
    $display("Length    : %0d", msg.len());           // 19
    $display("Upper     : %s",  msg.toupper());       // HELLO SYSTEMVERILOG
    $display("Lower     : %s",  msg.tolower());       // hello systemverilog
    $display("Substr 6-7: %s",  msg.substr(6,12));    // SystemV (indices 6..12)
    logfile = {"run_", msg.substr(0,4), ".log"};
    $display("Logfile   : %s",  logfile);             // run_Hello.log

    // ---- Static casting ----
    real r_val;
    int  i_val;
    r_val = 3.9;
    i_val = int'(r_val);          // truncates to 3, NOT rounds
    $display("int'(3.9) = %0d", i_val);    // 3

    data_byte = logic [7:0]'(counter + 1000); // truncate to 8 bits
    $display("truncated = 8'h%0h", data_byte); // low 8 bits of 1005

    // ---- Signed vs Unsigned pitfall ----
    byte  signed_b = -1;    // stored as 8'hFF
    bit [7:0] unsigned_b;
    unsigned_b = 8'hFF;     // 255 unsigned
    // Both hold 8'hFF, but interpreted differently in arithmetic
    $display("signed byte  -1 as unsigned: %0d", unsigned'(signed_b)); // 255
    $display("int of signed -1: %0d", int'(signed_b));                // -1

    // ---- 4-state equality vs 2-state equality ----
    logic [3:0] a = 4'bXX10;
    logic [3:0] b = 4'bXX10;
    $display("a == b  (2-state): %b", (a == b));  // 0 (X makes result X→0)
    $display("a === b (4-state): %b", (a === b)); // 1 (exact match inc X)
    $display("a !== b (4-state): %b", (a !== b)); // 0

    $display("\nAll data types demo complete.");
    $finish;
  end

endmodule

Day 2 takeaways

Frequently Asked Questions

What is the difference between logic and bit in SystemVerilog?

logic is 4-state (0, 1, X, Z) — use it for RTL signals and anything connected to the DUT so that uninitialized or floating signals show as X. bit is 2-state (0, 1 only) — use it for testbench-only data structures, loop counters, and random variables in classes. 2-state types simulate ~10-20% faster and have no X/Z to worry about.

When should I use bit vs logic in SystemVerilog?

Use logic for interface signals, DUT ports, clocking block signals, and any wire where X propagation is meaningful. Use bit, int, byte for scoreboard entries, packet fields, loop counters, and rand variables in constraint classes. If in doubt on a testbench signal that touches the DUT: logic.

What is an X value in SystemVerilog?

X means unknown — the simulator cannot determine whether the bit is 0 or 1. It appears on uninitialized registers, from bus contention, or when Z is sampled without a pull. X propagates through logic — good designs expose bugs this way. Use $isunknown(signal) to detect X/Z and === for 4-state comparison.

How does casting work in SystemVerilog?

Static cast: int'(3.9) truncates a real to int (gives 3, not 4). logic [7:0]'(wide_int) takes the low 8 bits. Dynamic cast: $cast(derived_handle, base_handle) downcasts a polymorphic class handle and returns 1 on success. Always use $cast for class handles — never static cast.

What is void in SystemVerilog?

void is the return type for a function that performs an action but returns no value — equivalent to C's void. Use function void check_result(...) instead of task when the function consumes no simulation time and you want the compiler to enforce the no-time-consumption rule.

Previous
← Day 1: Why SystemVerilog?

← Full course roadmap