Verilog Basic To Advance
Verilog Basic To Advance
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.
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
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
Ternary Two separate operators appear to separate three operands Z = (a < b)?x:y;
2.6-Identifiers
The identifiers are the names given to the objects that can be referenced in the design.
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
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:
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:
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
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"
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:
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’.
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
Note:
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
Design declaration:
module mux_2_1(
input sel, input
i0, i1, output
y);
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
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
and and g(out, i1, i2, Performs AND 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.
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
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);
Time = 3: i1 = 1, i2 = 0, ctrl = 1
[single delay] y1 = 1, out1 = 1
[two delays] y2 = 1, out2 = 1
[Three delays] out3 = 1
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);
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.
B. Implicit continuous
assignment delay wire #5 result
= i1 ^ i2;
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
Example:
module arithmetic_op;
reg [3:0] i1, i2;
initial begin i1
= 4'h6; i2 =
4'h2;
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 or
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
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
7. Shift operators
Operators Number of operands Description
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.
| 1 reduction or
^ 1 reduction xor
~| 1 reduction nor
^~ or ~^ 1 reduction xnor
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
The multiple operands can be appended using a concatenation operator. The operands have to be written in
braces and separate themselves with commas.
Note:
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
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;
1. initial block
2. always block
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
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
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
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@(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. 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;
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 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
A less than or equal to ‘<=’ is used as a symbol for the non-blocking assignment operator.
Note
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 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)
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;
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
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;
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
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
Event OR control Multiple events are declared using the ‘or’ keyword or comma ‘,’
symbol.
@(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;
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;
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
...
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
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;
#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
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
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
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 ?
2'bx1: out = 8;
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
initial begin
// Update array
for (int i = 0; i < $size(array); i++)
begin array[i] = i*i; end
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
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
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
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,
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
// 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;
#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
Example:
module switch_modeling (
input d_in, ctrl,
output p_out, n_out);
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);
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
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
module bidirectional_modeling (
input in_out1, ctrl,
output in_out2, in_out2_if0, in_out2_if1);
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
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
// 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
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.
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 >;
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.
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
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
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
// 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
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
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.
Example
:
module task_example;
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
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
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
`include To include entire content from another Verilog file into the existing file during
compilation. (Similar to #include in C language).
`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
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;
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.
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
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)
module strength(
input i1, i2,
output out);
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
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
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.
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(