A set of Verilog statements are usually executed sequentially in a simulation. These statements are placed inside a procedural block. There are mainly two types of procedural blocks in Verilog - initial and always
Syntax
initial
[single statement]
initial begin
[multiple statements]
end
What is the initial block used for ?
An initial
block is not synthesizable and hence cannot be converted into a hardware schematic with digital elements. Hence initial blocks do not serve much purpose than to be used in simulations. These blocks are primarily used to initialize variables and drive design ports with specific values.
When does an initial block start and end ?
An initial
block is started at the beginning of a simulation at time 0 unit. This block will be executed only once during the entire simulation. Execution of an initial
block finishes once all the statements within the block are executed.
The image shown above has a module
called behave which has two internal signals called a and b. The initial
block has only one statement and hence it is not necessary to place the statement within begin
and end
. This statement assigns the value 2'b10 to a when the initial block is started at time 0 units.
What happens if there is a delay element ?
The code shown below has an additional statement that assigns some value to the signal b. However this happens only after 10 time units from execution of previous statement. This means that a is assigned first with the given value and then after 10 time units, b is assigned to 0.

How many initial blocks are allowed in a module ?
There are no limits to the number of initial
blocks that can be defined inside a module.
The code shown below has three initial
blocks all of which are started at the same time and run in parallel. However, depending on the statements and the delays within each initial block, the time taken to finish the block may vary.
In this example, the first block has a delay of 20 units, while the second has a total delay of 50 units (10 + 40) and the last block has a delay of 60 units. Hence the simulation takes 60 time units to complete since there is atleast one initial block still running until 60 time units.
$finish
is a Verilog system task that tells the simulator to terminate the current simulation.
If the last block had a delay of 30 time units like shown below, the simulation would have ended at 30 time units thereby killing all the other initial
blocks that are active at that time.
initial begin
#30 $finish;
end
Synthesis
An initial
block is not synthesizable.
- What is a hardware schematic ?
- What is a Hardware Description Language ?
- What is meant by design functionality ?
- What is verification ?
- Sections of Verilog code
- Testbench code
- Sequential Logic with always
- JK Flip Flop
- Modulo-10 counter
- 4bit Left Shift Register
- Verilog if-else-if
- Syntax
- Hardware Implementation
- Examples
- UVM Field Macros
A digital element such as a flip-flop can be represented with combinational gates like NAND and NOR. The functionality of a flip-flop is achieved by the connection of a certain set of gates in a particular manner. How the gates have to be connected is usually figured out by solving K-map from the truth table. The truth table is nothing but a table that tells us what inputs combine together to give what values of output. Shown in the image below is an electronic circuit that represents a D-flip flop and the corresponding truth table. The output q becomes 1 only when rstn and d are both having a value of 1.
What is a hardware schematic ?
A hardware schematic is a diagram that shows how the combinational gates should be connected to achieve a particular hardware functionality. In this case, it is the set of NAND gates connected like shown towards the left in the image above. However, if we know what values of inputs contribute to make the output have a value of 1, then we can essentially hide the internal details of the connections and encapsulate it into a black-box. This block provides us with certain inputs and outputs that is similar to the hardware schematic made up of combinational gates.
What is a Hardware Description Language ?
It will be easier if we can describe how this block should behave and then let software tools convert that behavior into actual hardware schematic. The language that describes hardware functionality is called Verilog, and is classified as a Hardware Description Language.
What is meant by design functionality ?
Some typical behavioral requirements for a D-flip-flop are :
- clock should be an input to the flop
- if the active-low reset is 0, then the flop should reset
- if the active-low reset is 1, then the flop output 'q' should follow input 'd'
- output 'q' should get a new value only at the posedge of clock
An important question comes to mind : how do we know whether the behavior described in Verilog accurately reflects the intended behavior of the design ?
What is verification ?
This is checked by different methods and is collectively called as verification. The most common and widely practiced method of verification is circuit simulation. There are software tools to understand how a hardware described in Verilog should behave and provide various input stimuli to the design model. The output of the design is then checked against expected values to see if the design is functionally correct.
All simulations are performed by EDA (Electronic Design Automation) software tools and the Verilog design RTL is placed inside an entity called as testbench. Within the testbench, various tests provide different stimuli to the design. Such a testbench is shown in the image below.
Sections of Verilog code
All behavior code should be described within the keywords module
and endmodule
. Rest of the design code would mostly follow the given template.
Verilog sections template
- Module definition and port list declaration
- List of input and output ports
- Declaration of other signals using allowed Verilog data types
- Design may depend on other Verilog modules and hence their instances are created by module instantiations
- The actual Verilog design for this module that describes its behavior
module [design_name] ( [port_list] );
[list_of_input_ports]
[list_of_output_ports]
[declaration_of_other_signals]
[other_module_instantiations_if_required]
[behavioral_code_for_this_module]
endmodule
Example
The code shown below describes the behavior of a D type flip-flop. The first few lines declare a new module
called dff and define the input and output ports. The only other signal used in this design is q and is declared next. Since this is a simple design, it does not depend on any other module and hence there are no module instantiations. The always
block describes how the hardware should behave during certain events and hence is behavioral code.
// "dff" is the name of this module
module dff ( input d, // Inputs to the design should start with "input"
rstn,
clk,
output q); // Outputs of the design should start with "output"
reg q; // Declare a variable to store output values
always @ (posedge clk) begin // This block is executed at the positive edge of clk 0->1
if (!rstn) // At the posedge, if rstn is 0 then q should get 0
q <= 0;
else
q <= d; // At the posedge, if rstn is 1 then q should get d
end
endmodule // End of module
Testbench code
The testbench is the Verilog container module that allows us to drive the design with different inputs and monitor its outputs for expected behavior. In the example shown below, we have instantiated the flop design illustrated above and connected it with testbench signals denoted by tb_*. These testbench signals are then assigned certain values and are eventually driven as inputs to the design.
module tb;
// 1. Declare input/output variables to drive to the design
reg tb_clk;
reg tb_d;
reg tb_rstn;
wire tb_q;
// 2. Create an instance of the design
// This is called design instantiation
dff dff0 ( .clk (tb_clk), // Connect clock input with TB signal
.d (tb_d), // Connect data input with TB signal
.rstn (tb_rstn), // Connect reset input with TB signal
.q (tb_q)); // Connect output q with TB signal
// 3. The following is an example of a stimulus
// Here we drive the signals tb_* with certain values
// Since these tb_* signals are connected to the design inputs,
// the design will be driven with the values in tb_*
initial begin
tb_rsnt <= 1'b0;
tb_clk <= 1'b0;
tb_d <= 1'b0;
end
endmodule
A previous article showed different examples of using an always
block to implement combinational logic. An always
block is also mainly used to implement sequential logic which has memory elements like flip flops that can hold values.
JK Flip Flop
A JK flip flop is one of the many types of flops used to store values and has two data inputs j and k along with one for reset rstn and another for clock clk . The truth table for a JK flop is shown below and is typically implemented using NAND gates.
rstn | j | k | q | Comments |
---|---|---|---|---|
0 | 0 | 0 | 0 | When reset is asserted, output is always zero |
1 | 0 | 0 | Hold value | When both j and k are 0, output remains the same as before |
1 | 0 | 1 | 1 | When k=1, output becomes 1 |
1 | 1 | 0 | 0 | When k=0, output becomes 0 |
1 | 1 | 1 | Toggle value | When j=1,k=1 output toggles current value |
The behavioral Verilog code for a JK flip-flop can be written as shown below
module jk_ff ( input j, // Input J
input k, // Input K
input rstn, // Active-low async reset
input clk, // Input clk
output reg q); // Output Q
always @ (posedge clk or negedge rstn) begin
if (!rstn) begin
q <= 0;
end else begin
q <= (j & ~q) | (~k & q);
end
end
endmodule
Testbench
First declare all variables used in the testbench and start a clock using a simple always
block that can be driven to the design. Then instantiate the design and connect its ports with corresponding testbench variables. Note that q is of type wire
because it is connected to an output of the design which will be actively driving it. All other inputs to the design are of type reg
so that they can be driven within a procedural block such as initial
.
The stimulus first initializes all inputs to the design to zero and then de-asserts reset after some time. A for
loop is used to drive different values to j and k which are driven at random times. Once the loop is done, wait for some more time and finish the simulation.
module tb;
// Declare testbench variables
reg j, k, rstn, clk;
wire q;
integer i;
reg [2:0] dly;
// Start the clock
always #10 clk = ~clk;
// Instantiate the design
jk_ff u0 ( .j(j), .k(k), .clk(clk), .rstn(rstn), .q(q));
// Write the stimulus
initial begin
{j, k, rstn, clk} <= 0;
#10 rstn <= 1;
for (i = 0; i < 10; i = i+1) begin
dly = $random;
#(dly) j <= $random;
#(dly) k <= $random;
end
#20 $finish;
end
endmodule
Note from the simulation wave that at the posedge of clock, output q changes value based on the state of inputs j and k as given in the truth table.

