SystemVerilog Basics

Chapter 1 of 16

Why SystemVerilog? From Verilog to a Verification Language

Understand the core difference between describing hardware and executing software, where plain Verilog falls short for verification, what SystemVerilog actually adds, and the design-vs-verification-code mental model that runs through this whole series.

If you've written software before, glancing at a block of code probably makes you automatically "run" it in your head: this line executes, then the next, branches jump, loops exit. That instinct will trip you up in hardware. A hardware description language (HDL) doesn't describe "do this, then that" — it describes a bunch of circuitry that all exists and operates at the same time. The statements inside a module are describing parallel pieces of logic, not a sequential stream of instructions.

Holding onto that shift is the first step to learning SystemVerilog.

What Verilog is

Verilog is one of the earliest widely-used hardware description languages, used to describe digital circuits: modules, ports, wire, reg, and statements like assign and always that describe a circuit's structure and behavior. Design engineers use it to write code that synthesis tools turn into real gates and flip-flops.

This isn't a Verilog tutorial — you don't need to master it right now, later chapters will fill in the syntax you need as you go. For now, the important thing is just this: Verilog was born to describe circuits.

Where plain Verilog falls short for verification

Designing a circuit is only step one — you still have to prove it's correct. That's verification: applying stimulus to a circuit and checking that its behavior matches expectations.

The problem is Verilog has almost no language features built for that job: no real classes or objects, no built-in randomized stimulus generation, and reusing checking logic across projects is awkward. So verification engineers improvised — some wrapped driving logic in classes some other language, others just piled code into initial blocks. The result: every project's testbench looked different, was hard to reuse, and was expensive for newcomers to pick up.

What SystemVerilog actually is

SystemVerilog (IEEE 1800) is a superset of Verilog that extends it in two directions at once:

  • Stronger design (RTL) syntax — things like the logic type and clearer procedural blocks such as always_comb/always_ff, both covered in later chapters.
  • A whole verification-oriented language layer — classes and objects, constrained randomization, assertions, and more. None of that code ever becomes a circuit; it exists purely to generate stimulus and check results during simulation.

In other words, SystemVerilog isn't "a different language" — it unifies design and verification under one syntax. This track covers both halves in order: first filling in the core design-side syntax, then moving into the object-oriented, randomization, and assertion features that belong to verification.

The core mental model: design code vs. verification code

This is the single most important idea in this chapter, and it runs through the entire series:

  • Design (RTL) code must be synthesizable — it eventually becomes real registers and gates, so it can only use the subset of the language that tools know how to map onto hardware.
  • Verification (testbench) code never becomes hardware — it's "software" that runs in a simulator to generate stimulus and check results, so it's free to use language features with no hardware equivalent at all: non-synthesizable loops, dynamically allocated memory, $display printing, file I/O, and more.

The same SystemVerilog syntax gets written with completely different intent and constraints, depending on whether you're wearing the "design engineer" hat or the "verification engineer" hat.

A minimal example

Here's a 2-to-1 mux's design code, side by side with a small testbench that verifies it:

// —— design code (RTL): gets synthesized into a real circuit ——
module mux2 (
  input  logic sel,
  input  logic a,
  input  logic b,
  output logic y
);
 
  always_comb begin
    y = sel ? b : a;
  end
 
endmodule
// —— verification code (testbench): never becomes any gate ——
module tb_mux2;
  logic sel, a, b, y;
 
  // Instantiate the design under test (DUT)
  mux2 dut (
    .sel(sel),
    .a(a),
    .b(b),
    .y(y)
  );
 
  initial begin
    a = 1'b0;
    b = 1'b1;
 
    sel = 1'b0;
    #10 $display("sel=%0b -> y=%0b (expected %0b)", sel, y, a);
 
    sel = 1'b1;
    #10 $display("sel=%0b -> y=%0b (expected %0b)", sel, y, b);
 
    $finish;
  end
endmodule

The initial block and #10 delay statements inside tb_mux2 have no counterpart in a real chip — they only exist in simulation, used to change inputs in sequence, wait for the circuit to settle, and print out the check results. That's exactly what "verification code" looks like.

A heads-up: the code above also uses a few things this chapter hasn't explained yet — the sel ? b : a question-mark pattern (the ternary operator), 1'b0/1'b1 "width'base value" numeric literals, and the #10 delay statement all get covered in one place next chapter; the initial/always_comb procedural blocks aren't formally covered until chapter 8. For now, it's enough to follow what this code is trying to do — you don't need to be able to write something identical yet.

How this actually runs

Turning a piece of SystemVerilog text into simulation results roughly goes through:

  1. Compile — check the syntax and translate the source into a form the simulator understands internally.
  2. Elaborate — expand module instantiations into the full circuit/testbench structure.
  3. Simulate — actually advance through time, execute the initial/always blocks, and produce waveforms and printed output.

Any simulator that supports SystemVerilog can run this flow, including free browser-based options like EDA Playground — this track isn't tied to any specific tool, just to understanding the language through the examples.

What's ahead

Now that the design-vs-verification distinction is out of the way, the next chapter fills in everyday basics — module declarations, numeric literals, $display — before moving on to logic/reg/wire and other data types, arrays, operators, procedural blocks, and tasks and functions. After that comes the verification-only features: object orientation, randomization, interfaces, and assertions — wrapping up with a small hand-built testbench that ties the whole track together and leads straight into the next track, UVM.

Which of the following correctly describes design (RTL) code vs. verification (testbench) code?

What is the main thing SystemVerilog adds on top of Verilog?