VERILOG
HDL
With the advent of VLSI technology and increased usage of digital circuits, a designer has to design single chips with millions of transistors. It became almost impossible to verify these circuits of high complexity on breadboard. Hence Computer-aided techniques became critical for verification and design of VLSI digital circuits. As designs got larger and more complex, logic simulation assumed an important role in the design process. Designers could iron out functional bugs in the architecture before the chip was designed further. All these factors which led to the evolution of Computer-Aided Digital Design, intern led to the emergence of Hardware Description Languages.
Verilog HDL and VHDL are the popular HDLs.Today; Verilog HDL is an accepted IEEE standard. In 1995, the original standard IEEE 1364-1995 was approved. IEEE 1364-2001 is the latest Verilog HDL standard that made significant improvements to the original standard.
The
VLSI Design Flow
The VLSI IC circuits design flow is shown in the figure below. The various level of design are numbered and the gray colored blocks show processes in the design flow.
The VLSI IC circuits design flow is shown in the figure below. The various level of design are numbered and the gray colored blocks show processes in the design flow.


Ø Behavioral
description is then created to analyze the design in terms of functionality,
performance, compliance to given standards, and other specifications.
Ø RTL
description is done using HDLs. This RTL description is simulated to test
functionality. From here onwards we need the help of EDA tools.
Ø RTL
description is then converted to a gate-level net list using logic synthesis
tools. A gate-level net list is a description of the circuit in terms of gates
and connections between them, which are made in such a way that they meet the
timing, power and area specifications.
Ø Finally
a physical layout is made, which will be verified and then sent to fabrication.
Importance
of HDLs
Ø RTL
descriptions, independent of specific fabrication technology can be made a
verified.
Ø functional
verification of the design can be done early in the design cycle.
Ø Better
representation of design due to simplicity of HDLs when compared to gate-level
schematics.
Ø Modification
and optimization of the design became easy with HDLs.
Ø Cuts
down design cycle time significantly because the chance of a functional bug at
a later stage in the design-flow is minimal.
Verilog
HDL
Verilog HDL is one of the most used HDLs. It can be used to describe designs at four levels of abstraction:
Verilog HDL is one of the most used HDLs. It can be used to describe designs at four levels of abstraction:
1. Algorithmic
level.
2. Register
transfer level (RTL).
3. Gate
level.
4. Switch
level (the switches are MOS transistors inside gates).
Why Verilog?
Ø Easy
to learn and easy to use, due to its similarity in syntax to that of the C
programming language.
Ø Different
levels of abstraction can be mixed in the same design.
Ø Availability
of Verilog HDL libraries for post-logic synthesis simulation.
Ø Most
of the synthesis tools support Verilog HDL.
Ø The
Programming Language Interface (PLI) is a powerful feature that allows
the user to write custom C code to interact with the internal data structures
of Verilog. Designers can customize a Verilog HDL simulator to their needs with
the PLI.
Digital
design methods
Digital design methods are of two types:
Digital design methods are of two types:
1. Top-down
design method: In this design method we first define
the top-level block and then we build necessary sub-blocks, which are required
to build the top-level block. Then the sub-blocks are divided further into
smaller-blocks, and so on. The bottom level blocks are called as leaf cells. By
saying bottom level it means that the leaf cell cannot be divided further.
2. Bottom-up
design method: In this design method we first find
the bottom leaf cells, and then start building upper sub-blocks and building so
on, we reach the top-level block of the design.
In
general a combination of both types is used. This type of design methods helps
the design architects, logics designers, and circuit designers. Design
architects give specifications to the logic designers, who follow one of the
design methods or both. They identify the leaf cells. Circuit designers design
those leaf cells, and they try to optimize leaf cells in terms of power, area,
and speed. Hence all the design goes parallel and helps finishing the job
faster.
Operators
There are three types of operators: unary, binary, and ternary, which have one, two, and three operands respectively.
Unary: Single operand, which precede the operand.
Ex: x = ~y
~ is a unary operator
y is the operand
binary: Comes between two operands.
Ex: x = y || z
|| is a binary operator
y and z are the operands
ternary: Ternary operators have two separate operators that separate three operands.
Ex: p = x? Y: z
? : is a ternary operator
x, y, and z are the operands
There are three types of operators: unary, binary, and ternary, which have one, two, and three operands respectively.
Unary: Single operand, which precede the operand.
Ex: x = ~y
~ is a unary operator
y is the operand
binary: Comes between two operands.
Ex: x = y || z
|| is a binary operator
y and z are the operands
ternary: Ternary operators have two separate operators that separate three operands.
Ex: p = x? Y: z
? : is a ternary operator
x, y, and z are the operands
Comments
Verilog HDL also have two types of commenting, similar to that of C programming language. // is used for single line commenting and '/*' and '*/' are used for commenting multiple lines which start with /* and end with */.
EX: // single line comment
/* Multiple line
commenting */
/* This is a // LEGAL comment */
/* This is an /* ILLEGAL */ comment */
Whitespace
- - \b - backspace
- - \t - tab space
- - \n - new line
In verilog Whitespace is ignored except when it separates
tokens. Whitespace is not ignored in strings. Whitespaces are generally used in
writing test benches.
Strings
A string in verilog is same as that of C programming language. It is a sequence of characters enclosed in double quotes. Strings are treated as sequence of one byte ASCII values, hence they can be of one line only, they cannot be of multiple lines.
Ex: " This is a string "
" This is not treated as
string in verilog HDL "
Identifiers
Identifiers are user-defined words for variables, function names, module names, block names and instance names. Identifiers begin with a letter or underscore and can include any number of letters, digits and underscores. It is not legal to start identifiers with number or the dollar($) symbol in Verilog HDL. Identifiers in Verilog are case-sensitive.
Keywords
Keywords are special words reserved to define the language constructs. In verilog all keywords are in lowercase only. A list of all keywords in Verilog is given below:
Keywords are special words reserved to define the language constructs. In verilog all keywords are in lowercase only. A list of all keywords in Verilog is given below:
always
and assign attribute begin buf bufif0 bufif1 case casex casez cmos deassign default defparam disable edge else end endattribute endcase endfunction endmodule endprimitive endspecify endtable endtask |
event
for force forever fork function highz0 highz1 if ifnone initial inout input integer join medium module large macromodule nand negedge nmos nor not notif0 notif1 or |
output
parameter pmos posedge primitive pull0 pull1 pulldown pullup rcmos real realtime reg release repeat rnmos rpmos rtran rtranif0 rtranif1 scalared signed small specify specparam strength strong0 |
strong1
supply0 supply1 table task time tran tranif0 tranif1 tri tri0 tri1 triand trior trireg unsigned vectored wait wand weak0 weak1 while wire wor xnor xor |
Verilog keyword also
includes compiler directives, system tasks, and functions. Most of the keywords
will be explained in the later sections.
Number Specification
Sized Number Specification
Representation: [size]'[base][number]
Number Specification
Sized Number Specification
Representation: [size]'[base][number]
Ø [Size]
is written only in decimal and specifies the number of bits.
Ø [base]
could be 'd' or 'D' for decimal, 'h' or 'H' for hexadecimal, 'b' or 'B' for
binary, and 'o' or 'O' for octal.
Ø [number]
The number is specified as consecutive digits. Uppercase letters are legal for
number specification (in case of hexadecimal numbers).
Ex: 4’b1111: 4-bit
binary number
16’h1A2F: 16-bit hexadecimal number
32’d1: 32-bit decimal number
8’o3: 8-bit octal number
16’h1A2F: 16-bit hexadecimal number
32’d1: 32-bit decimal number
8’o3: 8-bit octal number
Unsized
Number Specification
By default numbers that are specified without a [base] specification is decimal numbers. Numbers that are written without a [size] specification have a default number of bits that is simulator and/or machine specific (generally 32).
Ex: 123: This is a decimal number
‘hc3: This is a hexadecimal number
Number of bits depends on simulator/machine, generally 32.
X or z values
x - Unknown value.
z - High impedance value
An x or z sets four bits for a number in the hexadecimal base, three bits for a number in the octal base, and one bit for a number in the binary base.
Note: If the most significant bit of a number is 0, x, or z, the number is automatically extended to fill the most significant bits, respectively, with 0, x, or z. this makes it easy to assign x or z to whole vector. If the most significant digit is 1, then it is also zero extended.
Negative Numbers
Representation: - [size]'[base][number]
Ex: -8'd9 : 8-bit negative number stored as 2's complement of 8
-8'sd3 : Used for performing signed integer math
By default numbers that are specified without a [base] specification is decimal numbers. Numbers that are written without a [size] specification have a default number of bits that is simulator and/or machine specific (generally 32).
Ex: 123: This is a decimal number
‘hc3: This is a hexadecimal number
Number of bits depends on simulator/machine, generally 32.
X or z values
x - Unknown value.
z - High impedance value
An x or z sets four bits for a number in the hexadecimal base, three bits for a number in the octal base, and one bit for a number in the binary base.
Note: If the most significant bit of a number is 0, x, or z, the number is automatically extended to fill the most significant bits, respectively, with 0, x, or z. this makes it easy to assign x or z to whole vector. If the most significant digit is 1, then it is also zero extended.
Negative Numbers
Representation: - [size]'[base][number]
Ex: -8'd9 : 8-bit negative number stored as 2's complement of 8
-8'sd3 : Used for performing signed integer math
Underscore
(_) and question (?) mark
An underscore, "_" is allowed to use anywhere in a number except in the beginning. It is used only to improve readability of numbers and is ignored by Verilog. A question mark "?" is the alternative for z w.r.t. numbers
ex: 8'b1100_1101: Underscore improves readability
4'b1??1: same as 4'b1zz1
An underscore, "_" is allowed to use anywhere in a number except in the beginning. It is used only to improve readability of numbers and is ignored by Verilog. A question mark "?" is the alternative for z w.r.t. numbers
ex: 8'b1100_1101: Underscore improves readability
4'b1??1: same as 4'b1zz1
Value Set
The Verilog HDL value set consists of four basic values:
The Verilog HDL value set consists of four basic values:
- 0 - represents a logic zero, or a
false condition.
- 1 - represents a logic one, or a
true condition.
- x - Represents an unknown logic
value.
- z - Represents a high-impedance
state.
The values 0 and 1
are logical complements of one another. Almost all of the data types in the
Verilog HDL store all four basic values.
Nets
Nets are used to make connections between hardware elements. Nets simply reflect the value at one end (head) to the other end (tail). It means the value they carry is continuously driven by the output of a hardware element to which they are connected to. Nets are generally declared using the keyword wire. The default value of net (wire) is z. If a net has no driver, then its value is z.
Registers
Registers are data storage elements. They hold the value until they are replaced by some other value. Register doesn't need a driver; they can be changed at anytime in a simulation. Registers are generally declared with the keyword reg. Its default value is x. Register data types should not be confused with hardware registers, these are simply variables.
Integers
Integer is a register data type of 32 bits. The only difference of declaring it as integer is that, it becomes a signed value. When you declare it as a 32 bit register (array) it is an unsigned value. It is declared using the keyword integer.
Real Numbers
Real number can be declared using the keyword real. They can be assigned values as follows:
real r_1;
r_1 = 1.234; // Decimal notation.
r_1 = 3e4; // Scientific notation.
Parameters
Parameters are the constants that can be declared using the keyword parameter. Parameters are in general used for customization of a design. Parameters are declared as follows:
parameter p_1 = 123; // p_1 is a constant with value 123.
Keyword defparam can be used to change a parameter value at module instantiation. Keyword localparam is usedd to declare local parameters; this is used when their value should not be changed.
Nets
Nets are used to make connections between hardware elements. Nets simply reflect the value at one end (head) to the other end (tail). It means the value they carry is continuously driven by the output of a hardware element to which they are connected to. Nets are generally declared using the keyword wire. The default value of net (wire) is z. If a net has no driver, then its value is z.
Registers
Registers are data storage elements. They hold the value until they are replaced by some other value. Register doesn't need a driver; they can be changed at anytime in a simulation. Registers are generally declared with the keyword reg. Its default value is x. Register data types should not be confused with hardware registers, these are simply variables.
Integers
Integer is a register data type of 32 bits. The only difference of declaring it as integer is that, it becomes a signed value. When you declare it as a 32 bit register (array) it is an unsigned value. It is declared using the keyword integer.
Real Numbers
Real number can be declared using the keyword real. They can be assigned values as follows:
real r_1;
r_1 = 1.234; // Decimal notation.
r_1 = 3e4; // Scientific notation.
Parameters
Parameters are the constants that can be declared using the keyword parameter. Parameters are in general used for customization of a design. Parameters are declared as follows:
parameter p_1 = 123; // p_1 is a constant with value 123.
Keyword defparam can be used to change a parameter value at module instantiation. Keyword localparam is usedd to declare local parameters; this is used when their value should not be changed.
Vectors
Vectors can be a net or reg data types. They are declared as [high: low] or [low: high], but the left number is always the MSB of the vector.
wire [7:0] v_1; // v_1[7] is the MSB.
reg [0:15] v_2; // v_2[15] is the MSB.
In the above examples: If it is written as v_1[5:2], it is the part of the entire vector which contains 4 bits in order: v_1[5], v_1[4], v_1[3], v_1[2]. Similarly v_2 [0:7], means the first half part of the vecotr v_2.
Vector parts can also be specified in a different way:
vector_name[start_bit+:width] : part-select increments from start_bit. In above example: v_2 [0:7] is same as v_2 [0+:8]. vector_name [start_bit-: width]: part-select decrements from start_bit. In above example: v_1 [5:2] is same as v_1 [5-:4].
Vectors can be a net or reg data types. They are declared as [high: low] or [low: high], but the left number is always the MSB of the vector.
wire [7:0] v_1; // v_1[7] is the MSB.
reg [0:15] v_2; // v_2[15] is the MSB.
In the above examples: If it is written as v_1[5:2], it is the part of the entire vector which contains 4 bits in order: v_1[5], v_1[4], v_1[3], v_1[2]. Similarly v_2 [0:7], means the first half part of the vecotr v_2.
Vector parts can also be specified in a different way:
vector_name[start_bit+:width] : part-select increments from start_bit. In above example: v_2 [0:7] is same as v_2 [0+:8]. vector_name [start_bit-: width]: part-select decrements from start_bit. In above example: v_1 [5:2] is same as v_1 [5-:4].
Arrays
Arrays of reg, integer, real, time, and vectors are allowed. Arrays are declared as follows:
reg a_1[0:7];
real a_3[15:0];
wire [0:3] a_4[7:0]; // Array of vector
integer a_5[0:3][6:0]; // Double dimensional array
Strings
Strings are register data types. For storing a character, we need a 8-bit register data type. So if you want to create string variable of length n. The string should be declared as register data type of length n*8.
reg [8*8-1:0] string_1; // string_1 is a string of length 8.
Arrays of reg, integer, real, time, and vectors are allowed. Arrays are declared as follows:
reg a_1[0:7];
real a_3[15:0];
wire [0:3] a_4[7:0]; // Array of vector
integer a_5[0:3][6:0]; // Double dimensional array
Strings
Strings are register data types. For storing a character, we need a 8-bit register data type. So if you want to create string variable of length n. The string should be declared as register data type of length n*8.
reg [8*8-1:0] string_1; // string_1 is a string of length 8.
Time
Data Type
Time data type is declared using the keyword time. These are generally used to store simulation time. In general it is 64-bit long.
time t_1;
t_1 = $time; // assigns current simulation time to t_1.
There are some other data types, but are considered to be advanced data types, hence they are not discussed here.
Time data type is declared using the keyword time. These are generally used to store simulation time. In general it is 64-bit long.
time t_1;
t_1 = $time; // assigns current simulation time to t_1.
There are some other data types, but are considered to be advanced data types, hence they are not discussed here.
Modules
A module is the basic
building block in Verilog HDL. In general many elements are grouped to form a
module, to provide a common functionality, which can be used at many places in
the design. Port interface (using input and output ports) helps in providing
the necessary functionality to the higher-level blocks. Thus any design
modifications at lower level can be easily implemented without affecting the
entire design code. The structure of a module is show in the figure below.