Modulo-10 counter
Modulus(MOD) counters simply count upto a certain number before rolling back to zero. A MOD-N counter will count from 0 to N-1 and then roll back to zero and start counting again. Such counters typically require log2N number of flops to hold the count value. Shown below is the Verilog code for a MOD-10 counter that keeps counting up at every clock clk as long as reset rstn is deasserted.
Verilog parameters can be used to make a MOD-N counter which is more scalable.
module mod10_counter ( input clk,
input rstn,
output reg[3:0] out);
always @ (posedge clk) begin
if (!rstn) begin
out <= 0;
end else begin
if (out == 10)
out <= 0;
else
out <= out + 1;
end
end
endmodule
Testbench
The testbench first declares some variables that can be assigned some values and driven to the design inputs. The counter module is then instantiated and connected with the testbench signals which are later driven with some values in the stimulus. Since the counter also requires a clock, the testbench clock is modeled with an always
block. The stimulus simply sets default values at time 0ns, then deasserts reset after 10ns and the design is allowed to run for some time.
module tb;
reg clk, rstn;
reg [3:0] out;
mod10_counter u0 ( .clk(clk), .rstn(rstn), .out(out));
always #10 clk = ~clk;
initial begin
{clk, rstn} <= 0;
#10 rstn <= 1;
#450 $finish;
end
endmodule
See that the counter module counts from zero to 9, rolls over to zero and starts counting again.

