Basic Data Types in SystemVerilog

SystemVerilog extends Verilog with many new data types specifically designed for verification. Understanding when to use which type is crucial for writing efficient testbenches.

4-State vs 2-State Types

This is the most fundamental distinction in SystemVerilog data types. Understanding it is essential!

4-State Types (From Verilog)

Can hold four values: 0, 1, X (unknown), Z (high impedance)

  • reg - Original Verilog register type
  • logic - SystemVerilog replacement for reg and wire
  • integer - 32-bit signed, 4-state
  • time - 64-bit unsigned for simulation time

2-State Types (New in SystemVerilog)

Can only hold: 0 and 1. Faster simulation, less memory!

  • bit - Single bit (unsigned)
  • byte - 8 bits signed
  • shortint - 16 bits signed
  • int - 32 bits signed
  • longint - 64 bits signed
4-State vs 2-State Comparison
// 4-state: Can detect X/Z from RTL
logic [7:0] rtl_data;        // Use in RTL, can be X or Z
wire [7:0] data_bus;         // Tri-state bus

// 2-state: Faster, use in testbench when you don't need X/Z
bit [7:0] tb_data;           // Testbench variables
int counter;                 // Loop counters
bit [31:0] expected_value;   // Reference model values

// Important: 2-state defaults to 0, not X!
bit    b;     // Initialized to 0
logic  l;     // Initialized to X

// Converting 4-state to 2-state
logic [7:0] from_rtl = 8'b1010_xxxx;
bit [7:0]   in_tb = from_rtl;  // X becomes 0: 8'b1010_0000
                                // WARNING: Silent conversion!

When to Use Which?

Use Case Recommended Type Reason
RTL design logic Need to detect uninitialized X values
Testbench transactions bit Faster simulation, known values
Loop counters int 32-bit signed, fast
Randomization bit Required for rand/randc
Checking RTL output logic Need to detect X from DUT

Signed vs Unsigned

By default, bit and logic are unsigned. Use the signed keyword when you need signed arithmetic.

Signed vs Unsigned
// Unsigned (default)
bit [7:0] unsigned_val = 8'hFF;  // Value: 255

// Signed
bit signed [7:0] signed_val = 8'hFF;  // Value: -1

// Pre-defined signed types
byte      b = -10;    // 8-bit signed
shortint  s = -1000;  // 16-bit signed  
int       i = -50000; // 32-bit signed
longint   l = -1;     // 64-bit signed

// Mixing signed and unsigned - CAREFUL!
logic [3:0] a = 4'b1111;           // 15 (unsigned)
logic signed [3:0] b = 4'b1111;   // -1 (signed)

int result1 = a;  // Result: 15
int result2 = b;  // Result: -1 (sign extended)

// Comparison gotcha
if (a > b) $display("a > b");  // True! 15 > -1
// But if both were unsigned:
logic [3:0] c = 4'b1111;
logic [3:0] d = 4'b1111;
if (c > d) $display("c > d");  // False, they're equal

String Type

SystemVerilog has a built-in string type for dynamic text handling. Much easier than Verilog's character arrays!

String Operations
// Declaration and initialization
string name = "AHB_Transaction";
string empty_string;  // Empty by default

// String methods
string s = "Hello, VLSI World!";

$display("Length: %0d", s.len());           // 18
$display("Upper: %s", s.toupper());         // HELLO, VLSI WORLD!
$display("Lower: %s", s.tolower());         // hello, vlsi world!
$display("Char at 7: %s", s.getc(7));       // V

// Substring
string sub = s.substr(7, 10);  // "VLSI"

// Comparison
string a = "abc";
string b = "abd";
if (a < b) $display("a comes before b");  // Lexicographic

// Concatenation
string first = "Hello";
string last = "World";
string full = {first, " ", last};  // "Hello World"

// Formatting
int addr = 32'h1000;
string msg = $sformatf("Address: 0x%08h", addr);