Keyword module
is used to begin a module and it ends with the keyword endmodule.
The syntax is as follows:
module module name
---
// internals
---
endmodule
Example:
module module name
---
// internals
---
endmodule
Example:
D Flip-flop
implementation (Try to understand the module structure, ignore unknown constraints/statements).
module D_FlipFlop(q, d, clk, reset);
// Port declarations
output q;
reg q;
input d, clk, reset;
// Internal statements - Logic
always @(posedge reset or poseedge clk)
if (reset)
q < = 1'b0;
else
q < = d;
// endmodule statement
endmodule
module D_FlipFlop(q, d, clk, reset);
// Port declarations
output q;
reg q;
input d, clk, reset;
// Internal statements - Logic
always @(posedge reset or poseedge clk)
if (reset)
q < = 1'b0;
else
q < = d;
// endmodule statement
endmodule
Note:
Ø Multiple
modules can be defined in a single design file with any order.
Ø See
that the endmodule statement should not written as endmodule; (no; is used).
Ø All
components except module, module name, and endmodule are optional.
Ø The
5 internal components can come in any order.
Modules communicate
with external world using ports. They provide interface to the modules. A
module definition contains list of ports. All ports in the list of ports must
be declared in the module, ports can be one the following types:
Ø Input
port, declared using keyword input.
Ø Output
port, declared using keyword output.
Ø Bidirectional
port, declared using keyword inout.
All the ports declared are considered
to be as wire by default. If a port is intended to be a wire, it is sufficient
to declare it as output, input, or inout. If output port
holds its value it should be declared as reg type. Ports of type input
and inout cannot be declared as reg because reg variables
hold values and input ports should not hold values but simply reflect the
changes in the external signals they are connected to.
Port Connection Rules
Port Connection Rules
Ø Inputs:
Always of type net (wire). Externally, they can be connected to reg
or net type variable.
Ø Outputs:
Can be of reg or net type. Externally, they must be connected to
a net type variable.
Ø Bidirectional
ports (inout): Always of type net. Externally, they must be
connected to a net type variable.
Note:
Ø It
is possible to connect internal and external ports of different size. In
general you will receive a warning message for width mismatch.
Ø There
can be unconnected ports in module instances.
Ports can declared in
a module in C-language style:
module module_1( input a, input b, output c);
--
// Internals
--
endmodule
module module_1( input a, input b, output c);
--
// Internals
--
endmodule
If there is an
instance of above module, in some other module. Port connections can be made in
two types.
Connection by Ordered List:
module_1 instance_name_1 ( A, B, C);
Connecting ports by name:
module_1 instance_name_2 (.a(A), .c(C), .b(B));
In connecting port by name, order is ignored.
Connection by Ordered List:
module_1 instance_name_1 ( A, B, C);
Connecting ports by name:
module_1 instance_name_2 (.a(A), .c(C), .b(B));
In connecting port by name, order is ignored.
Logical
Operators
Symbol
|
Description
|
#Operators
|
!
|
Logical negation
|
One
|
||
|
Logical OR
|
Two
|
&&
|
Logical AND
|
Two
|
Relational Operators
Symbol
|
Description
|
#Operators
|
>
|
Greater than
|
Two
|
<
|
Less than
|
Two
|
>=
|
Greater than or
equal to
|
Two
|
<=
|
Less than or equal
to
|
Two
|
Symbol
|
Description
|
#Operators
|
==
|
Equality
|
Two
|
!=
|
Inequality
|
Two
|
===
|
Case equality
|
Two
|
!==
|
Case inequality
|
Two
|
Arithmetic Operators
Symbol
|
Description
|
#Operators
|
+
|
Add
|
Two
|
-
|
Substract
|
Two
|
*
|
Multiply
|
Two
|
/
|
Divide
|
Two
|
**
|
Power
|
Two
|
%
|
Modulus
|
Two
|
Symbol
|
Description
|
#Operators
|
~
|
Bitwise negation
|
One
|
&
|
Bitwise AND
|
Two
|
|
|
Bitwise OR
|
Two
|
^
|
Bitwise XOR
|
Two
|
^~ or ~^
|
Bitwise XNOR
|
Two
|
Reduction Operators
Symbol
|
Description
|
#Operators
|
&
|
Reduction AND
|
One
|
~&
|
Reduction NAND
|
One
|
|
|
Reduction OR
|
One
|
~|
|
Reduction NOR
|
One
|
^
|
Reduction XOR
|
One
|
^~ or ~^
|
Reduction XNOR
|
One
|
Symbol
|
Description
|
#Operators
|
>>
|
Right shift
|
Two
|
<<
|
Left shift
|
Two
|
>>>
|
Arithmetic right
shift
|
Two
|
<<<
|
Arithmetic left
shift
|
Two
|
Symbol
|
Description
|
#Operators
|
?:
|
Conditional
|
Two
|
Symbol
|
Description
|
#Operators
|
{ { } }
|
Replication
|
> One
|
Symbol
|
Description
|
#Operators
|
{ }
|
Concatenation
|
> One
|
Operator Precedence

