Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
40 views79 pages

Verilog Basic To Advance

Verilog is a hardware description language that simplifies hardware design through various abstraction levels, including gate, dataflow, switch, and behavioral levels. The document outlines Verilog's lexical conventions, data types, modules, ports, and the process of writing a testbench for functional verification. It emphasizes the importance of module instantiation and connection rules for effective design and testing in digital circuits.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
40 views79 pages

Verilog Basic To Advance

Verilog is a hardware description language that simplifies hardware design through various abstraction levels, including gate, dataflow, switch, and behavioral levels. The document outlines Verilog's lexical conventions, data types, modules, ports, and the process of writing a testbench for functional verification. It emphasizes the importance of module instantiation and connection rules for effective design and testing in digital circuits.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 79

Introduction

Verilog is a hardware description language (HDL) that describes hardware design functionality,
which is then converted into actual designs by synthesis tools. Verilog, based on C language, is
simpler than VHDL, which is based on Ada and Pascal languages.

Latest Verilog standard: IEEE Standard 1800-2023

Verilog defines four abstraction levels for module implementation. While the external view of a
module remains consistent, internal implementations differ based on the abstraction level:

 Gate Level: Describes the module using logic gates and their interconnections.
 Dataflow Level: Describes the module based on how data flows and processes within the
design.
 Switch Level: Implements the module using storage nodes and switches, representing the
lowest level of abstraction.
 Behavioral Level: Describes the module using algorithmic-level implementation, similar
to C programming, without hardware details.

These abstraction levels correspond to different modeling terminologies. Designs can combine
gate-level, dataflow, and behavioral modeling. The term RTL (Register Transfer Level) commonly
refers to a combination of dataflow and behavioral modeling in digital design.
Lexical Conventions
The lexical tokens used in Verilog and C language are similar. They are specified as numbers, strings,
identifiers, comments, delimiters, and keywords.
2.1-Numbers
The numbers can be specified in two ways
Types Syntax Description
sized <size>'<base_format><number> Size is explicitly specified
unsized ‘<base_format><number> Size is not specified, depending on the simulator and
machine it has the default number of bits. (Usually 32
bits)
Where, denotes
‘D or ‘d – Decimal format
‘B or ‘b – Binary format
‘H or ‘h – Hexadecimal format
‘O or ‘o – Octal format

Note:

1.In case of unsized numbers, if base_format is not specified, then it is treated as a decimal format.
2.Verilog provides two symbols ‘x’ to denote ‘unknown value’, and ‘z’ to denote ‘high impedance value’.
The underscore is used to separate bits for readability.
3.Negative numbers can be specified by using a minus sign before the size of a number and it is stored as
2’s complement of the number. It is illegal to use minus sign between <base_format> and <number>.
Example: -8’d5 (valid format and it is stored as 2’s complement of 5), 8’d-5 (Invalid format).
Examples:
sized unsized
8’d7 (8-bit decimal number) ‘d56 (32-bit decimal number)
20’hab_561d (20-bit hexadecimal ’h48fa (32-bit hexadecimal
number) number)
3’b101 (3-bit binary number) ‘o7a (32-bit octal number)

2.2-Comments
The comments are used for documentation and to describe any functionality in simple language. Two
ways to write comments

Comment types Description

Single line comment Single line comment starts with //


Multiple lines Multiple lines can be enclosed as comments within /* and */. Multiple lines can
comment not be nested.

Examples:
Single line comment
// This is a single-line comment
Multiple line comment
/* This is the first line.
This is the second line.
This is the third line.
... */
2.3-Whitespace

Verilog whitespace includes blank space(\b), tabs(\t) and newline(\n). They are ignored by Verilog except
when it separates the tokens. They are not ignored in the strings. They are used for code indentation as well.
Example:
module tb; reg [1:0] data; // observe spaces are given for
indentation. initial begin
$display("Hello\tWorld"); // \t is used between 'Hello' and 'World'.
end
endmodule

2.4-Operators
Verilog has three operator types: Unary, binary, and ternary
Operators Description Example

Unary Appear before the operand. Y = ~x;

Binary Appear between two operands. Y = x || y

Ternary Two separate operators appear to separate three operands Z = (a < b)?x:y;

Operators are explained in detail in the Verilog operators section.


2.5-Strings
A string is a set of characters that are typically mentioned within double-quotes (” “). Each character in a
string requires 1 byte to store. They can not be divided into multiple lines.
Example:
"Hello World" // Requires 11 bytes to store 11 characters.

2.6-Identifiers
The identifiers are the names given to the objects that can be referenced in the design.

1. They are case-sensitive and made up of alphanumeric characters (A to Z, a to z, 0 to 9), the


underscore ( _ ), and the dollar sign ($).
2. They can not start with the dollar sign ($), and numbers.
3. Escaped identifiers begin with backslash \ character and end with whitespace (tab, space,
or newline). They are meant to process literally. Example: \x+y

Examples
value_1 //Valid
$value_1 // Invalid
1_value // Invalid

2.7-Keywords
The keywords are special identifiers that are reserved to define the Verilog language construct. They are in
lowercase.
Examples: module, endmodule, initial, always, begin, end, case, wire, while, etc.
Data Types in Verilog
A storage format having a specific range or type is called data type. They can be divided into two groups.

1. Net type group: The net-type group represents physical connections between digital circuits. Ex.
wire, wand, wor, etc.
2. Variable type group: The variable type group represents the storage of values in digital circuits.
They are used as variables. Ex. reg, integer Verilog supports 4 types of logic values as

Logic values Description

1 Logic one, true condition

0 Logic zero, false condition

x Unknown value

z High impedance

Nets
Nets represent physical connections between the digital circuits. They are declared using keyword wire. The
term net is not a keyword, it is a group of data types such as wire, wand, wor, tri, etc.
wire a; //one-bit value as a single net.
wire [5:0] a; //net as a vecto

Note:

1. The net and wire terms are interchangeably used.


2. Usually, the default value of the net is z.

Registers
The registers represent data storage elements. It retains value till it is overridden. The registers are like a
placeholder, so they do not require a driver. Keyword used to declare a register data type is reg.
reg a; // single bit register
reg [5:0] a; // 6 bit register as a vector

Note:

1. Registers are similar to variables in the C language.


2. The default value of the reg is x.
Scalars and Vectors

Scalars: Single bit wire or register is known as a scalar.


wire a;
reg a;

Vectors: The nets or registers can be declared as vectors to represent multiple bit widths. If bit width is not
specified, it is a scalar.
wire [5:0] a;
reg [5:0] a;

Constants

The value of constants cannot be changed. It is read-only in nature.


Integer data type
The integers are general-purpose 32-bit register data types. They are declared by the ‘integer’ keyword.
integer count;

Real
The real data types can be constants or real register data types. They are declared using the ‘real’ keyword.
The real value is rounded off if they are assigned to integer data type,
real data = 3.14;

String
An ordered collection of characters is called a string. They can be stored in reg data type. Each character in
a string requires 1 byte (8 bits) for storage and is typically mentioned within double-quotes (” “).
reg [8*11:0] name = "Hello World"; // String "Hello World"

//requires 11 bytes space.

Time Verilog provides a time register to store simulation time. A time register is declared using the ‘time’
keyword and it has a width of at least 64 bits depending on the simulator and machine used.
time curr_time; // curr_time is a time variable.

Arrays
Verilog allows arrays of reg, time, integer, and vector register data types.
Example:

integer count [0:5];


count[2] // To access 2nd element in an array integer
two_D_arr [3:0][3:0]; // illegal time timestamp[1:5]; //
array of 5 timestamp variables.
reg [3:0] var[0:7]; // Array of 8 var and each var has 4 as bit width

Note: 1. Verilog does not allow the use of multidimensional arrays.


2. Arrays for real variables are also not allowed in Verilog.

Memories
Verilog provides a facility to model register memories like ROM or RAM as an array of registers. Each array
element in an array is called a word.
reg mem [0:511]; // Memory mem with 512 1-bit word.
reg [3:0] mem [0:511]; // Memory mem with 512 4-bit words
Modules and Ports in Verilog
Modules
A Module is a basic building design block in Verilog and it can be an element that implements necessary
functionality. It can also be a collection of lower-level design blocks. As a part of defining a module, it has
a module name, port interface, and parameters (optional). The port interface i.e. inputs and outputs is used
to connect the high-level module with the lower one and hides internal implementation.
Declaration

The module is declared using a keyword ‘module’ with an optional port list and followed by its
implementation. In the end, it is enclosed with the keyword ‘endmodule’.

module <module_name> (<port_list>);


...
<implementation>
... endmodule

A module consists of variable declaration, dataflow statements, behavioral blocks, instantiation of lower
hierarchical modules, tasks, and functions. All of these are optional depending on the requirement
statements or blocks that can be used, but module, endmodule, and module name are mandatory. It is not
allowed to have nested modules; instead, it allows instantiating sub-module to have the module connections.
// This is illegal to write module
dut_1;
...
module dut_2;
...
endmodule
endmodule

Ports
An interface to communicate with other modules or a testbench environment is called a port. In simple
words, the input/ output pins of digital design are known as ports. This interface is termed a port interface
or port list. Since the port list is available for connection, internal design implementation can be hidden
from other modules or an environment.
Verilog keywords used for port declaration are as follows:
Port Type Keywords used Description

Input port input To receive signal values from another module

Output port output To send signal values to another module

Bidirectional port inout To send or receive signal values to another module.


Ports in Verilog Example:
module mod(input a, b, output y);
... endmodule

Note:

1. Ports are of wire data type by default.


2. If output ports hold their value, then they must be declared as reg data type.
3. The input and inout ports can not be reg as they can not store values. Input ports pass
signals from externally connected signals.

Connection rules in Verilog port


While writing a module, the designer needs to make sure what type of signals have to be connected to the
module’s inputs and outputs and follow the below rules.
For understanding port connecting rules, consider the current design module as an internal world and outside
of the module to be an external world.
Port External Internal Description
type world world

input reg or net net In an internal world, the input port must be of the net type and it can be
connected to reg or net type variable in an external world.

output net reg or In an internal world, the output port can be of reg or net type and it must be
net connected to the net type variable in an external world.

inout net net In an internal world, the inout port must be of the net type and it must be
connected to the net type variable in an external world.

Module instantiation
While designing complex digital circuits, usually it is split into various modules that connect to have a
toplevel block. Thus, Verilog supports a hierarchical design methodology. When a module is instantiated, a
unique object is created and it has a unique name. Similarly, a top-level design can also be instantiated while
creating a testbench.
module dut_1(<port_list>);
... endmodule module
dut_2(<port_list>);
...
endmodule
module dut_3(<port_list>);
dut_2 d2(...);
... endmodule

// Top level module module


dut_top(<port_list>);
dut_1 d1(...); dut_3
d3(...);
endmodule
Following the hierarchical approach, a particular signal can be reached out following hierarchy and each
identifier is separated using a dot.
// Hierarchical connections dut_top.d3.d2.<signal> // To
track a signal in dut_2 module.

A mechanism for connecting the port to the external signals


When a module is instantiated in the top-level hierarchy or top-level design in a testbench, any one of the
following methods can be used.
Method A: Connecting a port list in an ordered manner
An ordered manner connection is feasible when a port list has minimum signals as the user has to follow
the same order in which design level signals are declared.

Design declaration:
module mux_2_1(
input sel, input
i0, i1, output
y);

Design instantiation in testbench:


module mux_tb; reg in0, in1,
select; wire out;
mux_2_1 mux(select, in0, in1, out);
... endmodule

Observe that the port list in design mux_2_1 and its instantiation in testbench mux_tb
follow the same order.

module mux_2_1(input sel, input i0, i1, output y); // At design mux_2_1
mux(select, in0, in1, out); // At testbench

Note: It is not mandatory to use different names at the design and testbench level.

module mux_2_1(input sel, input i0, i1, output y); // At design mux_2_1
mux(sel, i0, i1, y); // At testbench

Method B: Connecting a port list by name


For complex designs having more ports, remembering, and writing in the same order while instantiating the
design is error-prone. In this method, the order of port list connection does not matter based on the port
name, the connection is established. For an above example,
module mux_2_1(input sel, input i0, i1, output y); // At design mux_2_1
mux(.i0(in0), .sel(select), .y(out), .i1(in1)); // At testbench
Writing a testbench in Verilog
The testbench is written to check the functional correctness based on design behavior. The connections
between design and testbench are established using a port interface. A testbench generates and drives a
stimulus to the design to check its behavior. Since the behavior of the design has to be tested, the design
module is known to be “Design Under Test” (DUT).

Connection between testbench and DUT


Let’s understand how to write Verilog testbench with a simple example of 2:1 MUX.
A 2:1 MUX is implemented using the ‘assign statement’ which will be discussed in the dataflow modeling
section. For now, it is better to focus on how DUT is connected with a testbench and how the generated
stimulus is driven.
module mux_2_1(
input sel, input
i0, i1, output
y);

assign y = sel ? i1 : i0; endmodule

Steps involved in writing a Verilog testbench i.


Declare a testbench as a module.
module
<testbench_name>;
Example: module mux_tb;

ii. Declare set signals that have to be driven to the DUT. The signals which are connected to the input of
the design can be termed as ‘driving signals’ whereas the signals which are connected to the output of
the design can be termed as ‘monitoring signals’. The driving signal should be of reg type because it
can hold a value and it is mainly assigned in a procedural block (initial and always blocks). The
monitoring signals should be of net (wire) type that get value driven by the DUT.
Note: The testbench signal nomenclature can be different the DUT port
reg i0, i1, sel; // declaration.
wire y;
iii. Instantiate top-level design and connect DUT port interface with testbench variables or signals.
<dut_module> <dut_inst> (<TB signals>) mux_2_1
mux(.sel(sel), .i0(i0), .i1(i1), .y(y)); or
mux_2_1 mux(sel, i0, i1, y);