// Convert to/from integer
string num_str = "12345";
int num = num_str.atoi();  // 12345

int value = 9876;
string val_str;
val_str.itoa(value);  // "9876"

// Useful in testbenches
function void print_transaction(string name, int addr, int data);
    $display("[%s] ADDR=0x%08h, DATA=0x%08h", name, addr, data);
endfunction

Enumerated Types

Enums give meaningful names to values, making code much more readable. Essential for state machines and protocol definitions.

Enum Examples
// Simple enum (values auto-assigned: 0, 1, 2...)
typedef enum {IDLE, SETUP, ACCESS, ERROR} apb_state_t;
apb_state_t current_state = IDLE;

// Enum with explicit values
typedef enum logic [1:0] {
    HTRANS_IDLE   = 2'b00,
    HTRANS_BUSY   = 2'b01,
    HTRANS_NONSEQ = 2'b10,
    HTRANS_SEQ    = 2'b11
} htrans_t;

htrans_t trans = HTRANS_NONSEQ;

// Enum methods
apb_state_t state = IDLE;

$display("Name: %s", state.name());    // "IDLE"
$display("Value: %0d", state);         // 0
$display("First: %s", state.first());  // IDLE
$display("Last: %s", state.last());    // ERROR
$display("Next: %s", state.next());    // SETUP
$display("Prev: %s", state.prev());    // ERROR (wraps!)

// Iterating through all enum values
for (apb_state_t s = s.first(); s != s.last(); s = s.next()) begin
    $display("State: %s = %0d", s.name(), s);
end

// Use in case statements (full coverage check)
always_comb begin
    case (current_state)
        IDLE:   next_state = SETUP;
        SETUP:  next_state = ACCESS;
        ACCESS: next_state = IDLE;
        ERROR:  next_state = IDLE;
        // Simulator warns if case not complete!
    endcase
end

// Casting between enum and integer
htrans_t t = htrans_t'(2);  // HTRANS_NONSEQ
int val = int'(t);          // 2

Typedef

typedef creates new type names, making code cleaner and more maintainable.

Typedef Examples
// Create type aliases
typedef bit [31:0] addr_t;
typedef bit [63:0] data_t;
typedef logic [7:0] byte_t;

// Use them
addr_t address;
data_t data;

// For parameterized types
typedef byte_t memory_t [0:1023];  // Array of 1024 bytes
memory_t my_memory;

// Complex typedef
typedef struct packed {
    bit [31:0] addr;
    bit [31:0] data;
    bit [3:0]  be;
    bit        write;
} transaction_t;

transaction_t txn;
txn.addr = 32'h1000;
txn.data = 32'hDEADBEEF;
txn.be = 4'b1111;
txn.write = 1;

// Forward declaration (for recursive structures)
typedef class my_class;

class another_class;
    my_class ref_to_my_class;
endclass

class my_class;
    another_class ref_to_another;
endclass

Complete Data Types Reference

Type Bits States Sign Default
bit 1 2 Unsigned 0
logic 1 4 Unsigned X
byte 8 2 Signed 0
shortint 16 2 Signed 0
int 32 2 Signed 0
longint 64 2 Signed 0
integer 32 4 Signed X
real 64 - Signed 0.0
string Dynamic - - ""

Common Interview Questions

  1. What is the difference between logic and reg?

    Functionally they're similar (both 4-state), but logic can be driven by both continuous assignment and procedural blocks, while reg is only for procedural. In modern SystemVerilog, use logic everywhere in RTL.

  2. When should you use bit vs logic in testbench?

    Use bit for testbench variables where you don't need X/Z detection (transactions, counters). Use logic when sampling signals from DUT where you might need to detect undefined values.

  3. What happens when you assign X to a bit type?

    X (and Z) silently converts to 0. This can hide bugs! Always check RTL outputs with logic type first.

  4. What's the advantage of typedef?

    Code readability, consistent type usage across files, easier to change types globally, and self-documenting code.