In Verilog HDL a module can be defined using various levels of abstraction. There are four levels of abstraction in verilog. They are:
Ø Behavioral
or algorithmic level: This is the highest level of abstraction. A module can be
implemented in terms of the design algorithm. The designer no need to have any
knowledge of hardware implementation.
Ø Data
flow level: In this level the module is designed by specifying the data flow.
Designer must how data flows between various registers of the design.
Ø Gate
level: The module is implemented in terms of logic gates and interconnections
between these gates. Designer should know the gate-level diagram of the design.
Ø Switch
level: This is the lowest level of abstraction. The design is implemented using
switches/transistors. Designer requires the knowledge of switch-level
implementation details.
Gate-level modeling is virtually the
lowest-level of abstraction, because the switch-level abstraction is rarely
used. In general, gate-level modeling is used for implementing lowest level
modules in a design like, full-adder, multiplexers, etc. Verilog HDL has gate
primitives for all basic gates.
Gate Primitives
Gate primitives are predefined in Verilog, which are ready to use. They are instantiated like modules. There are two classes of gate primitives: Multiple input gate primitives and Single input gate primitives.
Multiple input gate primitives include and, nand, or, nor, xor, and xnor. These can have multiple inputs and a single output. They are instantiated as follows:
// Two input AND gate.
and and_1 (out, in0, in1);
// Three input NAND gate.
nand nand_1 (out, in0, in1, in2);
// Two input OR gate.
or or_1 (out, in0, in1);
// Four input NOR gate.
nor nor_1 (out, in0, in1, in2, in3);
// Five input XOR gate.
xor xor_1 (out, in0, in1, in2, in3, in4);
// Two input XNOR gate.
xnor and_1 (out, in0, in1);
Note that instance name is not mandatory for gate primitive instantiation. The truth tables of multiple input gate primitives are as follows:
Gate Primitives
Gate primitives are predefined in Verilog, which are ready to use. They are instantiated like modules. There are two classes of gate primitives: Multiple input gate primitives and Single input gate primitives.
Multiple input gate primitives include and, nand, or, nor, xor, and xnor. These can have multiple inputs and a single output. They are instantiated as follows:
// Two input AND gate.
and and_1 (out, in0, in1);
// Three input NAND gate.
nand nand_1 (out, in0, in1, in2);
// Two input OR gate.
or or_1 (out, in0, in1);
// Four input NOR gate.
nor nor_1 (out, in0, in1, in2, in3);
// Five input XOR gate.
xor xor_1 (out, in0, in1, in2, in3, in4);
// Two input XNOR gate.
xnor and_1 (out, in0, in1);
Note that instance name is not mandatory for gate primitive instantiation. The truth tables of multiple input gate primitives are as follows:
Single input gate primitives include
not, buf, notif1, bufif1, notif0, and bufif0. These have a single input and one
or more outputs. Gate primitives notif1, bufif1, notif0, and bufif0 have a
control signal. The gates propagate if only control signal is asserted, else
the output will be high impedance state (z). They are instantiated as follows:
// Inverting gate.
not not_1 (out, in);
// Two output buffer gate.
buf buf_1 (out0, out1, in);
// Single output Inverting gate with active-high control signal.
notif1 notif1_1 (out, in, ctrl);
// Double output buffer gate with active-high control signal.
bufif1 bufif1_1 (out0, out1, in, ctrl);
// Single output Inverting gate with active-low control signal.
notif0 notif0_1 (out, in, ctrl);
// Single output buffer gate with active-low control signal.
bufif0 bufif1_0 (out, in, ctrl);
The truth tables are as follows:
// Inverting gate.
not not_1 (out, in);
// Two output buffer gate.
buf buf_1 (out0, out1, in);
// Single output Inverting gate with active-high control signal.
notif1 notif1_1 (out, in, ctrl);
// Double output buffer gate with active-high control signal.
bufif1 bufif1_1 (out0, out1, in, ctrl);
// Single output Inverting gate with active-low control signal.
notif0 notif0_1 (out, in, ctrl);
// Single output buffer gate with active-low control signal.
bufif0 bufif1_0 (out, in, ctrl);
The truth tables are as follows:
Array of Instances:
wire [3:0] out, in0, in1;
and and_array[3:0] (out, in0, in1);
wire [3:0] out, in0, in1;
and and_array[3:0] (out, in0, in1);
Gate Delays:
In Verilog, a designer can specify the gate delays in a gate primitive instance. This helps the designer to get a real time behavior of the logic circuit.
Rise delay: It is equal to the time taken by a gate output transition to 1, from another value 0, x, or z.
Fall delay: It is equal to the time taken by a gate output transition to 0, from another value 1, x, or z.
Turn-off delay: It is equal to the time taken by a gate output transition to high impedance state, from another value 1, x, or z.
In Verilog, a designer can specify the gate delays in a gate primitive instance. This helps the designer to get a real time behavior of the logic circuit.
Rise delay: It is equal to the time taken by a gate output transition to 1, from another value 0, x, or z.
Fall delay: It is equal to the time taken by a gate output transition to 0, from another value 1, x, or z.
Turn-off delay: It is equal to the time taken by a gate output transition to high impedance state, from another value 1, x, or z.
- If the gate output changes to x,
the minimum of the three delays is considered.
- If only one delay is specified, it
is used for all delays.
- If two values are specified, they
are considered as rise, and fall delays.
- If three values are specified,
they are considered as rise, fall, and turn-off delays.
- The default value of all delays is
zero.
and #(5) and_1 (out, in0, in1);
// All delay values are 5 time units.
nand #(3,4,5) nand_1 (out, in0, in1);
// rise delay = 3, fall delay = 4, and turn-off delay = 5.
or #(3,4) or_1 (out, in0, in1);
// rise delay = 3, fall delay = 4, and turn-off delay = min(3,4) = 3.
There is another way of specifying delay times in verilog, Min: Typ: Max values for each delay. This helps designer to have a much better real time experience of design simulation, as in real time logic circuits the delays are not constant. The user can choose one of the delay values using +maxdelays, +typdelays, and +mindelays at run time. The typical value is the default value.
nand #(3,4,5) nand_1 (out, in0, in1);
// rise delay = 3, fall delay = 4, and turn-off delay = 5.
or #(3,4) or_1 (out, in0, in1);
// rise delay = 3, fall delay = 4, and turn-off delay = min(3,4) = 3.
There is another way of specifying delay times in verilog, Min: Typ: Max values for each delay. This helps designer to have a much better real time experience of design simulation, as in real time logic circuits the delays are not constant. The user can choose one of the delay values using +maxdelays, +typdelays, and +mindelays at run time. The typical value is the default value.
and #(4:5:6) and_1 (out, in0, in1);
// For all delay values: Min=4, Typ=5, Max=6.
// For all delay values: Min=4, Typ=5, Max=6.
nand #(3:4:5,4:5:6,5:6:7) nand_1 (out,
in0, in1);
// rise delay: Min=3, Typ=4, Max=5, fall delay: Min=4, Typ=5,
// rise delay: Min=3, Typ=4, Max=5, fall delay: Min=4, Typ=5,
1. Gate level modeling of a 4x1 multiplexer.
The gate-level circuit diagram of 4x1 mux is shown below. It is used to write a module for 4x1 mux.