iv. Use an initial block to set variable values and it can be changed after some delay based on the
requirement. The initial block execution starts at the beginning of the simulation and updated values
will be propagated to an input port of the DUT. The initial block is also used to initialize the variables
in order to avoid x propagation to the DUT.
Example: Initialize clock and reset variables.
initial begin
clock = 0;
reset = 0; end
COPY
For 2:1 MUX example, initial
begin
// To print the values.
$monitor("sel = %h: i0 = %h, i1 = %h --> y = %h", sel, i0, i1, y);
i0 = 0; i1 = 1; sel = 0; #1; sel = 1; end

v. An always block can also be used to perform certain actions throughout the simulation.
Example: Toggling a clock always #2 clock = ~ clock;

In the above example, the clock is not used in the DUT, so we will not be declaring or using it.
vi. The system task $finish is used to terminate the simulation based on the requirement. vii.
The endmodule keyword is used to complete the testbench structure. Complete testbench
code
module mux_tb;
reg i0, i1, sel;
wire y;
mux_2_1 mux(sel, i0, i1, y);
initial begin
$monitor("sel = %h: i0 = %h, i1 = %h --> y = %h", sel, i0, i1,
y); i0 = 0; i1 = 1; sel = 0; #1; sel = 1;
end
endmodule
Gate Level Modeling
The module implementation is similar to the gate-level design description in terms of logic gates and
interconnections between them. It is a low-level abstraction that describes design in terms of gates.
Verilog supports some predefined basic gates (commonly knowns as primitives) as follows

Gate Syntax Description


types

and and g(out, i1, i2, Performs AND operation on two or more inputs
…)

or or g(out, i1, i2, Performs OR operation on two or more inputs


…)

xor xor g(out, i1, i2, Performs XOR operation on two or more inputs
…)

nand nand g(out, i1, Performs NAND operation on two or more inputs
i2, …)

nor nor g(out, i1, i2, Performs NOR operation on two or more inputs
…)

xnor xnor g(out, i1, i2, Performs XNOR operation on two or more inputs
…)

buf buf g(out, in) The buffer (buf) passes input to the output as it is. It has only one scalar input
and one or more scalar outputs.

not not g(out, in) The not passes input to the output as an inverted version. It has only one scalar
input and one or more scalar outputs.

bufif1 bufif1 g(out, It is the same as buf with additional control over the buf gate and drives input
in, control) signal only when a control signal is 1.

notif1 notif1 g(out, It is the same as not having additional control over the not gate and drives input
in, control) signal only when a control signal is 1.

bufif0 bufif0 g(out, It is the same as buf with additional inverted control over the buf gate and drives
in, control) input signal only when a control signal is 0.

notif0 notif0 g(out, It is the same as not with additional inverted control over the not gate and drives
in, control) input signal only when a control signal is 0.
Note:
1. bufif1 and notif1 provide ‘Z output when a control signal is 0.
2. bufif0 and notif0 provide ‘Z output when a control signal is 1.

Example for Gate level Modeling


module gate_modeling(
input i1, i2, ctrl,
output o_and, o_or,
output o_nand, o_nor,
output o_xor, o_xnor,
output o_buf, o_not,

output o_bufif1, o_notif1,


output o_bufif0, o_notif0
);

// Gate types and a1


(o_and , i1, i2); or o1
(o_or , i1, i2); nand
na1(o_nand, i1, i2); nor
no1(o_nor , i1, i2); xor
x1 (o_xor , i1, i2); xnor
xn1(o_xnor, i1, i2);
// buffer and not
buf(o_buf, i1); not(o_not,
i1);

// buffer and not with additional control


bufif1(o_bufif1, i1, ctrl); notif1(o_notif1,
i1, ctrl);
bufif0(o_bufif0, i1,
ctrl); notif0(o_notif0, i1,
ctrl); endmodule
Output:
0: i1 = 0, i2 = 0, ctrl = 0, o_and = 0, o_or = 0, o_nand = 1, o_nor = 1, o_xor = 0, o_xnor = 1, o_buf = 0,
o_not = 1, o_bufif1 = z, o_notif1 = z, o_bufif0 = 0, o_notif0 = 1
1: i1 = 0, i2 = 1, ctrl = 1, o_and = 0, o_or = 1, o_nand = 1, o_nor = 0, o_xor = 1, o_xnor = 0, o_buf = 0,
o_not = 1, o_bufif1 = 0, o_notif1 = 1, o_bufif0 = z, o_notif0 = z
2: i1 = 1, i2 = 0, ctrl = 0, o_and = 0, o_or = 1, o_nand = 1, o_nor = 0, o_xor = 1, o_xnor = 0, o_buf = 1,
o_not = 0, o_bufif1 = z, o_notif1 = z, o_bufif0 = 1, o_notif0 = 0
3: i1 = 1, i2 = 1, ctrl = 1, o_and = 1, o_or = 1, o_nand = 0, o_nor = 0, o_xor = 0, o_xnor = 1, o_buf = 1,
o_not = 0, o_bufif1 = 1, o_notif1 = 0, o_bufif0 = z, o_notif0 = z

Gate delays
In the real world, digital gates have delays involved for inputs propagating to the output with gate operation,
and the same delay can be modeled in Verilog. A pin-to-pin delay can also be modeled in Verilog.
The three types of delays can be specified for delays from inputs to the primitive gate output as the rise,
fall, and turn-off delays
Delays Definition

Rise delay The delay associated or time is taken for the output of the gate to change to 1 from some
value (0, x, or z).

Fall Delay The delay associated or time is taken for the output of the gate to change to 0 from some
value (1, x, or z).

Turn-off The delay associated or time is taken for the output of the gate to change to z from some
delay value.
Delay specification types
Delay specification Used for Syntax

One delay specification all three transitions. #(delay)

Two delay specification rise and fall transitions. #(rise, fall)

Three delay specification rise, fall, and turn-off transitions. #(rise, fall, turn-off)

Example:
module gates (
input i1, i2, ctrl,
output y1, y2, out1, out2, out3);

// #(3) : delay = 3 for all transition


// #(3,4) : rise = 3, fall = 4
// #(3,4,5) : rise = 3, fall = 4, turn-=off = 5

or #(3) o1(y1, i1, i2);


bufif1 #(3) b1(out1, i1, ctrl);
or #(3,4) o2(y2, i1, i2);
bufif1 #(3,4) b2(out2, i1, ctrl);

bufif1 #(3,4,5) b3(out3, i1, ctrl);


endmodule Output:
Time = 0: i1 = 1, i2 = 0, ctrl = 1
[single delay] y1 = x, out1 = x
[two delays] y2 = x, out2 = x
[Three delays] out3 = x

Time = 3: i1 = 1, i2 = 0, ctrl = 1
[single delay] y1 = 1, out1 = 1
[two delays] y2 = 1, out2 = 1
[Three delays] out3 = 1

Time = 10: i1 = 0, i2 = 1, ctrl = 1


[single delay] y1 = 1, out1 = 1
[two delays] y2 = 1, out2 = 1
[Three delays] out3 = 1

Time = 13: i1 = 0, i2 = 1, ctrl = 1


[single delay] y1 = 1, out1 = 0
[two delays] y2 = 1, out2 = 1
[Three delays] out3 = 1

Time = 14: i1 = 0, i2 = 1, ctrl = 1


[single delay] y1 = 1, out1 = 0
[two delays] y2 = 1, out2 = 0 [Three
delays] out3 = 0
Time = 20: i1 = 0, i2 = 0, ctrl = 1
[single delay] y1 = 1, out1 = 0
[two delays] y2 = 1, out2 = 0
[Three delays] out3 = 0

Time = 23: i1 = 0, i2 = 0, ctrl = 1


[single delay] y1 = 0, out1 = 0
[two delays] y2 = 1, out2 = 0
[Three delays] out3 = 0

Time = 24: i1 = 0, i2 = 0, ctrl = 1


[single delay] y1 = 0, out1 = 0
[two delays] y2 = 0, out2 = 0
[Three delays] out3 = 0
xmsim: *W,RNQUIE: Simulation is
complete.

Analysis:
Note the following delay types involved.
One delay : #(3) : delay = 3 for all transition
Two delay : #(3,4) : rise = 3, fall = 4
Three delay: #(3,4,5) : rise = 3, fall = 4, turn-=off = 5
Input driven: At time = 0: i1 = 1, i2 = 0, ctrl = 1
Since single delay = 3 and rise delay = 3, output y1, out1, y2, out2, out3 will be updated at 3 time units.
At time = 3:
[single delay] y1 = 1, out1 = 1
[two delays] y2 = 1, out2 = 1
[Three delays] out3 = 1
Input driven: Time = 10: i1 = 0, i2 = 1, ctrl = 1
An input i1 value is changed from 1 to 0 i.e. fall transition.
For single delay 3 units at time = 13, out1 is expected to change.
Time = 13:
[single delay] y1 = 1, out1 = 0
Outputs out2 and out3 are expected to change after a fall time delay which is 4 units.
Time = 14:
[two delays] y2 = 1, out2 = 0
[Three delays] out3 = 0
Input driven: Time = 20: i1 = 0, i2 = 0, ctrl = 1
An input i2 value is changed from 1 to 0 i.e. fall transition.
For single delay 3 units at time = 23, y1 is expected to change to 0.
Time = 23:
[single delay] y1 = 0, out1 = 0
Output y2 is expected to change after a fall time delay which is 4 units.
Time = 24:
[two delays] y2 = 0.
Additional controls min/ typ/ max values in delays
module gates (
input i1, i2, ctrl,
output y1, y2, out1, out2, out3);

// #(3:4:5) : delay(min = 3, typ = 4, max = 5) for all transition


or #(3:4:5) o1(y1, i1, i2); bufif1 #(3:4:5) b1(out1, i1, ctrl);

// #(3:4:5) : rise delay (min = 3, typ = 4, max = 5)


// #(4:5:6) : fall delay (min = 4, typ = 5, max = 6)
or #(3:4:5, 4:5:6) o2(y2, i1, i2); bufif1 #(3:4:5,
4:5:6) b2(out2, i1, ctrl);

// #(3:4:5) : rise delay (min = 3, typ = 4, max = 5)


// #(4:5:6) : fall delay (min = 4, typ = 5, max = 6)
// #(5:6:7) : Turn-off delay (min = 5, typ = 6, max = 7)
bufif1 #(3:4:5, 4:5:6, 5:6:7) b3(out3, i1, ctrl); endmodule
Data flow Modeling
Though gate-level modeling is easy to implement for the designs when the number of gates becomes more
in the design, it becomes difficult for the implementation. Hence, higher-level abstraction is required for
design implementation.
The data flow modeling provides a way to design circuits depending on how data flow between the registers
and how data is processed.
Continuous assignment
In data flow modeling, a continuous assignment is used to drive a value to a net or wire. A continuous
assignment statement is represented by an ‘assign’ statement.
Syntax: assign <drive_strength> <expression or net> = <delay> <constant value or

expression>

Where,
drive_strength: driven strength on a wire. It is used to resolve conflict when two or more assignments drive
the same net or wire. Refer strength in verilog delay: to specify a delay in an assignment in terms of time
units (similar to the delay in gate modeling). It is useful to mimic real-time circuit behavior.
Note:

1. The drive_strength and delay both are optional to use. The R.H.S. expression is evaluated
and assigned to the L.H.S. expression.
2. Since ‘assign’ statements are always active, they are known as continuous assignments.
3. In an implicit continuous assignment, Verilog provides flexibility to combine net
declaration and assign statement. Both regular and implicit continuous assignments have
no difference in terms of outcome.

//Regular continuous assignment wire


result;
assign result = i1 ^ i2;

//Implicit continuous assignment wire


result = i1 ^ i2;

Rules and characteristics of continuous assignment


1. The L.H.S. of an assignment must be always a vector or scalar net or a concatenation of
vector or scalar nets. It can not be a vector or scalar register.
2. The R.H.S. of an assignment can be registers or nets or function calls. Here, registers or
nets can be vectors or scalars.
3. Since continuous assignments are always active, as soon as the R.H.S. operand has any
changes, it assigns to the L.H.S. operand.
assign LHS = RHS

Delays in continuous assignment


The delay value specified in the ‘assign’ statement controls the timing effect on the L.H.S. operand when
the R.H.S. operand has any changes.
Verilog allows specifying delays in three ways and all of them have the same effect.
A. Regular assignment delay
wire result;
assign #5 result = i1 ^ i2;

B. Implicit continuous
assignment delay wire #5 result
= i1 ^ i2;

C. Net declaration delay


wire #5 result; assign result =
i1 ^ i2;

Data flow Modeling Example


module dataflow_modeling(
input i1, i2, output
[4:0] result
);

assign result[0] = i1^i2;


assign #5 result[1] = i1^i2;

assign result[3:2] = {i1,


i2}; endmodule
Output:
0: i1 = x, i2 = x, result = zxxxx
1: i1 = 0, i2 = 0, result = z00x0
6: i1 = 0, i2 = 1, result = z0101
11: i1 = 1, i2 = 0, result = z1011
16: i1 = 1, i2 = 1, result = z1110
21: i1 = 1, i2 = 1, result = z1100

Explanation:

The result is a 5-bit output variable and only [3:0] bits are updated with the ‘assign’ statement. Hence, the
4th-bit position will remain ‘Z’ always.
At 0 time units, no inputs are driven i.e. i1 = x and i2 = x that results in result = zxxxx.
At 1 time units, i1 = 0 and i2 = 0, result[1] will be updated after 5 time units, hence the value of result[1] =
x. Other bit positions 0th, 2nd, 3rd and 4th of the result vector has updated values.
Similarly, for remaining input combinations [3:0] bits have updated values.
Verilog operators
The Verilog operators are similar to the C programming language operator that is used to produce results
based on the required operation.
Verilog provides different categories of operators 1.
Arithmetic operators
Operators Number of operands Description

+ 2 addition

– 2 subtraction

* 2 multiplication

/ 2 division

** 2 raised to the power

% 2 modulus produces the remainder of the division of two numbers.