4bit Left Shift Register
Shown below is a 4-bit left shift register that accepts an input d into LSB and all other bits will be shifted left by 1. For example, if d equals zero and the initial value of the register is 0011, it will become 0110 at the next edge of the clock clk .
module lshift_4b_reg ( input d,
input clk,
input rstn,
output reg [3:0] out
);
always @ (posedge clk) begin
if (!rstn) begin
out <= 0;
end else begin
out <= {out[2:0], d};
end
end
endmodule
Testbench
The testbench follows a similar template like the one shown before where some variables are declared, design module is instantiated and connected with the testbench signals. Then a clock is started and the stimulus is driven to the design using an initial
block. In this testbench example, different values of d has to be exercised and hence a for
loop is used to iterate 20 times and apply random values to the design.
module tb;
reg clk, rstn, d;
wire [3:0] out;
integer i;
lshift_4b_reg u0 ( .d(d), .clk(clk), .rstn(rstn), .out(out));
always #10 clk = ~clk;
initial begin
{clk, rstn, d} <= 0;
#10 rstn <= 1;
for (i = 0; i < 20; i=i+1) begin
@(posedge clk) d <= $random;
end
#10 $finish;
end
endmodule
Note that each bit is shifted to the left by 1 and the new value of d is applied to LSB.