module 4x1_mux (out,
in0, in1, in2, in3, s0, s1);
// port declarations
output out; // Output port.
Input in0, in1, in2. in3; // Input ports.
Input s0, s1; // Input ports: select lines.
// intermediate wires
wire inv0, inv1; // Inverter outputs.
wire a0, a1, a2, a3; // AND gates outputs.
// port declarations
output out; // Output port.
Input in0, in1, in2. in3; // Input ports.
Input s0, s1; // Input ports: select lines.
// intermediate wires
wire inv0, inv1; // Inverter outputs.
wire a0, a1, a2, a3; // AND gates outputs.
// Inverters.
not not_0 (inv0, s0);
not not_1 (inv1, s1);
// 3-input AND gates.
and and_0 (a0, in0, inv0, inv1);
and and_1 (a1, in1, inv0, s1);
and and_2 (a2, in2, s0, inv1);
and and_3 (a3, in3, s0, s1);
// 4-input OR gate.
or or_0 (out, a0, a1, a2, a3);
endmodule
not not_0 (inv0, s0);
not not_1 (inv1, s1);
// 3-input AND gates.
and and_0 (a0, in0, inv0, inv1);
and and_1 (a1, in1, inv0, s1);
and and_2 (a2, in2, s0, inv1);
and and_3 (a3, in3, s0, s1);
// 4-input OR gate.
or or_0 (out, a0, a1, a2, a3);
endmodule
module half_adder (sum, carry, in0, in1);
output sum, carry;
input in0, in1;
// 2-input XOR gate.
xor xor_1 (sum, in0, in1);
// 2-input AND gate.
and and_1 (carry, in0, in1);
endmodule
Full adder:
module full_adder
(sum, c_out, ino, in1, c_in);
output sum, c_out;
input in0, in1, c_in;
wire s0, c0, c1;
// Half adder : port connecting by order.
half_adder ha_0 (s0, c0, in0, in1);
// Half adder : port connecting by name.
half_adder ha_1 (.sum (sum),
.in0 (s0),
.in1 (c_in),
.carry (c1));
// 2-input XOR gate, to get c_out.
xor xor_1 (c_out, c0, c1);
endmodule
output sum, c_out;
input in0, in1, c_in;
wire s0, c0, c1;
// Half adder : port connecting by order.
half_adder ha_0 (s0, c0, in0, in1);
// Half adder : port connecting by name.
half_adder ha_1 (.sum (sum),
.in0 (s0),
.in1 (c_in),
.carry (c1));
// 2-input XOR gate, to get c_out.
xor xor_1 (c_out, c0, c1);
endmodule
Dataflow
Modeling
Dataflow
modeling is a higher level of abstraction. The designer no need have any
knowledge of logic circuit. He should be aware of data flow of the design. The
gate level modeling becomes very complex for a VLSI circuit. Hence dataflow
modeling became a very important way of implementing the design.
In dataflow modeling most of the design is implemented using continuous assignments, which are used to drive a value onto a net. The continuous assignments are made using the keyword assign.
The assign statement
The assign statement is used to make continuous assignment in the dataflow modeling. The assign statement usage is given below:
assign out = in0 + in1; // in0 + in1 is evaluated and then assigned to out.
In dataflow modeling most of the design is implemented using continuous assignments, which are used to drive a value onto a net. The continuous assignments are made using the keyword assign.
The assign statement
The assign statement is used to make continuous assignment in the dataflow modeling. The assign statement usage is given below:
assign out = in0 + in1; // in0 + in1 is evaluated and then assigned to out.
Note:
Ø The
LHS of assign statement must always be a scalar or vector net or a
concatenation. It cannot be a register.
Ø Continuous
statements are always active statements.
Ø Registers
or nets or function calls can come in the RHS of the assignment.
Ø The
RHS expression is evaluated whenever one of its operands changes. Then the
result is assigned to the LHS.
Ø Delays
can be specified.
assign out[3:0] = in0[3:0] & in1[3:0];
assign {o3, o2, o1, o0} = in0[3:0] | {in1[2:0],in2}; // Use of concatenation.
Implicit Net Declaration:
wire in0, in1;
assign out = in0 ^ in1;
In the above example out is undeclared, but verilog makes an implicit net declaration for out.
Implicit Continuous
Assignment:
wire out = in0 ^ in1;
The above line is the implicit continuous assignment. It is same as,
wire out;
assign out = in0 ^ in1;
Delays
There are three types of delays associated with dataflow modeling. They are: Normal/regular assignment delay, implicit continuous assignment delay and net declaration delay.
Normal/regular assignment delay:
assign #10 out = in0 | in1;
If there is any change in the operands in the RHS, then RHS expression will be evaluated after 10 units of time. Lets say that at time t, if there is change in one of the operands in the above example, then the expression is calculated at t+10 units of time. The value of RHS operands present at time t+10 is used to evaluate the expression.
Implicit continuous assignment delay:
wire #10 out = in0 ^ in1;
is same as
wire out;
assign 10 out = in0 ^ in1;
Net declaration delay:
wire #10 out;
assign out = in;
is same as
wire out;
assign #10 out = in;
wire out = in0 ^ in1;
The above line is the implicit continuous assignment. It is same as,
wire out;
assign out = in0 ^ in1;
Delays
There are three types of delays associated with dataflow modeling. They are: Normal/regular assignment delay, implicit continuous assignment delay and net declaration delay.
Normal/regular assignment delay:
assign #10 out = in0 | in1;
If there is any change in the operands in the RHS, then RHS expression will be evaluated after 10 units of time. Lets say that at time t, if there is change in one of the operands in the above example, then the expression is calculated at t+10 units of time. The value of RHS operands present at time t+10 is used to evaluate the expression.
Implicit continuous assignment delay:
wire #10 out = in0 ^ in1;
is same as
wire out;
assign 10 out = in0 ^ in1;
Net declaration delay:
wire #10 out;
assign out = in;
is same as
wire out;
assign #10 out = in;
1. Implementation of a 2x4 decoder.
module decoder_2x4 (out, in0, in1);
output out[0:3];
input in0, in1;
// Data flow modeling uses logic operators.
assign out[0:3] = { ~in0 & ~in1, in0 & ~in1,
~in0 & in1, in0 & in1 };
endmodule
2. Implementation of a 4x1 multiplexer.
module mux_4x1 (out, in0, in1, in2, in3, s0, s1);
output out;
input in0, in1, in2, in3;
input s0, s1;
assign out = (~s0 & ~s1 & in0)|(s0 & ~s1 & in1)|
(~s0 & s1 & in2)|(s0 & s1 & in0);
endmodule
3. Implementation of a 8x1 multiplexer
using 4x1 multiplexers.
module mux_8x1 (out,
in, sel);
output out;
input [7:0] in;
input [2:0] sel;
wire m1, m2;
// Instances of 4x1 multiplexers.
mux_4x1 mux_1 (m1, in[0], in[1], in[2],
in[3], sel[0], sel[1]);
mux_4x1 mux_2 (m2, in[4], in[5], in[6],
in[7], sel[0], sel[1]);
assign out = (~sel[2] & m1)|(sel[2] & m2);
endmodule
output out;
input [7:0] in;
input [2:0] sel;
wire m1, m2;
// Instances of 4x1 multiplexers.
mux_4x1 mux_1 (m1, in[0], in[1], in[2],
in[3], sel[0], sel[1]);
mux_4x1 mux_2 (m2, in[4], in[5], in[6],
in[7], sel[0], sel[1]);
assign out = (~sel[2] & m1)|(sel[2] & m2);
endmodule
4. Implementation of a Full adder.
module full_adder (sum, c_out, in0, in1, c_in);
output sum, c_out;
input in0, in1, c_in;
assign { c_out, sum } = in0 + in1 + c_in;
endmodule
Behavioral
Modeling
Behavioral modeling
is the highest level of abstraction in the Verilog HDL. The other modeling
techniques are relatively detailed. They require some knowledge of how hardware
or hardware signals work. The abstraction in this modeling is as simple as
writing the logic in C language. This is a very powerful abstraction technique.
All that designer needs are the algorithm of the design, which is the basic
information for any design.
The initial Construct
The statements which come under the initial construct constitute the initial block. The initial block is executed only once in the simulation, at time 0. If there is more than one initial block. Then all the initial blocks are executed concurrently. The initial construct is used as follows:
The initial Construct
The statements which come under the initial construct constitute the initial block. The initial block is executed only once in the simulation, at time 0. If there is more than one initial block. Then all the initial blocks are executed concurrently. The initial construct is used as follows:
initial
begin
reset = 1'b0;
clk = 1'b1;
end
or
initial
clk = 1'b1;
begin
reset = 1'b0;
clk = 1'b1;
end
or
initial
clk = 1'b1;
In the first initial block there is more than one statement
hence they are written between begin and end. If there is only one statement
then there is no need to put begins and end.
The always Construct
The statements which come under the always construct constitute the always block. The always block starts at time 0, and keeps on executing all the simulation time. It works like a infinite loop. It is generally used to model a functionality that is continuously repeated.
The always Construct
The statements which come under the always construct constitute the always block. The always block starts at time 0, and keeps on executing all the simulation time. It works like a infinite loop. It is generally used to model a functionality that is continuously repeated.
always
#5 clk = ~clk;
initial
clk = 1'b0;
#5 clk = ~clk;
initial
clk = 1'b0;
The above code generates a clock signal clk, with a time period of 10 units. The initial blocks initiates the clk value to 0 at time 0. Then after every 5 units of time it toggled, hence we get a time period of 10 units. This is the way in general used to generate a clock signal for use in test benches.
always
@ (posedge clk, negedge reset)
begin
a = b + c;
d = 1'b1;
end
begin
a = b + c;
d = 1'b1;
end
In the above example, the always block will be executed whenever there is a positive edge in the clk signal, or there is negative edge in the reset signal. This type of always is generally used in implement a FSM, which has a reset signal.
always
@ (b,c,d)
begin
a = ( b + c )*d;
e = b | c;
end
begin
a = ( b + c )*d;
e = b | c;
end
In the above example, whenever there is a change in b, c, or d the always block will be executed. Here the list b, c, and d is called the sensitivity list.
In the Verilog 2000, we can replace always @ (b,c,d) with always @(*), it is equivalent to include all input signals, used in the always block. This is very useful when an always block is used for implementing the combination logic.
Procedural Assignments
Procedural assignments are used for updating reg, integer, time, real, realtime, and memory data types. The variables will retain their values until updated by another procedural assignment. There is a significant difference between procedural assignments and continuous assignments.
Continuous assignments drive nets and are evaluated and updated whenever an input operand changes value. Where as procedural assignments update the value of variables under the control of the procedural flow constructs that surround them.
The LHS of a procedural assignment could be:
Ø reg,
integer, real, realtime, or time data type.
Ø Bit-select
of a reg, integer, or time data type, rest of the bits are
untouched.
Ø Part-select
of a reg, integer, or time data type, rest of the bits are
untouched.
Ø Memory
word.
Ø Concatenation
of any of the previous four forms can be specified.
When the RHS evaluates to fewer bits
than the LHS, then if the right-hand side is signed, it will be sign-extended
to the size of the left-hand side.
There are two types of procedural assignments: blocking and non-blocking assignments.
There are two types of procedural assignments: blocking and non-blocking assignments.
Blocking assignments:
A blocking assignment statement is
executed in the order they are specified in a sequential block. The execution
of next statement begins only after the completion of the present blocking
assignments. A blocking assignment will not block the execution of the next
statement in a parallel block. The blocking assignments are made using the
operator =.
initial
begin
a = 1;
b = #5 2;
c = #2 3;
end
In the above example, a is assigned value 1 at time 0, and b is assigned value 2 at time 5, and c is assigned value 3 at time 7.
Non-blocking
assignments:
The nonblocking assignment allows
assignment scheduling without blocking the procedural flow. The nonblocking
assignment statement can be used whenever several variable assignments within
the same time step can be made without regard to order or dependence upon each
other. Non-blocking assignments are made using the operator <=.
Note: <= is same for less than or equal to operator, so whenever it appears in a expression it is considered to be comparison operator and not as non-blocking assignment.
Note: <= is same for less than or equal to operator, so whenever it appears in a expression it is considered to be comparison operator and not as non-blocking assignment.
initial
begin
a <= 1;
b <= #5 2;
c <= #2 3;
end
In the above example, a is assigned value 1 at time 0, and b is assigned value 2 at time 5, and c is assigned value 3 at time 2 (because all the statements execution starts at time 0, as they are non-blocking assignments.
Block
Statements
Block statements are used to group two or more statements together, so that they act as one statement. There are two types of blocks:
Block statements are used to group two or more statements together, so that they act as one statement. There are two types of blocks:
- Sequential block.
- Parallel block.
Sequential block:
The sequential block is defined using the keywords begin and end.
The procedural statements in sequential block will be executed sequentially in
the given order. In sequential block delay values for each statement shall be
treated relative to the simulation time of the execution of the previous
statement. The control will pass out of the block after the execution of last
statement.
Parallel block: The parallel block is defined using the keywords fork and join. The procedural statements in parallel block will be executed concurrently. In parallel block delay values for each statement are considered to be relative to the simulation time of entering the block. The delay control can be used to provide time-ordering for procedural assignments. The control shall pass out of the block after the execution of the last time-ordered statement.
Note that blocks can be nested. The sequential and parallel blocks can be mixed.
Block names: All the blocks can be named, by adding: block_name after the keyword begin or fork. The advantages of naming a block are:
Parallel block: The parallel block is defined using the keywords fork and join. The procedural statements in parallel block will be executed concurrently. In parallel block delay values for each statement are considered to be relative to the simulation time of entering the block. The delay control can be used to provide time-ordering for procedural assignments. The control shall pass out of the block after the execution of the last time-ordered statement.
Note that blocks can be nested. The sequential and parallel blocks can be mixed.
Block names: All the blocks can be named, by adding: block_name after the keyword begin or fork. The advantages of naming a block are:
- It allows declaring local
variables, which can be accessed by using hierarchical name referencing.
- They can be disabled using the disable
statement (disable block_name ;).
Conditional (if-else) Statement
The condition (if-else) statement is used to make a decision whether a statement is executed or not. The keywords if and else are used to make conditional statement. The conditional statement can appear in the following forms.
if ( condition_1 )
statement_1;
if ( condition_2 )
statement_2;
else
statement_3;
if ( condition_3 )
statement_4;
else if ( condition_4 )
statement_5;
else
statement_6;
if ( condition_5 )
begin
statement_7;
statement_8;
end
else
begin
statement_9;
statement_10;
end
statement_1;
if ( condition_2 )
statement_2;
else
statement_3;
if ( condition_3 )
statement_4;
else if ( condition_4 )
statement_5;
else
statement_6;
if ( condition_5 )
begin
statement_7;
statement_8;
end
else
begin
statement_9;
statement_10;
end
Conditional (if-else) statement usage is similar to that if-else statement of C programming language, except that parenthesis are replaced by begin and end.
Case Statement
The case statement is a multi-way decision statement that tests whether an expression matches one of the expressions and branches accordingly. Keywords case and endcase are used to make a case statement. The case statement syntax is as follows.
case
(expression)
case_item_1: statement_1;
case_item_2: statement_2;
case_item_3: statement_3;
...
...
default: default_statement;
endcase
case_item_1: statement_1;
case_item_2: statement_2;
case_item_3: statement_3;
...
...
default: default_statement;
endcase
If there are multiple statements under a single match, then they are grouped using begin, and end keywords. The default item is optional.
Case statement with don't cares: casez and casex
casez treats high-impedance values (z) as don't cares. casex treats both high-impedance (z) and unknown (x) values as don't care. Don't-care values (z values for casez, z and x values for casex) in any bit of either the case expression or the case items shall be treated as don't-care conditions during the comparison, and that bit position shall not be considered. The don't cares are represented using the? mark.
Loop Statements
There are four types of looping statements in Verilog:
Forever
Loop
Forever loop is defined using the keyword forever, which Continuously executes a statement. It terminates when the system task $finish is called. A forever loop can also be ended by using the disable statement.
Forever loop is defined using the keyword forever, which Continuously executes a statement. It terminates when the system task $finish is called. A forever loop can also be ended by using the disable statement.
initial
begin
clk = 1'b0;
forever #5 clk = ~clk;
end
begin
clk = 1'b0;
forever #5 clk = ~clk;
end
In the above example, a clock signal with time period 10 units of time is obtained.
Repeat Loop
Repeat loop is defined using the keyword repeat. The repeat loop block continuously executes the block for a given number of times. The number of times the loop executes can be mention using a constant or an expression. The expression is calculated only once, before the start of loop and not during the execution of the loop. If the expression value turns out to be z or x, then it is treated as zero, and hence loop block is not executed at all.
initial
begin
a = 10;
b = 5;
b <= #10 10;
i = 0;
repeat (a*b)
begin
$display ("repeat in progress");
#1 i = i + 1;
end
end
begin
a = 10;
b = 5;
b <= #10 10;
i = 0;
repeat (a*b)
begin
$display ("repeat in progress");
#1 i = i + 1;
end
end
In the above example the loop block is executed only 50 times, and not 100 times. It calculates (a*b) at the beginning, and uses that value only.
While Loop
The while loop is defined using the keyword while. The while loop contains an expression. The loop continues until the expression is true. It terminates when the expression is false. If the calculated value of expression is z or x, it is treated as a false. The value of expression is calculated each time before starting the loop. All the statements (if more than one) are mentioned in a block which begins and ends with keyword begin and end keywords.
initial
begin
a = 20;
i = 0;
while (i < a)
begin
$display ("%d", i);
i = i + 1;
a = a - 1;
end
end
begin
a = 20;
i = 0;
while (i < a)
begin
$display ("%d", i);
i = i + 1;
a = a - 1;
end
end
In the above example the loop executes for 10 times. (observe that a is decrementing by one and i is incrementing by one, so loop terminated when both i and a become 10).
For Loop
The For loop is defined using the keyword for. The execution of for loop block is controlled by a three step process, as follows:
1. Executes
an assignment, normally used to initialize a variable that controls the number
of times the for block is executed.
2. Evaluates
an expression, if the result is false or z or x, the for-loop shall terminate,
and if it is true, the for-loop shall execute its block.
3. Executes
an assignment normally used to modify the value of the loop-control variable
and then repeats with second step.
Note that the first step is executed only once.
initial
begin
a = 20;
for (i = 0; i < a; i = i + 1, a = a - 1)
$display ("%d", i);
end
begin
a = 20;
for (i = 0; i < a; i = i + 1, a = a - 1)
$display ("%d", i);
end
The above example produces the same result as the example used to illustrate the functionality of the while loop.
1. Implementation of a 4x1 multiplexer.
module 4x1_mux (out, in0, in1, in2, in3, s0, s1);
output out;
// out is declared as reg, as default is wire
reg out;
// out is declared as reg, because we will
// do a procedural assignment to it.
input in0, in1, in2, in3, s0, s1;
// always @(*) is equivalent to
// always @( in0, in1, in2, in3, s0, s1 )
always @ (*)
begin
case ({s1,s0})
2'b00: out = in0;
2'b01: out = in1;
2'b10: out = in2;
2'b11: out = in3;
default: out = 1'bx;
endcase
end
endmodule
begin
case ({s1,s0})
2'b00: out = in0;
2'b01: out = in1;
2'b10: out = in2;
2'b11: out = in3;
default: out = 1'bx;
endcase
end
endmodule
2. Implementation of a full adder.
module full_adder (sum, c_out, in0, in1, c_in);
output sum, c_out;
reg sum, c_out
input in0, in1, c_in;
always @(*)
{c_out, sum} = in0 + in1 + c_in;
endmodule
3. Implementation of a 8-bit binary counter.
module ( count, reset, clk );
output [7:0] count;
reg [7:0] count;
input reset, clk;
// consider reset as active low signal
always @( posedge clk, negedge reset)
begin
if(reset == 1'b0)
count <= 8'h00;
else
count <= count + 8'h01;
end
endmodule
Implementation of a 8-bit counter is a very good example, which explains the advantage of behavioral modeling. Just imagine how difficult it will be implementing a 8-bit counter using gate-level modeling.
In the above example the incrementation occurs on every positive edge of the clock. When count becomes 8'hFF, the next increment will make it 8’h00; hence there is no need of any modulus operator. Reset signal is active low.
Tasks
and Functions
Tasks
and functions are introduced in the verilog, to provide the ability to execute
common procedures from different places in a description. This helps the
designer to break up large behavioral designs into smaller pieces. The designer
has to abstract the similar pieces in the description and replace them either
functions or tasks. This also improves the readability of the code, and hence
easier to debug. Tasks and functions must be defined in a module and are local
to the module. Tasks are used when:
Ø There
are delay, timing, or event control constructs in the code.
Ø There
is no input.
Ø There
is zero output or more than one output argument.
o
Functions are used when:
Ø The
code executes in zero simulation time.
Ø The
code provides only one output (return value) and has at least one input.
Ø There
are no delay, timing, or event control constructs.
Functions
|
Tasks
|
Can enable another
function but not another task.
|
Can enable other
tasks and functions.
|
Executes in 0
simulation time.
|
May execute in
non-zero simulation time.
|
Must not contain
any delay, event, or timing control statements.
|
May contain delay,
event, or timing control statements.
|
Must have at least
one input argument. They can have more than one input.
|
May have zero or
more arguments of type input, output, or inout.
|
Functions always
return a single value. They cannot have output or inout arguments.
|
Tasks do not return
with a value, but can pass multiple values through output and inout
arguments.
|
Tasks
There are two ways of defining a task. The first way shall begin with the keyword task, followed by the optional keyword automatic, followed by a name for the task, and ending with the keyword endtask. The keyword automatic declares an automatic task that is reentrant with all the task declarations allocated dynamically for each concurrent task entry. Task item declarations can specify the following:
There are two ways of defining a task. The first way shall begin with the keyword task, followed by the optional keyword automatic, followed by a name for the task, and ending with the keyword endtask. The keyword automatic declares an automatic task that is reentrant with all the task declarations allocated dynamically for each concurrent task entry. Task item declarations can specify the following:
- Input arguments.
- Output arguments.
- Inout arguments.
- All data types that can be
declared in a procedural block
The second way shall begin with the
keyword task, followed by a name for the task and a parenthesis which encloses
task port list. The port list shall consist of zero or more comma separated
ports. The task body shall follow and then the keyword endtask.
In both ways, the port declarations are same. Tasks without the optional keyword automatic are static tasks, with all declared items being statically allocated. These items shall be shared across all uses of the task executing concurrently. Task with the optional keyword automatic are automatic tasks. All items declared inside automatic tasks are allocated dynamically for each invocation. Automatic task items can not be accessed by hierarchical references. Automatic tasks
can be invoked through use of their hierarchical name.
Functions
Functions are mainly used to return a value, which shall be used in an expression. The functions are declared using the keyword function, and definition ends with the keyword endfunction.
If a function is called concurrently from two locations, the results are non-deterministic because both calls operate on the same variable space. The keyword automatic declares a recursive function with all the function declarations allocated dynamically for each recursive call. Automatic function items can not be accessed by hierarchical references. Automatic functions can be invoked through the use of their hierarchical name.
When a function is declared, a register with function name is declared implicitly inside Verilog HDL. The output of a function is passed back by setting the value of that register appropriately.
In both ways, the port declarations are same. Tasks without the optional keyword automatic are static tasks, with all declared items being statically allocated. These items shall be shared across all uses of the task executing concurrently. Task with the optional keyword automatic are automatic tasks. All items declared inside automatic tasks are allocated dynamically for each invocation. Automatic task items can not be accessed by hierarchical references. Automatic tasks
can be invoked through use of their hierarchical name.
Functions
Functions are mainly used to return a value, which shall be used in an expression. The functions are declared using the keyword function, and definition ends with the keyword endfunction.
If a function is called concurrently from two locations, the results are non-deterministic because both calls operate on the same variable space. The keyword automatic declares a recursive function with all the function declarations allocated dynamically for each recursive call. Automatic function items can not be accessed by hierarchical references. Automatic functions can be invoked through the use of their hierarchical name.
When a function is declared, a register with function name is declared implicitly inside Verilog HDL. The output of a function is passed back by setting the value of that register appropriately.
1. Simple task example, where task is used to get the address tag and offset of a given address.
module example1_task;
input addr;
wire [31:0] addr;
wire [23:0] addr_tag;
wire [7:0] offset;
task get_tag_and_offset ( addr, tag, offset);
input addr;
output tag, offset;
begin
tag = addr[31:8];
offset = addr[7:0];
end
endtask
always @(addr)
begin
get_tag_and_offset (addr, addr_tag, addr_offset);
end
// other internals of module
endmodule
2. Task example,
which uses the global variables of a module. Here task is used to do
temperature conversion.
module example2_global;
real t1;
real t2;
// task uses the global variables of the module
task t_convert;
begin
t2 = (9/5)*(t1+32);
end
endtask
always @(t1)
begin
t_convert();
end
endmodule
Comments
Post a Comment