The outcome takes the sign of the first operand.

The arithmetic operator performs an arithmetic operation on two operands.

Example:
module arithmetic_op;
reg [3:0] i1, i2;
initial begin i1
= 4'h6; i2 =
4'h2;

$display("i1 = %0h and i2 = %0h", i1, i2);


$display("Add: %0h", i1 + i2);
$display("Sub: %0h", i1 - i2);
$display("Mul: %0h", i1 * i2);
$display("Div: %0h", i1 / i2);
$display("pow: %0h", i2 ** 3);
$display("Mod: %0h", i1 % i2);

i1 = 4'ha; i2 = 4'h3;
$display("\ni1 = %0h and i2 = %0h", i1, i2);
$display("Mod: %0h", i1 % i2);
end endmodule Output: i1 = 6 and
i2 = 2
Add: 8
Sub: 4
Mul: c
Div: 3
pow: 8
Mod: 0
i1 = a and i2 = 3
Mod: 1
2. Logical operators
Operators Number of operands Description

! 1 logical negation

&& 2 logical and

|| 2 logical or

Logical operators take variables or expressions as operands and it evaluates to 0, 1, or x value.


Example:
module logical_op; reg
[3:0] i1, i2; initial
begin i1 = 4'h6; i2 =
4'h2;
$display("For operator: (&&): i1 = %0h && i2 = %0h: %h", i1, i2, i1 && i2);
$display("For operator: (||): i1 = %0h || i2 = %0h: %h", i1, i2, i1 || i2);
$display("For operator: (!) : i1 = %0h ! i2 = %0h: %h", i1, i2, !i1);

i1 = 4'b1x0z; i2 = 4'b0x1x;
$display("For operator: (&&): i1 = %0b && i2 = %0b: %h", i1, i2, i1 && i2);
$display("For operator: (||): i1 = %0b || i2 = %0b: %h", i1, i2, i1 || i2); end
endmodule Output:
For operator: (&&): i1 = 6 && i2 = 2: 1
For operator: (||): i1 = 6 || i2 = 2: 1
For operator: (!) : i1 = 6 ! i2 = 2: 0
For operator: (&&): i1 = 1x0z && i2 = x1x: 1 For
operator: (||): i1 = 1x0z || i2 = x1x: 1
The bitwise operator follows the below truth table in an operation.
6. Conditional operators
Operators Number of operands Description

?: 3 conditional

Syntax:
<result> = <conditional_expression> ? <true_expression> : <false_expression>
Evaluation:
The <conditional_expression> is evaluated first. If the result is

1. True, then <true_expression> is evaluated.


2. False, then <false_expression> is evaluated.
3. X (ambiguous ) then both <true_expression> and <false_expression> are evaluated and
their results are compared bit by bit. Each bit position of outcome is returned as a. bit
value if both bits are the same. b. X if bit value differs.