This conditional statement is used to make a decision on whether the statements within the if
block should be executed or not.
- If the expression evaluates to true (i.e. any non-zero value), all statements within that particular
if
block will be executed - If it evaluates to false (zero or 'x' or 'z'), the statements inside
if
block will not be executed - If there is an else statement and expression is false then statements within the
else
block will be executed.
Syntax
If multiple statements need to be placed inside the if or else part, it needs to be enclosed within begin
and end
.
if ([expression])
Single statement
// Use "begin" and "end" blocks for more than 1 statements
if ([expression]) begin
Multiple statements
end
// Use else to execute statements for which expression is false
if ([expression]) begin
Multiple statements
end else begin
Multiple statements
end
// if-else-if style to check for more expressions if the previous one doesn't match
if ([expression 1])
Single statement
else if ([expression 2]) begin
Multiple Statements
end else
Single statement
Hardware Implementation
if without else
if
without an else
part implies that the value remain unchanged for any condition that does not satisfy the expression inside if
.
module des ( input en,
input d,
output reg q);
always @ (en or d)
if (en)
q = d;
endmodule
Value of output q is updated whenever d or en changes in value.

if with else
Output q will get the value of input d at the positive edge of clock if rstn is high and describes the behavior of a D flop.
module dff ( input clk,
input rstn,
input d,
output reg q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else
q <= d;
end
endmodule
Note that the synthesized output indicates a flop with an output q .

if else if
In the following example, the design module has a 4-bit output q that is incremented when mode is 1 and decrements when mode is 2 with if else
construct. Note that the description does not specify what has to be done if mode is 0 or 3 which are valid values for a 2-bit variable. It is assumed that the circuit does nothing when mode is 1 and 3, but maintain exiting value of q . It is not recommended to leave such ambiguity in real design code, but is shown here to highlight the possibility.
module des ( input [1:0] mode,
input clk,
input rstn,
output reg [3:0] q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else begin
if (mode == 1)
q <= q + 1;
else if (mode == 2)
q <= q - 1;
end
end
endmodule
The synthesized output may differ with availability of cells for a given technology library
Shown below is the synthesized output and it is worth to note that q got implemented as a 4-bit flop which has a pin CE to enable the flop. Note that this flop is enabled only when mode is 1 or 2 and not for other values. Output q is fed back through an adder and subtractor block into the input of the same flop through a mux which is again controlled by mode .

Consider the same design from above with a 1-bit mode .
module des ( input mode,
input clk,
input rstn,
output reg [3:0] q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else begin
if (mode)
q <= q + 1;
else
q <= q - 1;
end
end
endmodule
In this case, a regular flop without a CE pin is used along with a few multiplexers to choose the correct signal based on value of mode .

Examples
if without else for single statement
module tb;
int a = 10;
initial begin
if (a == 10) // if block can have only one statement in it
$display ("a is found to be 10");
$display ("Always executed regardless of value of a"); // This statement is outside if block because
end
endmodule
ncsim> run a is found to be 10 Always executed regardless of value of a ncsim: *W,RNQUIE: Simulation is complete.
if without else for multiple statements
module tb;
int a = 10;
initial begin
if (a == 10) begin // if block has begin end keywords, and can support multiple statements
$display ("a is found to be 10");
$display ("Its good to get 10");
// Anything else can be done here until the "end" keyword
end
$display ("Always executed regardless of value of a"); // Statement is outside the if block, because of the closing "end" for the "begin" in if
end
endmodule
ncsim> run a is found to be 10 Its good to get 10 Always executed regardless of value of a ncsim: *W,RNQUIE: Simulation is complete.
if-else for single statement
module tb;
int a = 9;
initial begin
if (a == 10)
$display ("a is found to be 10"); // Is executed when "if" expression is True
else
$display ("a is NOT 10 :("); // Is executed when "if" expression is false
end
endmodule
ncsim> run a is NOT 10 :( ncsim: *W,RNQUIE: Simulation is complete.
if-else for multiple statements
module tb;
int a = 9;
initial begin
if (a == 10) begin
$display ("a is found to be 10"); // Is executed when "if" expression is True
// Can have more additional statements here
end else begin
$display ("a is NOT 10 :("); // Is executed when "if" expression is false
$display ("Why is a not 10 ?");
// Can have more additional statements here
end
end
endmodule
ncsim> run a is NOT 10 :( Why is a not 10 ? ncsim: *W,RNQUIE: Simulation is complete.
uvm_object
is the one of the base classes from where almost all UVM classes are derived. Typically configuration classes and data objects are derived from this class and are passed to different testbench components during the course of a simulation. There is often a need to copy, compare and print values in these classes.
UVM has included user-definable functions like do_copy() to copy, do_compare() to compare and do_print to print values in the class. But it can at times be an overhead to define all these methods for every class object created in the testbench. UVM comes with automation of core methods like copy, compare, pack, and print using `uvm_field_*
macros. This avoids the need for a user implementation of the do_* methods for each function.
These macros expand into complicated code that may not be run-time efficient and are not generally recommended !
The `uvm_field_*
macros are called inside `uvm_*_utils_begin
and `uvm_*_utils_end
macro blocks during factory registration.
ARG | Variable name compatible with the macro type |
FLAG | Default value is UVM_ALL_ON, ARG variable will be included in all data methods |
FLAG types
All possible values for FLAG are shown in the table below. Multiple flags can be OR'ed together with the |
operator to apply the required operations.
Value | Operation |
---|---|
UVM_ALL_ON | Set all operations on |
UVM_DEFAULT | [Recommended] Enables all operations. Additional flags may be turned off by default in the future versions |
UVM_NOCOPY | Do not copy this field |
UVM_NOCOMPARE | Do not compare this field |
UVM_NOPRINT | Do not print this field |
UVM_NOPACK | Do not pack/unpack this field |
UVM_REFERENCE | For object types, operate on handles only (like no deep copy) |
Print Options
The following flags can be OR'ed together to print in the required format.
Value | Operation |
---|---|
UVM_BIN | Print/record the field in binary |
UVM_DEC | Print/record the field in decimal |
UVM_UNSIGNED | Print/record the field in unsigned decimal |
UVM_OCT | Print/record the field in octal |
UVM_HEX | Print/record the field in hexadecimal |
UVM_STRING | Print/record the field in string format |
UVM_TIME | Print/record the field in time format |
typedef enum {FALSE, TRUE} e_bool;
class Child extends uvm_object;
string m_name;
logic[3:0] m_age;
`uvm_object_utils_begin(Child)
`uvm_field_string (m_name, UVM_ALL_ON)
`uvm_field_int (m_age, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name="Child");
super.new(name);
endfunction
endclass
class Parent extends uvm_object;
string m_name;
bit[15:0] m_age;
int m_numbers[$];
e_bool m_employed;
Child m_child;
`uvm_object_utils_begin(Parent)
`uvm_field_enum (e_bool, m_employed, UVM_ALL_ON)
`uvm_field_int (m_age, UVM_ALL_ON)
`uvm_field_queue_int (m_numbers, UVM_ALL_ON)
`uvm_field_string (m_name, UVM_ALL_ON)
`uvm_field_object (m_child, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name="Parent");
super.new(name);
endfunction
endclass
module tb;
initial begin
Parent p = Parent::type_id::create("Parent");
p.m_name = "Joey";
p.m_employed = TRUE;
p.m_age = 29;
p.m_numbers = '{1234, 5678, 9011};
p.m_child = new();
p.m_child.m_name = "Joey Jr";
p.m_child.m_age = 1;
p.print();
end
endmodule
ncsim> run UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES] ----------------------------------------- Name Type Size Value ----------------------------------------- Parent Parent - @1829 m_employed e_bool 32 TRUE m_age integral 16 'h1d m_numbers da(integral) 3 - [0] integral 32 'h4d2 [1] integral 32 'h162e [2] integral 32 'h2333 m_name string 4 Joey m_child Child - @1830 m_name string 7 Joey Jr m_age integral 4 'h1 ----------------------------------------- ncsim: *W,RNQUIE: Simulation is complete.