System Verilog Program Structure — module, program, initial
- Feb 6
- 4 min read
1. Introduction
Once you move beyond basic syntax in System Verilog, the first real confusion beginners face is:
Why does my simulation sometimes behave differently?
Why does System Verilog have both module and program?
What exactly does initial control?
This topic appears very frequently in interviews, but more importantly, it directly affects whether your simulation code is safe or unsafe.
A key point to understand early is this:
Code can compile, simulate, and still be logically unsafe.
This article explains why that happens and how System Verilog prevents it.
A key point to understand early is this:
Code can compile, simulate, and still be logically unsafe.
This article explains why that happens and how System Verilog prevents it.
2. Before Anything Else: Design vs Testbench (No Assumptions)
Before discussing keywords, we must clearly separate two ideas.
What is Design (DUT)?
Design (DUT – Design Under Test):
Represents real hardware
Meant to be synthesized
Describes how a circuit behaves
In System Verilog, design is written using module.
Example designs:
Flip-flop
Counter
ALU
What is a Testbench?
A testbench:
Is not hardware
Exists only in simulation
Drives inputs to the design
Observes outputs from the design
You can think of a testbench as a test program for hardware.
Even in simple testbenches, two roles exist:
Driver → applies inputs
Checker → checks outputs
System Verilog introduces the program block specifically for testbench code.
3. What Is the Difference Between module and program? (Snippet Target)
In System Verilog, a module models hardware and executes in the active simulation region, while a program models testbench behavior and executes after all modules to prevent race conditions.
In short:
module → hardware
program → testbench
Quick Comparison
Feature | module | program |
Purpose | Hardware modeling | Testbench modeling |
Synthesizable | Yes | No |
Simulation region | Active | Reactive |
Can race with DUT | Yes | No |
Intended use | Design | Verification |
4. Why This Distinction Matters (The Core Problem)
In simulation:
The design samples inputs on clock edges
The testbench drives those inputs
If both execute at the same simulation time, then:
Sometimes the design sees the old value
Sometimes the new value
The language gives no guarantee
This situation is called a race condition.
A race condition means behavior depends on execution order, not logic.
5. Where initial Fits (Important Clarification)
initial:
Runs once when simulation starts
Does not control priority
Executes according to where it is declared
Key rule:
initial defines what runs, module vs program defines when it runs.
6. Unsafe Code Example — Works but Is NOT Safe
This code may appear to work, but it is not guaranteed by the language.
❌ NOT SAFE — Testbench as module
module dut(input logic clk, input logic a, output logic y);
always_ff @(posedge clk)
y <= a;
endmodule
module testbench;
logic clk, a, y;
dut d1(.clk(clk), .a(a), .y(y));
initial clk = 0;
always #5 clk = ~clk;
initial begin
a = 0;
#10 a = 1; // changes exactly at posedge
#1 if (y !== 0) $error("RACE: y sampled new value");
#10 $finish;
end
endmodule
Why This Is Unsafe
DUT and testbench are both module
Both execute in the same simulation region
Order is not guaranteed
Behavior depends on simulator scheduling
No syntax error. No compile error. But language-level unsafe.
7. Safe Code Example — Deterministic and Correct
Now we fix only one thing: Move the testbench into a program.
✅ SAFE — Testbench as program
module dut(input logic clk, input logic a, output logic y);
always_ff @(posedge clk)
y <= a;
endmodule
program testbench;
logic clk, a, y;
dut d1(.clk(clk), .a(a), .y(y));
initial clk = 0;
always #5 clk = ~clk;
initial begin
a = 0;
#10 a = 1; // same timing as before
#1 if (y !== 0) $error("THIS SHOULD NEVER HAPPEN");
#10 $finish;
end
endprogram
Why This Is Safe
DUT (module) executes first
Testbench (program) executes after
Sampling happens before driving
Execution order is guaranteed by System Verilog
Same logic. Different scheduling. Only one is correct.
8.Key Takeaway (This Is the Core Insight)
Code can work and still be unsafe
Race conditions do not always show visible errors
System Verilog program exists to remove undefined behavior
This is why verification engineers care about this distinction.
9. Common Beginner Mistakes
Writing entire testbench using module
Assuming delays (#) fix race conditions
Believing initial controls execution order
Mixing synthesizable logic inside program
10. Interview Perspective
Interviewers are not asking:
“Does the code run?”
They are asking:
“Is the behavior guaranteed by the language?”
Strong one-line answer:
Program prevents race conditions between testbench and RTL.
11. Knowledge Check
Why can two modules race in simulation?
What guarantee does program provide?
Does initial decide execution order?
If you can answer these clearly, you understand the topic.
12. What’s Next
Now that execution order and structure are clear, the next confusion beginners face is:
“Which data types should I use, and why?”
👉 Next Article: SystemVerilog Data Types — logic, bit, reg, wire
This topic is both:
Highly interview-relevant
Critical for writing correct code
Comments