Examples:
module conditional_op;
reg [3:0] i1, i2; reg
[3:0] result; initial
begin i1 = 4'h6; i2 =
4'h2;
$display("i1 = %0h, i2 = %0h", i1, i2);
result = (i1 > i2)? 1 : 0; $display("result
= %0h", result);

i1 = 4'h6; i2 = 4'h6;
$display("i1 = %0h, i2 = %0h", i1, i2);
result = (i1 > i2)? 1 : 0;
$display("result = %0h", result);

i1 = 4'b1x00; i2 = 4'b0100;
$display("i1 = %b, i2 = %b", i1, i2);
result = (i1 > i2)? (i1 & i2) : (i1 | i2);
// The outcome is ambiguous then both <true_expression> and <false_expression>
// will be evaluated and compared to compute outcome
// <true_expression> = i1 & i2 = 4'b0x00
// <false_expression> = i1 | i2 = 4'b1x00
// result = xx00;
$display("result = %b", result);
end endmodule Output: i1 = 6, i2 =
2 result = 1 i1 = 6, i2 = 6 result =
0 i1 = 1x00, i2 = 0100
result = xx00

2:1 MUX or tri-state buffer can be easily implemented by a conditional operator.


2:1 MUX implementation
module
mux_2_1(
input sel,
input i0, i1,
output y);
assign y = sel ? i1 : i0;
endmodule Output: sel = 0: i0 =
0, i1 = 1 --> y = 0 sel = 1: i0 =
0, i1 = 1 --> y = 1

Tri-state buffer implementation


module tristate_buff(
input in, input en,
output out);

assign out = en ? in : 1'bz;


endmodule Output:
en = 0: in = 0 --> out = z en
= 1: in = 0 --> out = 0 en =
1: in = 1 --> out = 1

7. Shift operators
Operators Number of operands Description

<< 2 logical left shift

>> 2 logical right shift

<<< 2 arithmetic left shift

>>> 2 arithmetic right shift

Logical shift: Logical shift operators shift a vector to left or right by a specified number of bits and fill
vacant bit positions with zeros.
8. Reduction operators
The reduction operators give 1-bit output by performing the bitwise operation over a single vector operand.

Operators Number of operands Description

& 1 reduction and

| 1 reduction or

^ 1 reduction xor

~& 1 reduction nand

~| 1 reduction nor

^~ or ~^ 1 reduction xnor

Note: 1. Reduction operators work bit by bit from right to left.

2. Results of reduction nand, reduction nor and reduction xnor are inverted versions of results
of reduction and, reduction or and reduction xor.

module
reduction_op; reg
[3:0] i1; initial
begin i1 =
4'h6;
$display("For operator: (&) : i1 = %b -> %b", i1, &i1);
$display("For operator: (|) : i1 = %b -> %b", i1, |i1);
$display("For operator: (^) : i1 = %b -> %b", i1, ^i1);
$display("For operator: (~&) : i1 = %b -> %b", i1, ~&i1);
$display("For operator: (~|) : i1 = %b -> %b", i1, ~|i1);
$display("For operator: (~^) : i1 = %b -> %b", i1, ~^i1);
i1 =
4'b1x0z;
$display("For operator: (&) : i1 = %b -> %b", i1, &i1);
$display("For operator: (|) : i1 = %b -> %b", i1, |i1);
$display("For operator: (^) : i1 = %b -> %b", i1, ^i1);
$display("For operator: (~&) : i1 = %b -> %b", i1, ~&i1);
$display("For operator: (~|) : i1 = %b -> %b", i1,
~|i1); $display("For operator: (~^) : i1 = %b -> %b",
i1, ~^i1); end endmodule
Output:
For operator: (&) : i1 = 0110 -> 0
For operator: (|) : i1 = 0110 -> 1
For operator: (^) : i1 = 0110 -> 0
For operator: (~&) : i1 = 0110 -> 1
For operator: (~|) : i1 = 0110 -> 0
For operator: (~^) : i1 = 0110 -> 1
For operator: (&) : i1 = 1x0z -> 0
For operator: (|) : i1 = 1x0z -> 1
For operator: (^) : i1 = 1x0z -> x
For operator: (~&) : i1 = 1x0z -> 1
For operator: (~|) : i1 = 1x0z -> 0
For operator: (~^) : i1 = 1x0z -> x
9. Concatenation operators
Operators Number of operands Description

{ } ‘N’ number concatenation

The multiple operands can be appended using a concatenation operator. The operands have to be written in
braces and separate themselves with commas.
Note:

1. Operands can be vector/scalar registers or nets, sized constants, bit-select.


2. Unsized operands are not allowed because computed result size is dependent on each
operand size.

module
concatenation_op;
reg [1:0] i1, i2;
reg [3:0] i3; reg
[7:0] out;
initial begin
i1 = 2'h2; i2 = 2'h3;
i3 = 4'h8;
$display("out = %b",
{i3, i2, i1});
$display("out = %b", {i3, i2, 2'b11});
$display("out = %b", {i3, i2[1], 1'b1, i1[0]});
end endmodule Output:
out = 10001110
out = 10001111
out = 1000110
10. Replication operators
Operators Number of operands Description

{ { } } ‘N’ number replication

The same number can be replicated for a specific number of times (replication constant) using a replication
operator.
module
replication_op; reg
[1:0] i1, i2; reg
[7:0] out;
initial begin
i1 = 2'h2; i2 = 2'h3;

$display("out = %b", {4{i1}});


$display("out = %b", {{3{i2}}, {2{i1}}}
); end endmodule Output:
out = 10101010
out = 1111111010
Note:

1. It is recommended to use parentheses in the expressions to avoid ambiguity.


2. If parentheses are not used then precedence order is followed as
Arithmetic operator (Highest precedence) -> Relational operator -> equality operator -> reduction operator
-> logical operator -> conditional operator (Lowest precedence).
Behavioral Modeling
On increasing digital design complexity, the designer has to find out the optimum algorithm and
architecture to implement in hardware. Hence, the designer needs a way that can describe the design as
per algorithm behavior. The Verilog provides the facility to represent the behavior of design at a high-level
abstraction similar to C language programming.
Structured Procedure blocks
Verilog provides two structured procedural blocks.

1. initial block
2. always block

initial block in Verilog


The initial block executes only once and starts its execution at the beginning of a simulation.

1. It is a non-synthesizable block and it can not contribute to building design schematic


development.
2. Multiple statements are enclosed within ‘begin’ and ‘end’ keywords and statements are
executed sequentially.
3. Multiple initial blocks are valid in a module and each block executes at zero simulation
time. An individual block may finish their execution independently.
4. Nested initial blocks are not valid. 5. It can execute in zero time or take some time
depending on the delay involved. Syntax:
initial
<single statement>
initial begin
<statement 1>
<statement 2>
<statement 3>
...
...
End

Use of initial block:


The initial block is used for

1. Initialize variables
2. Implementing a testbench code
3. Monitoring waveforms and other processes that are executed only once during the entire
simulation.
4. Drive ports with specific values. Examples:
Basic initial block that executes in zero simulation time.
module tb;
reg i1; reg
[3:0] i2;
initial
begin i1 =
0; i2 =
4'h5;
$display("i1 = %0h, i2 = %0h", i1,
i2); end endmodule Output:
i1 = 0, i2 = 5

Multiple initial blocks


module tb;
reg i1; reg
[3:0] i2;

initial
i1 = 0;
initial
begin
i2 =
4'h5;
$display("i1 = %0h, i2 = %0h", i1,
i2); end
initial
begin #5;
i1 = 1; i2
= 4'h8;
$display("i1 = %0h, i2 = %0h", i1,
i2); end endmodule Output:
i1 = 0, i2 = 5
i1 = 1, i2 = 8

Terminate simulation using $finish system task


module tb;
reg i1; reg
[3:0] i2;

initial
begin #5;
i1 = 1; i2
= 4'h8;
$display("i1 = %0h, i2 = %0h", i1, i2);
#5 i1 = 0;
i2 = 4'h2;
$display("i1 = %0h, i2 = %0h", i1,
i2); end initial
#6 $finish;

endmodu
le
Output:
i1 = 1, i2 = 8
$finish called from file "testbench.sv", line 18.
$finish at simulation time 6

always block in Verilog


The always block starts its execution at the beginning of the simulation and continues to execute in a loop.

1. Similar to the initial block, the always block is executed sequentially.


2. Based on changes in single or multiple signals in the sensitivity list, always block is
executed.
3. If no sensitivity list is specified, always block executes repeatedly throughout the
simulation.
4. Multiple always blocks are valid in a module and each block executes independently.
5. Nested always blocks are not valid.
Syntax:
always
<single statement>
always
@(<sensitivity_list>)
<single statement>
always @(<sensitivity_list>)
begin
<statement 1>
<statement 2>
<statement 3>
...
... end

Use of always block 1. To model repetitive activity in a digital design. Ex. clock
generation. 2. To implement combinational and sequential elements in
digital circuits.
Sensitivity list
The sensitivity list can be a single or multiple signals placed within parentheses () after @ operator. Based
on the change in any signal value, it allows executing the always block.

If the sensitivity list includes all inputs, then only ‘*’ is used to represent all inputs instead of writing all
inputs.
always @(i1 or i2 or i3) begin
<statements>
end

Based on the change in signal value in anyone of the i1, i2, or i3 signals, the always block will be executed.
Examples
Clock Generator
In the below example, the signal clk value changes after every 5 time-units.
The time period of the clock = 10 time-units.
module
tb; reg
clk;
initial clk = 0;
always #5 clk = ~clk;
initial
begin
$monitor("At t = %0d, clk = %0b", $time, clk);
#50
$finish; end
endmodule
Output:
At t = 0, clk = 0
At t = 5, clk = 1
At t = 10, clk = 0
At t = 15, clk = 1
At t = 20, clk = 0
At t = 25, clk = 1
At t = 30, clk = 0
At t = 35, clk = 1
At t = 40, clk = 0
At t = 45, clk = 1

It is mandatory to put a delay in always block while generating the clock otherwise, simulation hangs due to
zero-delay infinite loop.

always clk = ~clk; // causes simulation


hang
Always block with a sensitivity list
module and_gate(input i1, i2, i3, output reg out);

always@(i1 or i2 or i3)
begin out = i1 & i2 & i3;
end
// or
//always @(*) out = i1 & i2 & i3;

endmodul
e
Output:
At T = 0: i1 = 0, i2 = 0, i3 = 0, out = 0
At T = 5: i1 = 0, i2 = 1, i3 = 0, out = 0
At T = 13: i1 = 1, i2 = 1, i3 = 0, out = 0
At T = 16: i1 = 1, i2 = 1, i3 = 1, out = 1

Procedural Assignments
In the dataflow modeling, a continuous assignment statement is discussed where LHS expression is always
updated for changes in RHS expression. Refer to continuous assignment for more details. In the case of
procedural assignment, the LHS variable remains unchanged until the same value is updated by the next
procedural statement.
Syntax:
<variable> = <expression or value>

1. RHS expression can be a value or an expression that evaluates to a value.


2. LHS expression can be reg, integer, real, time-variable, or memory element.
3. The procedural assignments can be placed inside procedural blocks like initial and always
blocks. They can also be placed inside tasks and functions.
4. The procedural assignments can also be placed directly inside a module while declaring
a variable. (Ex. reg [3:0] i_data = 4’h7)
There are two types of procedural assignments and both of them are widely used in the designs written in
the Verilog language.

1. Blocking assignments
2. Non-blocking assignments

Blocking Assignments
The blocking assignment statements are executed sequentially by evaluating the RHS operand and finishes
the assignment to LHS operand without any interruption from another Verilog statement. Hence, it blocks
other assignments until the current assignment completes and is named as “blocking assignment”.
An equal ‘=’ is used as a symbol for the blocking assignment operator.
Syntax:
<variable> = <expression or value>

Example:
module blocking; reg
[3:0] data = 4'h4;
real r_value;
integer i_value;
time T;
initial
begin
$monitor("At time T = %0t: data = %0d, r_value = %0f, i_value = %0h", T, data,
r_value, i_value); r_value = 3.14; i_value = 4; #2 data = 4'h5; #3 data =
'd7; i_value = 10; i_value = 6; $finish; end
always #1 T =
$time; endmodule
Output:
At time T = 0: data = 4, r_value = 3.140000, i_value = 4
At time T = 1: data = 4, r_value = 3.140000, i_value = 4
At time T = 2: data = 5, r_value = 3.140000, i_value = 4
At time T = 3: data = 5, r_value = 3.140000, i_value = 4
At time T = 4: data = 5, r_value = 3.140000, i_value = 4
$finish called from file "design.sv", line 16.
$finish at simulation time 5

A blocking assignment does not block the execution of a statement in another procedural block. For
example, two initial blocks start execution at the same simulation time. A blocking assignment in the first
initial block does not block execution in another initial block.
Race around condition: A problem with blocking assignment
If a variable is used in LHS of blocking assignment in one procedural block and the same variable is used
in RHS of another blocking assignment in another procedural block. The race-around condition can occur
as an order of execution is unknown if both statements are scheduled at the same simulation time.
module blocking; reg
[3:0] data = 4'h5;
reg [3:0] y = 4'h3;

initial begin // first initial block


y = data;
$display("1st block: data = %0h and y = %0h", data,
y); end
initial begin // second initial
block data = y;
$display("2nd block: data = %0h and y = %0h", data,
y); end endmodule
Output:
1st block: data = 5 and y = 5
2nd block: data = 5 and y = 5

In this example,
Since procedural blocks (both initial and always) can be executed in any order.
A. If the first initial block executed before the second initial block then

1. The value of y will be updated as 4‘h5 in the first initial block


2. The value of data will be updated as 4‘h5 in the second initial block B.
If the second initial block executed before the first initial block then

1. The value of data will be updated as 4‘h3 in the second initial block
2. The value of y will be updated as 4‘h3 in the first initial block

Non Blocking Procedural assignments


The non-blocking assignment statement starts its execution by evaluating the RHS operand at the
beginning of a time slot and schedules its update to the LHS operand at the end of a time slot. Other Verilog
statements can be executed between the evaluation of the RHS operand and the update of the LHS operand.
As it does not block other Verilog statement assignments, it is called a non-blocking assignment.

A less than or equal to ‘<=’ is used as a symbol for the non-blocking assignment operator.
Note

1. If <= symbol is used in an expression then it is interpreted as a relational operator.


2. If <= symbol is used in an assignment then it is interpreted as a non blocking
operator. Example:
module nonblocking;
reg [3:0] data = 4'h4;
real r_value;
integer i_value;
time T; initial
begin
$monitor("At time T = %0t: data = %0d, r_value = %0f, i_value = %0h", T, data, r_value,
i_value); r_value <= 3.14; i_value <= 4; #2 data <= 4'h5; data <= #3 'd7;
i_value <= 10; i_value <= 6; #4 $finish; end
always #1 T = $time;
endmodule

At time T = 0: data = 4, r_value = 3.140000, i_value = 4 33


At time T = 2: data = 5, r_value = 3.140000, i_value = 33
At time T = 3: data = 5, r_value = 3.140000, i_value = 33
At time T = 4: data = 5, r_value = 3.140000, i_value = 33
At time T = 5: data = 7, r_value = 3.140000, i_value =$finish called from file "design.sv", line 16.
Error! Bookmark not defined.

At time T = 1: data = 4, r_value = 3.140000, i_value = 4


$finish at simulation time 6

How race around condition is resolved in a nonblocking assignment?


If a variable is used in LHS of blocking assignment in one procedural block and the same variable is used
in RHS of another blocking assignment in another procedural block.
module nonblocking;
reg [3:0] data = 4'h5;
reg [3:0] y = 4'h3;

initial begin // first initial block


y <= data;
#1 $display("1st block: data = %0h and y = %0h", data,
y); end
initial begin // second initial
block data <= y;
#1 $display("2nd block: data = %0h and y = %0h", data,
y); end endmodule
Output:
1st block: data = 3 and y = 5
2nd block: data = 3 and y = 5

In this example,
Since procedural blocks (both initial and always) can be executed in any order.
In a non-blocking assignment statement no matter what is the order of execution, both RHS of the
assignments (y <= data and data <= y) are evaluated at the beginning of the timeslot and LHS operands
are updated at the end of a time slot. Thus, race around condition is avoided as there is no dependency on
execution order and the order of execution of these two statements can be said to happen parallelly.
Verilog procedural assignment guidelines
For a beginner in Verilog, blocking and non-blocking assignments may create confusion. If are used
blindly, it may create race conditions or incorrect synthesizable design. Hence, it is important to understand
how to use them. To achieve synthesized RTL correctly, Verilog coding guidelines for blocking and non-
blocking assignments are mentioned below

1. Use non-blocking assignments for modeling flip flops, latches, and sequential logic.
2. Use blocking assignment to implement combinational logic in always block.
3. Use non-blocking assignment to implement sequential logic in always block.
4. Do not mix blocking and non-blocking assignments in single always block i.e. For the
implementation of sequential and combination logic in a single ‘always’ block, use
nonblocking assignments.
5. Do not assign value to the same variable in the different procedural blocks.
6. Use non-blocking assignments while modeling both combination and sequential logic
within the same always block.
7. Avoid using #0 delay in the assignments.
mplementation of sequential logic
Basic sequential logic implementation: D Flip flop
The D flip flop is a basic sequential element that has data input ‘d’ is being driven to output ‘q’ as per clock
edge. Also, the D flip flop held the output value till the next clock cycle. Hence, it is called an edge-
triggered memory element that stores a single bit.
Based on the type of D flip flop, a sensitive list of always block has to be written.
D Flip flop types Sensitivity list

positive edge-triggered always @(posedge clk)

negative edge-triggered always @(negedge clk)

positive edge-triggered and asynchronous active high reset always @(posedge clk or posedge rst)

positive edge-triggered and asynchronous active low reset always @(posedge clk or negedge rst)

Positive edge-triggered and asynchronous active low reset D flip flop

Design schematic
module D_flipflop
( input clk,
rst_n, input d,
output reg q

);
always@(posedge clk or negedge rst_n)
begin if(!rst_n) q <= 0; else
q <= d; end endmodule Testbench:
module tb; reg clk, rst_n; reg d;
wire q;

D_flipflop dff(clk, rst_n, d, q);

always #2 clk =
~clk; initial begin
clk = 0; rst_n = 0;
d = 0;
#3 rst_n = 1;
repeat(6) begin d =
$urandom_range(0, 1);
#3; end rst_n =
0; #3; rst_n = 1;
repeat(6) begin d =
$urandom_range(0, 1);
#3;
end
$finish; end
initial begin

$dumpfile("dump.vcd");
$dumpvars(1); end
endmodule

Positive edge-triggered and synchronous active low reset D flip flop

Design schematic
module D_flipflop
( input clk,
rst_n, input d,
output reg q

);
always@(posedge clk)
begin if(!rst_n) q <=
0; else q <= d;
end endmodule
Testbench: module tb;
reg clk, rst_n; reg d;
wire q;

D_flipflop dff(clk, rst_n, d, q);

always #2 clk =
~clk; initial begin
clk = 0; rst_n = 0;
d = 0;
#3 rst_n = 1;
repeat(6) begin d =
$urandom_range(0, 1);
#3; end rst_n =
0; #3; rst_n = 1;
repeat(6) begin d =
$urandom_range(0, 1);
#3;
end
$finish; end
initial begin

$dumpfile("dump.vcd");
$dumpvars(1); end
endmodule

Procedural timing control


The timing control is used to advance simulation time and it has two methods

1. Delay timing control


2. Event timing control
Delay timing control
The delay timing control allows to add up a delay between when the statement is encountered and when it
is executed by the simulator. The delay is specified with the ‘#’ symbol.
Syntax:
A number, identifies or (min, typical, max) expression is used to specify
delay timing control
#<number>
#<identifier>
#(<min_exp>: <typical_exp>, <max_exp>)

There are three types of delay timing control as follows


Delay timing control types Declaration

Regular delay control The non-zero delay is specified at the LHS of the procedural statement.

Intra-assignment delay Delay is specified between the assignment operator and the RHS
control operand.

Zero delay control The zero delay is specified at LHS of procedural statement

Use Cases:
1. Regular delay control delays the execution of the entire statement by a specified value.
Examples:
a. #5 data = i_value;
b. #(2:3:6) data = 20;
2. Intra-assignment delay control delays computed value assignment by a specified value.
The RHS operand expression is evaluated at the current simulation time and assigned to
LHS operand after a specified delay value.
Example: data = #5 i_value;
3. Zero delay control is used to control execution order when multiple procedural blocks try
to update values of the same variable. Both always and initial blocks execution order is
non-deterministic as they start evaluation at the same simulation time. The statement
having zero control delay executes last, thus it avoids race conditions.
Example:
reg [2:0]
data; initial
begin data
= 2; end
initial begin
#0 data = 3;
end
Without zero delay control, the ‘data’ variable may have a value of either 2 or 3 due to race conditions.
Having zero delay statement as specified in the above code guarantees outcome to be 3. However, it is not
recommended to assign value to the variable at the same simulation time.
Event timing control
The event timing control method is used to trigger a statement or procedural block execution due to a
change in the value of a net or register. It can be classified into four types
Event timing control types Declaration

Regular event control An event control is specified using @ symbol

Event OR control Multiple events are declared using the ‘or’ keyword or comma ‘,’
symbol.

Named event control The event declared using

1. -> symbol as event triggering


2. @ symbol as waiting for event trigger.
Level sensitive timing The ‘wait’ keyword is used in the declaration
control

a. Regular event control


The statement or procedural block is executed based on positive or negative edge transition in a signal
value. posedge – A posedge is used to execute statement/s whenever there is a transition from 0 or X or Z
to 1. negedge – A negedge is used to execute statement/s whenever there is a transition from 1 or X or Z
to 0.
Examples:
Examples Description

@(posedge clk) q = d; The q = d is executed whenever the clk signal does transition from 0/X/Z to
1.

@(negedge clk) q = d; The q = d is executed whenever the clk signal does transition from 1/X/Z to
0.

out = @(posedge clk) (a & The a & b is evaluated immediately but assigned to out at the positive edge
b) of the clk.
out = @(negedge clk) (a & The a & b is evaluated immediately but assigned to out at the negative edge
b) of the clk.
module tb;
reg clk; reg [3:0] i1,
i2; reg [3:0] out1, out2,
out3;

initial clk = 0;
always #5 clk = ~clk;

always @(clk) begin


@(posedge clk) out1 = i1;
@(negedge clk) out2 = i2;
out3 = @(posedge clk) (i1 &
i2); end initial begin
$monitor("Time = %0t: i1 = %0d, i2 = %0d, out1 = %0d, out2 = %0d", $time, i1, i2,
out1, out2); repeat(4) begin i1 = $random; i2 = $random; #10;
end #20; $finish; end endmodule
Output:
Time = 0: i1 = 4, i2 = 1, out1 = x, out2 = x
Time = 10: i1 = 9, i2 = 3, out1 = x, out2 = x
Time = 15: i1 = 9, i2 = 3, out1 = 9, out2 = x
Time = 20: i1 = 13, i2 = 13, out1 = 9, out2 = 13
Time = 30: i1 = 5, i2 = 2, out1 = 9, out2 = 13
Time = 35: i1 = 5, i2 = 2, out1 = 5, out2 = 13
Time = 40: i1 = 5, i2 = 2, out1 = 5, out2 = 2
Simulation complete via $finish(1) at time 60 NS + 0
./testbench.sv:25 $finish;

b. Event OR control
The statement or procedural block is executed based on positive or negative edge or level transition of
more signals by mentioning into the sensitivity list using the ‘or’ keyword or comma ‘,’.
Examples Description

always @(clk or rst_n) Level sensitive for clk and rst_n signals using or keyword.

always @(posedge clk or negedge rst_n) Edge sensitive for clk and rst_n signals using or keyword.

always @(clk, rst_n) Level sensitive for clk and rst_n signals using a comma.

always @(posedge clk, negedge rst_n) Edge sensitive for clk and rst_n signals using a comma.
module tb;
reg clk_1, clk_2, clk_3;
reg [3:0] i1, i2; reg
[3:0] out1, out2, out3;

initial
begin
clk_1 = 0;
clk_2 = 0;
clk_3 = 0;
end
always #5 clk_1 = ~clk_1;
always #10 clk_2 = ~clk_2;
always #15 clk_3 = ~clk_3;
always @(posedge clk_1 or negedge clk_2)
begin out1 = i1; out2 = (i1 & i2);
end
always @(negedge clk_1, posedge clk_3)
begin out3 = (i1 | i2); end
initial begin
$monitor("Time = %0t: i1 = %0d, i2 = %0d, out1 = %0d, out2 = %0d", $time, i1, i2,
out1, out2); repeat(4) begin i1 = $random; i2 = $random; #10;
end #20; $finish; end initial begin

$dumpfile("dump.vcd");
$dumpvars(1); end
endmodule
Output:
Time = 0: i1 = 4, i2 = 1, out1 = x, out2 = x
Time = 5: i1 = 4, i2 = 1, out1 = 4, out2 = 0
Time = 10: i1 = 9, i2 = 3, out1 = 4, out2 = 0
Time = 15: i1 = 9, i2 = 3, out1 = 9, out2 = 1
Time = 20: i1 = 13, i2 = 13, out1 = 13, out2 = 13
Time = 30: i1 = 5, i2 = 2, out1 = 13, out2 = 13
Time = 35: i1 = 5, i2 = 2, out1 = 5, out2 = 0
Simulation complete via $finish(1) at time 60 NS + 0
./testbench.sv:35 $finish;

c. Named event control


In Verilog, the keyword ‘event’ is used to declare ‘named events’ that trigger an event using -> symbol and
it is recognized by @ symbol.

The named events are also commonly knowns as Verilog events’.


The named event or Verilog events are used for synchronization between two or more processes.
Syntax:
To trigger an event: ->
Waiting for the event or recognize the event: @() Note:
1. The Verilog event does not hold any data.
2. The @ symbol is used for edge-sensitive constructs.
module
tb;
event e1;
initial
begin
#10;
$display("Triggering an event e1");
->e1;
end
initial begin
@(e1) $display("Event e1 is
triggered"); end endmodule
Output:
Triggering an event e1
Event e1 is triggered

d. Level sensitive timing control


Along with the edge-sensitive construct (while waiting for an event trigger), Verilog adds up the ‘wait’
level-sensitive construct to wait for a specified condition to be true, and then only one or more statements
will be executed. Thus, a set of statements will be blocked until the ‘wait’ condition is evaluated to be true.
Syntax:
wait(<expression or variable>)
module tb;
integer count;

initial begin
count = 0;
forever begin
count++; #2;
end end
initial begin
wait(count == 'd5);
$display("count has reached till %0d at time = %0t", count, $time);

$finish;
end
endmodule
Output:
count has reached till 5 at time = 8

if statement in Verilog
Verilog supports ‘if’, ‘else if’, ‘else’ same as other programming languages.

The ‘If’ statement is a conditional statement based on which decision is made whether to execute lines
inside if block or not.

The begin and end are required in case of multiple lines present in ‘if’ block. For single-line inside if
statement may not require ‘begin..end’

The ‘if’ statement returns true if the expression calculates its value as 1 otherwise, for 0, x, z values ‘if’
block will not be executed.
Syntax:
if(<condition>) begin
...
End

The else if or else statement


In case, ‘if’ statement does not hold true, ‘else if’ or ‘else’ will be executed. For any condition hold true
in ‘else if’ statement, subsequent ‘else if’ or ‘else’ statement will not be checked. Syntax: ‘else if’ and
‘else’ condition
if(<condition>) begin
...
end
else if(<condition>) begin
... end else
if(<condition>) begin
...
end else
begin

...
end

Syntax: if and else condition


if(<condition>) begin
...
end else
begin
...
end

Example
:
module
if_example;
initial begin
int a, b; a
= 10; b =
20; if(a>b)
$display("a is greater than b");
else if(a<b)
$display("a is less than b");
else
$display("a is equal to
b"); end endmodule Output:
a is less than b

Case statement in Verilog


The case statement has a given expression and it is checked with the expression (case item) mentioned in
the list in the written order and if it matches then the statement or group of statements are executed. If it
does not match with any of the written expressions of the list then, the default statement will be executed.
If the ‘default’ statement is not given and the given expression is not matched with any expression in the
list, then the case statement evaluation will exit.
Verilog case statement uses case, endcase, and default keywords.
Syntax:
case(<expression>)
<case_item1>:
<case_item2>:
<case_item3>:
<case_item4>:
begin
...
...
end default:
endcase Note:

1. The default statement is not mandatory. There must be only one default statement for the
single case statement.
2. The nested case statement is allowed.
3. Verilog case statements work similarly as switch statements in C language.
4. An expression inside a case statement can not use <= (relational operator).
5. The === operator is used instead of == operator in case statement comparison. I.e. case
statement checks for 0, 1, x and z values in the expression explicitly.
Example:
module case_example;
reg [2:0] data;

always @(data) begin


case(data)
3'h2: $display("value of data is 2");
3'h4: $display("value of data is 4");
3'h5: $display("value of data is 5");
default: $display("default statement is executed for data = %0d",
data); endcase end initial begin repeat(10) begin data
= $random;

#1;
end
end
endmodu
le
Output:
value of data is 4
default statement is executed for data =
1 default statement is executed for data
= 3 value of data is 5 value of data is
2
default statement is executed for data = 1
value of data is 5

4:1 Mux Verilog implementation


The case statement is also used to design multiplexers. Below 4:1 MUX has two select inputs (sel) and
four inputs (i3,i2,i1, and i0) and has one output (y).
module mux_example(
input [1:0] sel,
input i0,i1,i2,i3,
output reg y);

always @(*)
begin
case(sel)
2'h0: y = i0;
2'h1: y = i1;
2'h2: y = i2;
2'h3: y = i3;
default: $display("Invalid sel
input"); endcase end endmodule

Output:
sel = 00 -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 1
sel = 01 -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 0
sel = 11 -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 0
sel = 01 -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 0

Ambiguous inputs to the case statement


Now, let’s see if the user provides ambiguous values to the case statement (Point 5)
module case_example(
input a,
output reg [3:0] out);

always @(*)
begin case(a)
1'h0: out = 4;
1'h1: out = 5;
1'hx: out = 6;
1'hz: out = 7;
default: $display("Invalid sel
input"); endcase end endmodule

Testbench: User has provided ambiguous values as x and


z. module tb; reg a; wire [3:0] out;

case_example case_ex(a, out);


initial
begin
$monitor("a = %b -> out = %0h", a,
out); a = 1'b1; #1 a = 1'b0;
#1 a =
1'bz; #1 a
= 1'bx; end
endmodule
Output:
a = 1 -> out = 5
a = 0 -> out = 4
a = z -> out = 7
a = x -> out = 6

casex and cazez statements


The case statement also has a total of three variations: case, casex and casez. Note the following
differences.

1. case: considers x and z as it is (as shown in above example). If an exact match is not
found, the default statement will be executed.
2. casex: considers all x and z values as don’t care.
3. casez: considers all z values as don’t cares. The z can also be specified as ?

casez statement example casex statement example


module casez_example( input [1:0] module casez_example( input [1:0] data,
output reg [3:0] out);
data, output reg [3:0] out);

always @(*) begin casez(data)


always @(*) begin 2'b0z: out = 1;
casez(data) 2'b0z: out = 1; 2'bz0: out = 2;
2'b1z: out = 3;
2'bz0: out = 2;
2'bxz: out = 4;
2'b1z: out = 3; 2'b0x: out = 5;
2'bxz: out = 4; 2'bx0: out = 6;
2'b1x: out = 7;
2'bx1: out = 8;
2'b0x: out = 5;
2'bx0: out = 6; default: $display("Invalid sel input"); endcase
2'b1x: out = 7; end endmodule

2'bx1: out = 8;

default: $display("Invalid sel module casex_example(


input [1:0] data,
input"); endcase end endmodule output reg [3:0] out);

always @(*) begin


casex(data)
2'b0z: out = 1;
2'bz0: out = 2;
2'b1z: out = 3;
2'bxz: out = 4;
2'b0x: out = 5;
2'bx0: out = 6;
2'b1x: out = 7;
2'bx1: out = 8;

default: $display("Invalid sel


input"); endcase end endmodule
Output: Output:
data = x1 -> out = 4 data = x1 -> out = 1
data = 0x -> out = 1 data = 0x -> out = 1
data = x0 -> out = 2 data = x0 -> out = 1
data = z1 -> out = 1 data = z1 -> out = 1
data = 0z -> out = 1 data = 0z -> out = 1
data = z0 -> out = 1 data = z0 -> out = 1
data = 1z -> out = 2 data = 1z -> out = 2
Note:
1. In simple terms, casez ignores bit positions having z value alone and casex ignores bit
positions having x or z values.
2. The ‘casez’ is more likely used as compared to ‘casex’ as the ‘casez’ does not ignore bit
positions having x values and ‘casex’ is not synthesizable as well.

Loops in Verilog
A loop is an essential concept of any programming language. The loop is useful to read/ update an array
content, execute a few statements multiple times based on a certain condition. All looping statements can
only be written inside procedural (initial and always) blocks. In Verilog, we will discuss the following
loop blocks.
1. For loop
2. While loop
3. Forever loop
4. Repeat loop
In all supported loops, begin and end keywords are used to enclose multiple statements as a single block. A
begin and end keywords are optional if the loop encloses a single statement.
For loop
The for loop iterates till the mentioned condition is satisfied. The execution of for loop depends on –

1. Initialization
2. Condition
3. Update Syntax:
for (<initialization>; <condition>; <update>)
begin ... end
1. Initialization: An initial value of the variable is set. It is executed only once.
2. Condition: A condition or expression is evaluated. If it is evaluated to be the true body of
for loop (statements inside begin and end) are executed else, the loop terminates.
3. Update: After execution of for loop body, the variable value is updated

for loop flow chart


Example:
module for_example;
int array[10];

initial begin
// Update array
for (int i = 0; i < $size(array); i++)
begin array[i] = i*i; end

// Display array elements


for (int i = 0; i < $size(array); i++)
begin $display("array[%0d] = %0d", i,
array[i]); end end endmodule Output:
array[0] = 0
array[1] = 1
array[2] = 4
array[3] = 9
array[4] =
16 array[5]
= 25
array[6] =
36 array[7]
= 49
array[8] =
64 array[9]
= 81
It is possible to have multiple variable initializations, conditions, and update values.
module for_example;
int array[10];
initial
begin //
Update array
for (int i = 0, cnt = 0; i < $size(array); i++, cnt++) begin
array[i] = i*i;
$display("cnt = %0d", cnt);
end

// Display array elements


for (int i = 0; i < $size(array); i++) begin
$display("array[%0d] = %0d", i, array[i]); end end endmodule
Output: cnt = 0 cnt = 1 cnt = 2 cnt = 3 cnt = 4 cnt = 5 cnt = 6 cnt
= 7 cnt = 8 cnt = 9 array[0] = 0 array[1] = 1 array[2] = 4 array[3]
= 9 array[4] = 16 array[5] = 25 array[6] = 36 array[7] = 49
array[8] = 64 array[9] = 81
A for loop behaves as a while loop if initializations, conditions, and update values are
not written. module for_example; int cnt; initial begin for (;;) begin
$display("cnt = %0d",
cnt); if(cnt == 10)
break; cnt++; end
end endmodule Output: cnt = 0
cnt = 1 cnt = 2 cnt = 3 cnt = 4
cnt = 5 cnt = 6 cnt = 7 cnt = 8
cnt = 9
cnt = 10

While loop
A while loop is a control flow statement that executes statements repeatedly if the condition holds true
else loop terminates Syntax:
while(<condition>) begin
...
end
while loop flow chart
Example:

In the below example, the value of ‘count’ is incremented till the condition holds true.
module
while_example;
int count;
initial begin
while(count<10) begin
$display("Value of count = %0d",
count); count++; end end
endmodule
Output:
Value of count = 0
Value of count = 1
Value of count = 2
Value of count = 3
Value of count = 4
Value of count = 5
Value of count = 6
Value of count = 7
Value of count = 8
Value of count = 9

Note: The ‘do while’ loop is not supported in Verilog.


Forever loop As the name suggests, a forever loop runs indefinitely. To terminate the loop, a ‘disable’
statement can be used. The forever loop is the same as the while loop with an expression that holds always
true i.e. while(1) loop.
The forever loop must be used with a timing control construct otherwise the simulator will get stuck in a
zero-delay simulation time loop.
Syntax:
forever
begin ...
end

Example with $finish:


To terminate the loop, $finish system call is used.
module
forever_example;
integer count = 0;
initial begin
forever begin
$display("Value of count = %0d",
count); count++; #5; end
end initial begin #30; $finish;
end endmodule

Value of count = 0
Value of count = 1
Value of count = 2
Value of count = 3
Value of count = 4
Value of count = 5
$finish called from file "testbench.sv", line 16.
$finish at simulation time 30

Note: The break and disable statements are not synthesizable in Verilog

Difference between always and forever block


Both always and forever block the same effect. The always block is a procedural block and it can not be
placed inside other procedural blocks. Syntax: always
// For multiple statements in always block
always begin
...
end

// For single statement


always <single statement>

Example of always block:


module
always_example;
int count; always
begin
$display("Value of count = %0d",
count); count++; #5; end

initial
begin
#30;
$finish;
end endmodule

Output
:
Value of count = 0
Value of count = 1
Value of count = 2
Value of count = 3
Value of count = 4
Value of count = 5
$finish called from file "testbench.sv", line 14.
$finish at simulation time 30

A always block inside another procedural block


A compilation error is expected when always block is used inside another procedural block i.e. initial begin
.. end block. In such a case, a forever block can be used.
module
always_example;
int count; initial
begin
always begin // can not use inside other procedural block
$display("Value of count = %0d",
count); count++; #5; end
end initial begin #30; $finish;
end endmodule
COPY
Output:
Error-[SE] Syntax error
Following verilog source has syntax error :
"testbench.sv", 7: token is 'always'
always begin // can not use inside other procedural block
^
1 error
CPU time: 1.120 seconds to compile
Exit code expected: 0, received: 1

Repeat loop
A repeat loop is used to execute statements a given number of times.
Syntax:
repeat(<number>) begin // <number> can be variable or fixed
value ... end

Example:
In the below example,

1. Based on the array size, array elements are printed.


2. The string “Repeat it” is printed three times.
module repeat_example;
int array[5] = '{100, 200, 300, 400,
500}; int i; initial begin
repeat ($size(array)) begin
$display("array[%0d] = %0d", i,
array[i]); i++; end

repeat(3)
$display("Repeat
it"); end endmodule

Output:
array[0] = 100
array[1] = 200
array[2] = 300
array[3] = 400
array[4] = 500
Repeat it
Repeat it
Repeat it

Verilog blocks
The Verilog blocks are nothing but a group of statements that acts as one. The multiple statements are
grouped together using ‘begin’ and ‘end’ keywords. Verilog classifies blocks into two types.

1. Sequential blocks
2. Parallel blocks
Sequential blocks Parallel blocks

The sequential block executes a group of statements The parallel block executes a group of
(blocking assignment statement) in a sequential statements concurrently as their execution
manner in which they are specified. starts at the same simulation time.

Keywords used: begin and end Keywords used: fork and join

Example:
module tb; reg
[3:0] i1, i2, i3;
reg [3:0] x1, x2, x3;

// sequential block
initial begin
$monitor("T = %0t: i1 = %0d, i2 = %0d, i3 = %0d, x1 = %0d, x2 = %0d, x3 = %0d", $time,
i1, i2, i3, x1, x2, x3); i1 = 3; i2 = 2; #4 i3 = 7; end
initial
begin
#10;
// Parallel
block fork
x1 = i1; #2
x2 = i2; #5
x3 = i3; join
#15 x1 = i1 +
i2; end endmodule
Output:
T = 0: i1 = 3, i2 = 2, i3 = x, x1 = x, x2 = x, x3 = x
T = 4: i1 = 3, i2 = 2, i3 = 7, x1 = x, x2 = x, x3 = x
T = 10: i1 = 3, i2 = 2, i3 = 7, x1 = 3, x2 = x, x3 = x
T = 12: i1 = 3, i2 = 2, i3 = 7, x1 = 3, x2 = 2, x3 = x
T = 15: i1 = 3, i2 = 2, i3 = 7, x1 = 3, x2 = 2, x3 = 7
T = 30: i1 = 3, i2 = 2, i3 = 7, x1 = 5, x2 = 2, x3 = 7

Named blocks, and disabling named blocks


Both sequential and parallel blocks can be named and variables in a named block can be accessed by
referring to its hierarchical name. The named blocks can also be terminated using the ‘disable’ keyword.
Named block example:
module tb; reg
[3:0] i1, i2, i3;
reg [3:0] x1, x2, x3;

// sequential block
initial begin: seq_blk1
$monitor("T = %0t: i1 =
%0d, i2 = %0d, i3 = %0d, x1
= %0d, x2 = %0d, x3 = %0d",
$time, i1, i2, i3, x1, x2,
x3); i1 = 3; i2 =
2; #4 i3 = 7; end
initial begin: seq_blk2
#10;
// Parallel
block fork:
par_blk1 x1 =
i1; #2 x2 =
i2; #5 x3 =
i3; join
#15 x1 = i1 +
i2; end
endmodule
Output:
T = 0: i1 = 3, i2 = 2, i3 = x, x1 = x, x2 = x, x3 = x
T = 4: i1 = 3, i2 = 2, i3 = 7, x1 = x, x2 = x, x3 = x
T = 10: i1 = 3, i2 = 2, i3 = 7, x1 = 3, x2 = x, x3 = x
T = 12: i1 = 3, i2 = 2, i3 = 7, x1 = 3, x2 = 2, x3 = x
T = 15: i1 = 3, i2 = 2, i3 = 7, x1 = 3, x2 = 2, x3 = 7
T = 30: i1 = 3, i2 = 2, i3 = 7, x1 = 5, x2 = 2, x3 = 7
Disable named block
example: module tb;
integer count = 0;

// Disable below count_loop


block initial begin begin:
count_loop forever begin
count++;
$display("At T = %0t: count = %0d", $time, count);
if(count == 10) disable count_loop;

#5;
end
end end
endmodule
Output:
At T = 0: count = 1
At T = 5: count = 2
At T = 10: count = 3
At T = 15: count = 4
At T = 20: count = 5
At T = 25: count = 6
At T = 30: count = 7
At T = 35: count = 8
At T = 40: count = 9
At T = 45: count = 10

Switch Level Modeling


The switch level modeling is used to model digital circuits at the MOS level transistor. In this era, digital
circuits have become more complex and involve millions of transistors, so modeling at the transistor level
is rarely used by the designer. Hence, mostly higher abstraction levels are used for design description.
NMOS and PMOS switches
The keywords ‘nmos’ and ‘pmos’ are used to model NMOS and PMOS respectively.

Example:
module switch_modeling (
input d_in, ctrl,
output p_out, n_out);

pmos p1(p_out, d_in, ctrl);


nmos n1(n_out, d_in, ctrl);

endmodul
e
Output:
At time = 0: ctrl = 0, d_in = 0, p_out = 0, n_out = z
At time = 5: ctrl = 1, d_in = 0, p_out = z, n_out = 0
At time = 10: ctrl = 1, d_in = 1, p_out = z, n_out = 1
At time = 15: ctrl = 0, d_in = 1, p_out = 1, n_out = z
At time = 20: ctrl = 0, d_in = 0, p_out = 0, n_out = z

CMOS switch
A CMOS is modeled with a combination of NMOS and PMOS devices.

cmos
module cmos_modeling (
input d_in, p_ctrl, n_ctrl,
output out);

cmos p1(out, d_in, p_ctrl, n_ctrl);

endmodu
le
output:
At time = 0: d_in = 0, p_ctrl = 0, n_ctrl = 0, out = 0
At time = 5: d_in = 0, p_ctrl = 1, n_ctrl = 0, out = 0
At time = 10: d_in = 1, p_ctrl = 1, n_ctrl = 0, out = 1
At time = 15: d_in = 0, p_ctrl = 1, n_ctrl = 0, out = 0
At time = 20: d_in = 0, p_ctrl = 1, n_ctrl = 1, out = 0
At time = 25: d_in = 1, p_ctrl = 1, n_ctrl = 1, out = 1
At time = 30: d_in = 0, p_ctrl = 1, n_ctrl = 1, out = 0

Power and ground


The power (Vcc) and ground (GND) sources need to be defined in transistor level modeling to provide the
supply to the signals.
Keywords used: supply1(Vcc) and supply0 (GND)
module power_modeling (
output vcc, gnd
);
supply1 high;
supply0 low;
assign vcc =
high; assign gnd
= low; endmodule
Output:
At time = 0: vcc = 1, gnd = 0

Bidirectional switches
As of now, we have discussed unidirectional switches like PMOS, NMOS, and CMOS that conduct from
drain to source. Sometimes in the design, there is a need to have a bi-directional switch that can be driven
from any of the device sides.
Keywords used: tran, tranif0, and tranif1

All tran, tranif0, and tranif1 behaves as a buffer from in either of the device side,
Switch types Description

tran Connects in_out1 and in_out2

tranif0 Connects in_out1 and in_out2 if control signal is 0.


If the control signal is 1, then

1. If in_out1 is a driver signal, in_out2 = z (high impedence)


2. If in_out2 is a driver signal, in_out1 = z (high impedence)
tranif1 Connects in_out1 and in_out2 if control signal is 1.
If the control signal is 0, then

1. If in_out1 is a driver signal, in_out2 = z (high impedence)


2. If in_out2 is a driver signal, in_out1 = z (high impedence)

module bidirectional_modeling (
input in_out1, ctrl,
output in_out2, in_out2_if0, in_out2_if1);

tran t1(in_out1, in_out2); tranif0


t2(in_out1, in_out2_if0, ctrl);
tranif1 t3(in_out1, in_out2_if1, ctrl);
endmodule
Output:
At time = 0: ctrl = 0, in_out1 = 0, in_out2 = 0, in_out2_if0 = 0, in_out2_if1 = z
At time = 5: ctrl = 0, in_out1 = 1, in_out2 = 1, in_out2_if0 = 1, in_out2_if1 = z
At time = 10: ctrl = 1, in_out1 = 1, in_out2 = 1, in_out2_if0 = z, in_out2_if1 = 1
At time = 15: ctrl = 1, in_out1 = 0, in_out2 = 0, in_out2_if0 = z, in_out2_if1 = 0

Resistive switches
The resistive switches provide a high impedance from source to drain with a reduction in signal strength
as compared to regular switches. All PMOS, NMOS, CMOS, and bidirectional switches can be modeled
as resistive devices.
Keyword used: ‘r’ as a prefix to the regular switches.
Resistive switches Resistive keywords

Resistive pmos rpmos

Resistive nmos rnmos

Resistive cmos rcmos

Resistive tran rtran

Resistive tranif0 rtranif0

Resistive tranif1 rtranif1

module resistive_modeling (
input in_out1, ctrl,
output in_out2, in_out2_if0,
in_out2_if1); rtran t1(in_out1,
in_out2); rtranif0 t2(in_out1,
in_out2_if0, ctrl); rtranif1 t3(in_out1,
in_out2_if1, ctrl); endmodule
Output:
At time = 0: ctrl = 0, in_out1 = 0, in_out2 = 0, in_out2_if0 = 0, in_out2_if1 = z
At time = 5: ctrl = 0, in_out1 = 1, in_out2 = 1, in_out2_if0 = 1, in_out2_if1 = z
At time = 10: ctrl = 1, in_out1 = 1, in_out2 = 1, in_out2_if0 = z, in_out2_if1 = 1
At time = 15: ctrl = 1, in_out1 = 0, in_out2 = 0, in_out2_if0 = z, in_out2_if1 = 0

User-defined Primitives (UDP)


Till now we have seen standard primitives supported by Verilog such as AND, OR, NOT, NAND, etc.
However, Verilog also provides a facility to use their own customized primitives commonly known as
Userdefined Primitives (UDP). 1. UDP can not instantiate other modules or primitives.
2. UDPs are instantiated similar to gate-level primitives.
3. They have exactly one output that can have either of these states 0, 1, or x. The high
impedance z state can not be handled by UDP. For the given z input, it will be considered
as x.
4. They start with the primitive keyword and end with the endpremitive keyword. UDP can
have multiple input ports but only one output port and UDP should not be declared inside
the module block.
Syntax:
primitive <UDP name> ( <output name>, <input names>);

// port declaration output <output name>; input <input


names>; reg <output name>; // Applicable only for sequential UDP
(optional)

// UDP initialization
initial <output name> = <value>; // Applicable only for sequential UDP
(optional)
// UDP state table
table
<table entries>
endtable
endprimitive

UDP Rules
UDP follows the below rules 1. UDP can have only scalar input terminals (1 bit), but multiple

inputs are allowed.

2. UDP can have only one scalar input terminal (1 bit) and it must be the first to appear in
the terminal list. Multiple outputs are not allowed.
3. The inputs and output are declared with input and output keywords respectively. The
output for the sequential UDP must be declared as a reg.
4. The initial statement is used to initialize the state of sequential UDP.
5. UDPs are defined at the same level as modules and they can only be instantiated inside
the module like gate primitive (can not be defined inside the module).
6. Bidirectional port (inout) is not supported in UDP.
7. The state table entries can have 0,1 or x value. The z value is not handled, but for the
given z value, UDP treats it as an x value.
Following symbols are used to specify inputs and output values
Symbol Description

0 Logic 0

1 Logic 1

x Unknown. It can be either logic 0 or 1 and can be used as input/output or the current state of
sequential UDPs.

? Logic 0, 1 or x. It can not be the output of UDP

– No change, only allowed in the output of UDP

ab Change in value from a to b where a or b is either 0, 1, or x

* Same as ??, indicates any change in the input value


r Same as 01 -> rising edge on input

f Same as 10 -> falling edge on input

p Potential positive edge on input. either 0->1, 0->x, or x->1

n Potential falling edge on input. either 1->0, x->0, 1->x

Types of User-defined Primitives (UDP)


1. Combinational UDP
2. Sequential UDP

Combinational UDP
In combinational UDP, the logical combination of the inputs determines the output. On changing the state
of the input, the output sets the value based on the state table row.
Maximum number of input supported: 10
Table in combinational UDP
The state table is written in the ‘table’ and ‘endtable’ keywords that enclose all possible combinations of
the inputs and their corresponding output.
Syntax:
<input1 > < input2> < input3> ... <inputN > : <output >;

Example: 4:1 MUX with UDP

primitive mux_4_1 (y, s1, s0, i0, i1, i2, i3);


//port declaration
output y; input s1,
s0; input i0, i1,
i2, i3;
endtable endprimitive
module udp_tb;
reg s1, s0;
reg i0,i1,i2,i3;
wire y;
mux_4_1 mux(y, s1, s0, i0, i1, i2, i3);

initial
begin
$monitor("At time = %0t: {s1 = %b, s0 = %b} -> i3 = %0b, i2 = %0b ,i1 = %0b, i0 = %0b -> y =
%0b", $time, s1,s0,i3,i2,i1,i0,
y); {i3,i2,i1,i0} = 4'h5;
repeat(6) begin
{s1, s0} = $random;
#5;
end
s1 = 1'bx; s0 = 1;
#5;
s1 = 0; s0 =
1'bx; end
endmodule output:
At time = 0: {s1 = 0, s0 = 0} -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 1
At time = 5: {s1 = 0, s0 = 1} -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 0
At time = 15: {s1 = 1, s0 = 1} -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 0
At time = 20: {s1 = 0, s0 = 1} -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = 0
At time = 30: {s1 = x, s0 = 1} -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = x
At time = 35: {s1 = 0, s0 = x} -> i3 = 0, i2 = 1 ,i1 = 0, i0 = 1 -> y = x

Sequential UDP
In sequential UDP, the current input and the current output value determine the next output value. The
sequential UDP provides an efficient interface for sequential circuits (i.e. flip-flops and latches) modeling.

1. The output has to be declared as reg in sequential UDP


2. To initialize the output of sequential UDP, use the initial keyword.
3. Maximum number of input supported: 9 (since internal state treated as an input)
Table in sequential UDP
The state table is written in the table and endtable keywords that enclose all possible combinations of the
inputs and their corresponding output.
Syntax:
<input1 > < input2> < input3> ... <inputN > : <current_state > : <next_state >;

COPY
Types of sequential UDP
1. Level-sensitive sequential UDP
2. Edge-sensitive sequential UDP
Level-sensitive sequential UDP
The sequential UDP which is sensitive to input level is called level-sensitive sequential UDP.

In this example,
If reset = 1, output q is always 0.
If reset = 0, output q = d
If clock = 0, output q retains its value.
primitive latch (q, clock, reset, d);
//port declaration
output reg q; input
clock, reset, d;

// initialization
initial q = 0;
table
//clock reset d : q : q_next
? 1 ? : ? : 0; // reset condition
1 0 1 : ? : 1; // q = data
1 0 0 : ? : 0; // q = data
0 0 ? : ? : -; // retain previous state for clock =
0 endtable endprimitive

module udp_tb; reg


clock, reset, d;
wire q;
latch lch(q, clock, reset, d);

initial clock = 0;
always #5 clock=~clock;

initial
begin
$monitor("At time = %0t: clock = %b, reset = %b, d = %b, q = %b", $time, clock, reset, d,
q); reset = 1; #10 reset = 0; d = 1; #20; d = 0; #10 $finish; end
endmodule

At time = 0: clock = 0, reset = 1, d = x, q = 0


At time = 5: clock = 1, reset = 1, d = x, q = 0
At time = 10: clock = 0, reset = 0, d = 1, q = 0
At time = 15: clock = 1, reset = 0, d = 1, q = 1
At time = 20: clock = 0, reset = 0, d = 1, q = 1
At time = 25: clock = 1, reset = 0, d = 1, q = 1
At time = 30: clock = 0, reset = 0, d = 0, q = 1
At time = 35: clock = 1, reset = 0, d = 0, q = 0
Simulation complete via $finish(1) at time 40 NS + 0

Edge-sensitive sequential UDP


The sequential UDP which is sensitive to input edge transition called edge-sensitive sequential UDP.

In this example,
If reset = 1, output q is always 0.
If reset = 0, output q = d on the negative transition of clock i.e. from 1 to 0.
If the clock changes to an unknown state, output q = no change
On the positive transition of the clock, output q = no change

Symbols Description

(10) A negative edge transition from 1 to 0.

(1x) A transition from 1 to x.

(??) A transition from 0 to 1, 0 to x, 1 to 0, 1 to x or stable state.

(0?) A transition from 0 to 0, 0 to 1 or 0 to x.


primitive dff_nedge (q, clock, reset, d);
//port declaration
output reg q; input
clock, reset, d;

// initialization
initial q = 0;
table
//clock reset d : q : q_next
? 1 ? : ? : 0; // reset condition
? (10) ? : ? : -; // ignoring negative transiton of
reset
(10) 0 1 : ? : 1; // q = data
(10) 0 0 : ? : 0; // q = data

(1x) 0 ? : ? : -; // for unknown clock transition, hold previous state of


q
(0?) 0 ? : ? : -; // ignoring positive transiton of
clock (x1) 0 ? : ? : -; // ignoring positive transiton
of clock
? 0 (??) : ? : -; // ignoring changes in d for no changes in clock
endtable endprimitive

module udp_tb; reg


clock, reset, d;
wire q;
dff_nedge dff(q, clock, reset, d);

initial clock = 0;
always #5 clock=~clock;

initial
begin
$monitor("At time = %0t: clock = %b, reset = %b, d = %b, q = %b", $time, clock, reset, d,
q); reset = 1; #10 reset = 0; d = 1; #20; d = 0; #10 $finish; end
endmodule

At time = 0: clock = 0, reset = 1, d = x, q = 0


At time = 5: clock = 1, reset = 1, d = x, q = 0
At time = 10: clock = 0, reset = 0, d = 1, q = 0
At time = 15: clock = 1, reset = 0, d = 1, q = 0
At time = 20: clock = 0, reset = 0, d = 1, q = 1
At time = 25: clock = 1, reset = 0, d = 1, q = 1
At time = 30: clock = 0, reset = 0, d = 0, q = 1
At time = 35: clock = 1, reset = 0, d = 0, q = 1
Simulation complete via $finish(1) at time 40 NS + 0

Tasks and Functions in Verilog


A function or task is a group of statements that performs some specific action. Both of them can be
called at various points to perform a certain operation. They are also used to break large code into
smaller pieces to make it easier to read and debug.
Functions in Verilog
A function that does not consume simulation time, returns a single value or an expression, and may or may
not take arguments.

Keywords used: function and endfunction.

Syntax:
// Style 1 function <return_type> <function_name> (input <port_list>,inout <port_list>, output
<port_list>);
...
return <value or
expression> endfunction
// Style 2
function <return_type> <function_name>
(); input <port_list>; inout
<port_list>; output <port_list>;
...
return <value or expression>
endfunction
Example: module
function_example;
function compare(input int a, b);
if(a>b)
$display("a is greater than b");
else if(a<b)
$display("a is less than b");
else
$display("a is equal to b");
return 1; // Not mandatory to write
endfunction
initial
begin
compare(10,10);
compare(5, 9);
compare(9, 5);
end endmodule
Output:
a is equal to b
a is less than
b
a is greater than b

Tasks in Verilog
A task that may or may not consume simulation time, returns values as output or inout argument type, and
may or may not take arguments.

Keywords used: task and endtask.


Syntax:
// Style 1 task <task_name> (input <port_list>, inout <port_list>, output
<port_list>);
...
endtas
k
// Style 2 task
<task_name> ();
input <port_list>;
inout <port_list>;
output <port_list>;
...
endtask

Example
:
module task_example;

task compare(input int a, b, output done);


if(a>b)
$display("a is greater than b");
else if(a<b)
$display("a is less than b");
else
$display("a is equal to b");
#10; done
= 1; endtask
initial begin bit
done;
compare(10,10, done);
if(done) $display("comparison completed at time = %0t", $time);
compare(5,9, done);
if(done) $display("comparison completed at time = %0t", $time);
compare(9,5, done);
if(done) $display("comparison completed at time = %0t",
$time); end endmodule Output:
a is equal to b
comparison completed at time = 10
a is less than b
comparison completed at time = 20
a is greater than b
comparison completed at time = 30

Similarities between function and task


1. Both are static in nature by default. Refer to System Verilog tasks and functions for more
details.
2. Both support default arguments and arguments have input direction by default unless it is
specified.
3. Multiple statements can be written without using a begin .. end block. Difference between
function and task
Function Task

Can not contain simulation delay, so execute in the same can or can not contain a simulation time
time unit. It can not contain @, wait, negedge, and delay(#), @, wait, negedge, and posedge
posedge time-controlled statements. time-controlled statements.

Can return a single value Can return multiple values as output or inout
argument. It can not return a value that a
function can do.

Can not call another task Can call another function or task

Verilog Scheduling semantics


The Verilog scheduling semantics is used to describe Verilog language element’s behavior and their
interaction with each other. This interaction is described for event execution and its scheduling. It is
important to note that Verilog is like a parallel programming language in terms of blocks or process
executions. Hence, the user should know the guaranteed or indeterminate execution order while using it.
Verilog processes include elements or statements such as always and initial procedural blocks, continuous
assignments, asynchronous tasks, modules, and primitives.
Processes are ultimately sensitive to event updates. The terminology of event update describes any change
in a variable or net state change. For example, always @(*) is sensitive for all variables or nets used. Any
change in variable or net is considered as an event update. Another example could be having multiple
initial blocks in the code. The evaluation order of these initial blocks can be arbitrary depending on
simulator implementation. Programming Language Interface (PLI) callbacks are used to call a user-
defined external routine of a foreign language. Such PLI callbacks are also considered as an event that has
to be evaluated.
Event Simulation
The Verilog language works on the evaluation and execution of such events. So, it becomes important to
understand how these events will be evaluated and executed. The events are scheduled in a particular order
to render an event execution systematically.
The design takes some time cycles to respond to the driven variables. The simulator models the actual time
for the design description that is commonly known as simulation time. A single time cycle or slot is divided
into various regions and that helps out to schedule the events. The scheduling an event terminology
describes keeping all such events in an event queue and processing them in the correct order. The simulator
executes all the events in the current time slot and then moves to the next time slot. This ensures that
simulation always proceeds forward in time. The events in a simulation time slot are classified as

1. Active
2. Inactive
3. NBA (Non-Blocking Assignment)
4. Monitor
5. Future
Active events
The active events occur at the current simulation time and can be processed in any order. Evaluation
of active event

1. Inputs and update outputs of Verilog primitives


2. Right Hand Side (RHS) of all nonblocking assignments and execute to update Left Hand
Side (LHS) in the NBA region.
Execution of active events

1. Blocking assignments of all modules 2.


Continuous assignments of all modules
3. $display and $finish commands.
Inactive events
The inactive events occur at the current simulation time and shall be processed after processing all the
active events. An explicit #0 delay is scheduled in the inactive region of the current time slot.
NBA events
The non-blocking assignment events are mainly used to update LHS of all nonblocking assignments whose
RHS were evaluated in the active region.
Monitor events
The monitor events are processed after processing all active, inactive, and non-blocking assignment events.
The $monitor and $strobe system tasks are executed as a part of monitor events.
Future events
The future events occur at some future simulation time.
Nondeterminism
It is important to focus on the situation when multiple processes are triggered simultaneously and how
they are going to execute? The IEEE standard does not specify the execution order. Hence, the order of
execution is arbitrary.

Let’s understand nondeterminism with the below case:


module
non_det; reg
data;
initial data = 0;
initial data = 1;
endmodule

In this scenario, since both procedural initial blocks are executed simultaneously, order execution of initial
blocks is nondeterministic and the user can not control it.
System Tasks in Verilog
Difference between $display and $monitor
1. The $monitor can continuously monitor the changes in mentioned variables or signals
values whereas $display prints mentioned variables or signals values when it is called.
2. $monitor should be invoked only once whereas $display can be invoked multiple times.
3. In case multiple $monitor tasks have called only the last $monitor statement will be active
and previous statements will be overridden.

module display_tb;
reg [3:0] d1, d2;
initial
begin d1 = 4;
d2 = 5;

#5 d1 = 2; d2 =
3; end
initial
begin
$display("At time %0t: {$display A} -> d1 = %0d, d2 = %0d", $time, d1, d2);
$monitor("At time %0t: {$monitor A} -> d1 = %0d, d2 = %0d", $time, d1, d2);
$write("At time %0t: {$write A} -> d1 = %0d, d2 = %0d", $time, d1, d2);
$strobe("At time %0t: {$strobe A} -> d1 = %0d, d2 = %0d", $time, d1, d2);

#5;

$display("At time %0t: {$display B} -> d1 = %0d, d2 = %0d", $time, d1, d2);
// $monitor is missing -> Observe print for $monitor A
$write("At time %0t: {$write B} -> d1 = %0d, d2 = %0d", $time, d1, d2);
$strobe("At time %0t: {$strobe B} -> d1 = %0d, d2 = %0d", $time, d1, d2);
end endmodule
Output:
At time 0: {$display A} -> d1 = 4, d2 = 5
At time 0: {$write A} -> d1 = 4, d2 = 5At time 0: {$monitor A} -> d1 = 4, d2 = 5
At time 0: {$strobe A} -> d1 = 4, d2 = 5
At time 5: {$display B} -> d1 = 2, d2 = 3
At time 5: {$write B} -> d1 = 2, d2 = 3At time 5: {$strobe B} -> d1 = 2, d2 = 3
At time 5: {$monitor A} -> d1 = 2, d2 = 3

Simulation controlling system tasks


$stop – It is used to stop or suspend the running simulation. It is generally used for debugging purposes
and puts the simulation mode in interactive mode so that designers can examine signal values.
$finish – It is used to terminate the simulation.
Random number generator system task
$random – It returns a 32-bit signed integer i.e. it can be positive or negative integer.
Example:
reg [7:0] = $random;
integer value = $random;
Compiler directives in Verilog
The compiler directives are similar to C language preprocessor directives that may be used to specify
certain information and ask the compiler to process it. The character ` is used prior to the specific keyword.
Its declaration can be put outside of the module and its scope may not be limited to a single module.
Syntax: `<keyword>
Compiler Description
directives

`define To define text macros. (Similar to #define in C language)

`include To include entire content from another Verilog file into the existing file during
compilation. (Similar to #include in C language).

`ifdef…`endif Conditional compiler directives that behave as if..else conditional statements.


`ifdef..`else..`endif

`timescale To specify time units and precision for the module

‘ifdef, ‘else, and ‘endif Directives


module dut(
input i1, i2,
output out
);
`ifdef AND_OP
assign out = i1 & i2;
`else
assign out = i1 | i2;
`endif
endmodule
COPY
Testbench:
module tb;
reg i1, i2;
wire out;
dut and_or(i1, i2, out);
initial begin
`ifdef AND_OP
$monitor("[AND Operation] At time T = %0t: i1 = %b, i2 = %b, out = %b", $time, i1, i2,
out); `else
$monitor("[OR Operation] At time T = %0t: i1 = %b, i2 = %b, out = %b", $time, i1, i2,
out);
`endif i1
= 0; i2 = 0;
#1;
i1 = 0; i2 = 1;
#1;
i1 = 1; i2 = 0;
#1; i1 =
1; i2 = 1; end
endmodule
Output:
[AND Operation] At time T = 0: i1 = 0, i2 = 0, out = 0
[AND Operation] At time T = 1: i1 = 0, i2 = 1, out = 0
[AND Operation] At time T = 2: i1 = 1, i2 = 0, out = 0
[AND Operation] At time T = 3: i1 = 1, i2 = 1, out = 1
If the AND_OP macro is specified as a run option, a corresponding ‘AND’ operation is performed
otherwise, the ‘OR’ operation is executed by default.
`timescale
Syntax:
`timescale <time_unit>/<time_precision>

time_unit: Measurement for simulation time and delay.


time_precision: Rounding the simulation time values means the simulator can at least advance by a
specified value.
Examples
`timescale 1ns/1ns
`timescale
1ns/1ns module
tb; initial
begin
$display ("At time T=%0t",
$realtime);
#0.45;
$display ("At time T=%0t",
$realtime); #0.50;
$display ("At time T=%0t",
$realtime); #0.55;
$display ("At time T=%0t",
$realtime); end endmodule
Output:
At time T=0
At time T=0
At time T=1
At time T=2

`timescale 1ns/1ps
`timescale
1ns/1ps module
tb; initial
begin
$display ("At time T=%0t",
$realtime); #0.45;
$display ("At time T=%0t",
$realtime); #0.50;
$display ("At time T=%0t",
$realtime);
#0.55;
$display ("At time T=%0t",
$realtime); end endmodule
Output:
At time T=0
At time T=450
At time T=950
At time T=1500

`timescale 10ns/1ns
`timescale
10ns/1ns module
tb; initial
begin
$display ("At time T=%0t",
$realtime);
#0.45;
$display ("At time T=%0t",
$realtime);
#0.50;
$display ("At time T=%0t",
$realtime);
#0.55;
$display ("At time T=%0t",
$realtime); end endmodule
Output:
At time T=0
At time T=5
At time T=10
At time T=16
Explaination:
`timescale 1ns/1ns: Since precision = 1ns, the simulator will advance its time if the delay value is greater
or equal to 0.5ns. Thus, time advancement does not happen for 0.45ns delay.
`timescale 1ns/1ps: Since precision = 1ps, the simulator will advance for all the cases.
`timescale 10ns/1ns: Since precision = 1ns, the simulator will advance for all the cases. Here, the delay
involved is multiplied with mentioned time units and then it rounds off based on precision to calculate
actual simulation advancement. Actual simulation time

1. (0.45*10) = 4.5ns — Rounded off to 5ns 2.


(0.50*10) = 5ns
3. (0.55*10) = 5.5ns — Rounded off to 6ns
Note: if the timescale is not mentioned then the simulator takes default timescale values.

Parameters and overriding parameters


Verilog parameter is used to pass a constant to the module when it is instantiated. It is not considered under
net or reg data types. The parameter value can not be changed at run time. Verilog allows changing
parameter values during compilation time using the ‘defparam’ keyword. The ‘defparam’ is used as
overriding the parameter value using a hierarchical name of the module instance. The parameter value can
be updated in two ways

1. Pass constant or define based value


2. Use the ‘defparam’ keyword Syntax:
Module <module_name> #(<parameter list>) <port_list>;

The following example described how parameters are used.


module param_example #(parameter DATA_WIDTH = 8, ID_WIDTH = 32) (data,
id); input bit [DATA_WIDTH-1: 0] data; input bit [ID_WIDTH-1: 0] id;

initial begin
// Display width values
$display("DATA_WIDTH = %0d, ID_WIDTH = %0d", DATA_WIDTH, ID_WIDTH);

// Display variables
$display("data = %0d, id = %0d", data, id);
$display("-------------------------------------");
end endmodule
COPY
Testbench:
`define D_WIDTH 32
`define I_WIDTH 8

module tb_top;

param_example p1(.data(2), .id(1)); // without passing parameter


param_example #(4, 16) p2(.data(3), .id(2)); // constant parameter passing
param_example #(`D_WIDTH, `I_WIDTH) p3(.data(6), .id(3)); // macro define
based parameter passing
param_example p4(.data(9),
.id(4)); // Change parameter value
using defparam defparam p4.DATA_WIDTH =
10; defparam p4.ID_WIDTH = 16;
endmodule
COPY
Output:
DATA_WIDTH = 8, ID_WIDTH = 32
data = 2, id = 1
-------------------------------------
DATA_WIDTH = 4, ID_WIDTH = 16
data = 3, id = 2
-------------------------------------
DATA_WIDTH = 32, ID_WIDTH = 8
data = 6, id = 3
-------------------------------------
DATA_WIDTH = 10, ID_WIDTH = 16
data = 9, id = 4
-------------------------------------

Procedural continuous assignments


Till now we have seen two types of assignments i.e. continuous assignment and procedural assignment.
The continuous assignment is used to drive net data type variables using the ‘assign’ statements whereas
procedural assignments are used to drive reg data type variables using initial and always block statements.
Verilog also provides a third type of assignment i.e. procedural continuous assignment that drives net or
reg data type variables for a certain period of time by overriding the existing assignments. There are two
types of procedural continuous assignments

1. assign and deassign


2. force and release

assign and deassign


The assign and deassign statements control reg type variable values by overriding existing procedural
assignments for a limited time period. After the execution of the deassign statement, another procedural
or procedural continuous assignment can change the variable value once again, till then the previous value
can hold.
module
assign_deassign_ex;
reg [3:0] d1; initial
begin
$monitor("At time T = %0t: d1 = %0d", $time,
d1); d1 = 5; #20 d1 = 7; end
initial begin
#5; assign d1
= 3; #5
deassign d1;
$display("At time T = %0t: deassign d1",
$time); end endmodule
Output:
At time T = 0: d1 = 5
At time T = 5: d1 = 3
At time T = 10: deassign d1
At time T = 20: d1 = 7

The d1 = 3 is assigned at #5 time units and deassign at #10 time units.The d1 = 3 retains till next assignment
d1 = 7 happens at 20 time units.

force and release


The force and release statements control net and reg data type variable values by overriding existing
procedural, continuous or procedural continuous assignments for a limited time period. After the execution
of the release statement for the reg data type variable, another procedural or procedural continuous
assignment can change the variable value once again, till then the previous value can hold. The value of
the previous continuous assignment retains in the case of the net data type variable.
module
assign_deassign_ex;
reg [3:0] d1; wire
[3:0] d2;

assign d2 = 2;
initial begin
$monitor("At time T = %0t: d1 = %0d, d2 = %0d", $time, d1,
d2); d1 = 5; #20 d1 = 7; end initial begin
#5;
$display("At time T = %0t: force d1 and d2",
$time); force d1 = 3; force d2 = 4; #5
release d1; release d2;
$display("At time T = %0t: release d1 and d2",
$time); end endmodule
Output:
At time T = 0: d1 = 5, d2 = 2
At time T = 5: force d1 and d2
At time T = 5: d1 = 3, d2 = 4
At time T = 10: release d1 and d2
At time T = 10: d1 = 3, d2 = 2
At time T = 20: d1 = 7, d2 = 2

The d1 belongs to the reg data type and d2 belongs to the net data type. Both variables are forced at #5
time units and released at #10 time units Once, it is released,

1. The d1 value remains the same (d1 = 3) until it is changed to d1 = 7 at 20 time units.
2. The d2 value holds a previously assigned value using continuous assignment (d2 = 2).

Strength in Verilog
The strength is used to have more accurate modeling that specifies a value on a net.
The strength of a net is derived based on the strength of multiple drivers and the final strength will get
the strength of the strongest driver. Verilog provides three types of strength.

1. Driving strength
2. Capacitive strength
3. High impedance
Strength type Name of strength

Driving strength Supply drive, strong drive, pull drive, weak drive

Capacitive strength Large capacitive, medium capacitive, small capacitive

impedance High impedance

Keywords used in Verilog strength


Strength level Name of strength keywords

0 High impedance highz0, highz1

1 Small capacitive small

2 Medium drive medium

3 Weak drive weak0, weak1

4 Large capacitive large


5 Pull drive pull0, pull1

6 Strong drive strong0, strong1

7 Supply drive supply0, supply1

Note:
1. The default strength is the strong drive.
2. The default strength for pullup and pulldown gates is the pull drive.
3. The default strength for trireg is medium capacitive.
4. The default strength for supply nets is the supply driver.
5. A net can not be driven with a high impedance strength.
6. The (highz1, highz0) and (highz0, highz1) strength combinations are not allowed.
7. If a net is driven with 0 (low) and 1 (high) simultaneously, the result will be ‘x’.
Syntax:
( <strength0>, <strength1> )
( <strength1>, <strength0> )
cap_strength

Where,
strength1 = highz1, pull1, strong1, supply1, weak1
strength0 = highz0, pull0, strong0, supply0, weak0
cap_strength = large, medium, small

strength0: When net drivers drive the value as 0, then strength is specified by strength0.
strength1: When net drivers drive the value as 1, then strength is specified by strength1.
cap_strength: For trireg nets, the only cap_strenth is applicable.
For example:
or (strong0, weak1) o1(out, i1, i2)
assign (weak0, strong1) out = i1 & i2;
trireg (large) tr1
Verilog strength Example
When below ‘OR’ and ‘AND’ operation drives the same output, the final strength of the output be the
strength of the strongest driver. or (supply1, pull0) o1(out, i1, i2) and (strong1, supply0) a1(out, i1, i2)

Input driven Output strength Explanation

i0 = 0, i1 = 0 out = 0 (pull0) pull0 strength (= 5) > weak0 strength (=3)

i0 = 0, i1 = 1 out = x The out net is driven as 0 (supply0) and 1 (supply1) simultaneously.


i0 = 1, i1 = 0 out = x The out net is driven as 0 (supply0) and 1 (supply1) simultaneously.

i0 = 1, i1 = 1 out = 1 (supply1) supply1 strength (= 7) > strong1 strength (=6)

module strength(
input i1, i2,
output out);

//assign (supply1, pull0) out = (i1 | i2);


//assign (strong1, supply0) out = i1 & i2;

or (supply1, pull0) o1(out, i1, i2);


and (strong1, supply0) a1(out, i1, i2);

endmodul
e
Output:
At time = 0: i1 = 0, i2 = 0 -> out = 0
At time = 1: i1 = 0, i2 = 1 -> out = x
At time = 2: i1 = 1, i2 = 0 -> out = x
At time = 3: i1 = 1, i2 = 1 -> out = 1

Generate Blocks in Verilog


The generate statement in Verilog is a very useful construct that generates synthesizable code during
elaboration time dynamically. The simulator provides an elaborated code of the ‘generate’ block. It
provides
the below facilities:
1. To generate multiple module instances or code repetition.
2. Conditionally instantiate a block of code based on the Verilog parameter, however, the
parameter is not permitted in the generate statement.
It basically provides control on variables, functions, tasks, and instantiation declarations. A generate
block
has been written within generate and endgenerate keywords.

Types of generate instantiation


1. Modules
2. Verilog gate primitives
3. Continuous assignments
4. Initial and always blocks
5. User-defined primitives

Let’s see what is allowed within the scope of a generate block.


A. Data types
1. integer, real
2. net, reg
3. time, realtime
4. event
B. Function and task

Note: Function and task are not allowed within a generate loop, but they are allowed in generate block.
Below module items/declarations are not allowed within the scope of a generate block
1. Port declarations like input, output, and inout
2. specify blocks
3. parameters and local parameters

Methods to write generate statements


1. Generate loop
2. Generate conditional (includes generate if-else and generate case)

Generate loop
The generate loop is similar to the for loop statement, but it uses genvar keyword as a loop variable.
• The genvar keyword is only used during the evaluation of generate block and does not exist
during the simulation of the design. It needs to be used by a generate loop.
• Generate loop provides flexibility to reduce code lines by replacing repetitive statements to a
single statement like for loop.
• Similar to a for loop, generate loops also can be nested with different genvar as an index variable.
Generate conditional
A generate block allows conditionally instantiated using if-else-if construct and case keyword.

Example: generate If-else


In the below example, based on parameter sel full adder or half-adder design is instantiated. By default,
parameter sel = 0 means half adder will be instantiated. But from the testbench code, parameter sel = 1 is
passed to instantiate full adder. $display can not be used within generate block without initial block,
otherwise, it throws an error '$display' is an invalid generate scope construct.

module half_adder(
input a, b,
output sum, cout
);
assign {sum, cout} = {a^b, (a & b)};
//or
//assign sum = a^b;
//assign cout = a & b;
endmodule
module full_adder(

output sum, cout


);
assign {sum, cout} = {a^b^cin, ((a & b) | (b & cin) | (a & cin))};
//or
//assign sum = a^b^cin;
//assign cout = (a & b) | (b & cin) | (a & cin);
endmodule79 | P a g e
module gen_if_ex #(parameter sel = 0)(
input A, B, Cin,
output S, Cout);
generate
if(sel) begin
initial $display("Full Adder is selected");
full_adder fa(A, B, Cin, S, Cout);
end
else begin
initial $display("Half Adder is selected");
half_adder ha(A, B, S, Cout);
end
endgenerate
endmodule
COPY
Output:
Full Adder is selected
A = 0: B = 1, Cin = 1 --> S = 0, Cout = 1
A = 1: B = 1, Cin = 1 --> S = 1, Cout = 1
A = 1: B = 0, Cin = 1 --> S = 0, Cout = 1
xmsim: *W,RNQUIE: Simulation is complete.

Example: generate case


Similarly, the above example if-else generate block can alternatively use case statement as specified in the
below example.
module half_adder(
input a, b,
output sum, cout
);
assign {sum, cout} = {a^b, (a & b)};
//or
//assign sum = a^b;
//assign cout = a & b;
endmodule
module full_adder(
input a, b, cin,
output sum, cout
);
assign {sum, cout} = {a^b^cin, ((a & b) | (b & cin) | (a & cin))};
//or
//assign sum = a^b^cin;
//assign cout = (a & b) | (b & cin) | (a & cin);
endmodule
module gen_if_ex #(parameter sel = 0)(
input A, B, Cin,
output S, Cout);
generate
case(sel)
0: begin
initial $display("Full Adder is selected");
half_adder ha(A, B, S, Cout);
end
1: begin
initial $display("Full Adder is selected");
full_adder fa(A, B, Cin, S, Cout);
end
endcase
endgenerate
endmodule
COPY
Output:
Full Adder is selected
A = 0: B = 1, Cin = 1 --> S = 0, Cout = 1
A = 1: B = 1, Cin = 1 --> S = 1, Cout = 1
A = 1: B = 0, Cin = 1 --> S = 0, Cout = 1
xmsim: *W,RNQUIE: Simulation is complete.

You might also like