0% found this document useful (0 votes)
3 views

Lesson 1. Verilog HDL

This document provides an introduction to Verilog, a Hardware Description Language (HDL) used for designing digital systems. It covers the history, abstraction levels, syntax, and operators of Verilog, as well as its application in digital design and verification processes. The document emphasizes the importance of Verilog in simplifying hardware design and enhancing the design process through automation tools.

Uploaded by

Maxine Tayros
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Lesson 1. Verilog HDL

This document provides an introduction to Verilog, a Hardware Description Language (HDL) used for designing digital systems. It covers the history, abstraction levels, syntax, and operators of Verilog, as well as its application in digital design and verification processes. The document emphasizes the importance of Verilog in simplifying hardware design and enhancing the design process through automation tools.

Uploaded by

Maxine Tayros
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 93

Lesson 1

Introduction to Verilog Hardware Description Language (HDL)


Learning Objectives:
At the end of the lesson, the students are able to:
1.
Introduction
Verilog is a Hardware Description Language (HDL). It is a language used for
describing a digital system such as a network switch, a microprocessor, a
memory, or a flip-flop. We can describe any digital hardware by using HDL at
any level. Designs described in HDL are independent of technology, very
easy for designing and debugging, and are normally more useful than
schematics, particularly for large circuits.

Verilog was developed to simplify the process and make the HDL more
robust and flexible. Today, Verilog is the most popular HDL used and
practiced throughout the semiconductor industry.

HDL was developed to enhance the design process by allowing engineers to


describe the desired hardware's functionality and let automation tools
convert that behavior into actual hardware elements like combinational
gates and sequential logic.

Verilog is like any other hardware description language. It permits the


designers to design the designs in either Bottom-up or Top-down
methodology.

o Bottom-Up Design: The traditional method of electronic design is


bottom-up. Each design is performed at the gate-level using the
standards gates. This design gives a way to design new structural,
hierarchical design methods.
o Top-Down Design: It allows early testing, easy change of different
technologies, and structured system design and offers many other
benefits.

Verilog Abstraction Levels

Verilog supports a design at many levels of abstraction, such as:

o Behavioral level
o Register-transfer level
o Gate level

Behavioral level

The behavioral level describes a system by concurrent algorithms


behavioral. Every algorithm is sequential, which means it consists of a set of
executed instructions one by one. Functions, tasks, and blocks are the main
elements. There is no regard for the structural realization of the design.

Register-Transfer Level

Designs using the Register-Transfer Level specify a circuit's characteristics


using operations and the transfer of data between the registers.

The modern definition of an RTL code is "Any code that is synthesizable is


called RTL code".

Gate Level

The characteristics of a system are described by logical links and their timing
properties within the logical level. All signals are discrete signals. They can
only have definite logical values (`0', `1', `X', `Z`).

The usable operations are predefined logic primitives (basic gates). Gate
level modeling may not be the right idea for logic design. Gate level code is
generated using tools such as synthesis tools, and his netlist is used for gate-
level simulation and backend.

History of Verilog

o Verilog HDL's history goes back to the 1980s when a company called
Gateway Design Automation developed a logic simulator, Verilog-XL,
and a hardware description language.
o Cadence Design Systems acquired Gateway in 1989 and with it the
rights to the language and the simulator. In 1990, Cadence put the
language into the public domain, with the intention that it should
become a standard, non-proprietary language.
o The Verilog HDL is now maintained by a nonprofit making organization,
Accellera, formed from the merger of Open Verilog International (OVI)
and VHDL International. OVI had the task of taking the language
through the IEEE standardization procedure.
o In December 1995, Verilog HDL became IEEE Std. 1364-1995. A
significantly revised version was published in 2001: IEEE Std. 1364-
2001. There was a further revision in 2005, but this only added a few
minor changes.
o Accellera has also developed a new standard, SystemVerilog, which
extends Verilog.
o SystemVerilog became an IEEE standard (1800-2005) in 2005.

How is Verilog useful?

Verilog creates a level of abstraction that helps hide away the details of its
implementation and technology.
For example, a D flip-flop design would require the knowledge of how the
transistors need to be arranged to achieve a positive-edge triggered FF and
what the rise, fall, and CLK-Q times required to latch the value onto a flop
among much other technology-oriented details.

Power dissipation, timing, and the ability to drive nets and other flops would
also require a more thorough understanding of a transistor's physical
characteristics.

Lexical Tokens

Lexical conventions in Verilog are similar to the C programming language.


Verilog language source text files are a stream of lexical tokens.

A lexical token may consist of one or more characters, and every single
character is in exactly one token.

The tokens can be keywords, comments, numbers, white space, or strings.


All lines should be terminated by a semi-colon (;).

o Verilog HDL is a case-sensitive language.


o And all keywords are in lowercase.

White Space

White space can contain the characters for tabs, blanks, newlines, and form
feeds. These characters are ignored except when they serve to separate
other tokens. However, blanks and tabs are significant in strings.

Comments

There are two types to represent the comments, such as:

1. Single line comments begin with the token // and end with a carriage
return.
For example, //this is the single-line syntax.
2. Multi-Line comments begin with the token /* and end with the token */
For example, /* this is multiline syntax*/

Numbers

We can specify constant numbers in binary, decimal, hexadecimal, or octal


format. Negative numbers are represented in 2's complement form. The
question mark (?) character is the Verilog alternative for the z character
when used in a number. The underscore character (_) is legal anywhere in a
number, but it is ignored as the first character.

1. Integer Number

Verilog

HDL allows integer numbers to be specified as:


o Sized or unsized numbers (Unsized size is 32 bits ).
o In a radix of decimal, hexadecimal, binary or octal.
o Radix and hex digits (a,b,c,d) are case insensitive.
o Spaces are allowed between the radix, size, and value.

Syntax

The syntax is given as:

1. <size>'<radix><value>

2. Real Numbers

o Verilog supports real constants and variables.


o Verilog converts real numbers to integers by rounding.
o Real Numbers can not contain 'A' and 'X'.
o Real numbers may be specified in either decimal or scientific notation.
o < value >.< value >
o < mantissa >E< exponent >
o Real numbers are rounded off to the nearest integer when assigning to
an integer.

3. Signed and Unsigned Numbers

erilog supports both the type of numbers, but with certain restrictions. In C
language

, we don't have int and unint types to say if a number is signed integer or unsigned integer.

Any number that does not have a negative sign prefix is positive. Or indirect
way would be "Unsigned".

Negative numbers can be specified by putting a minus sign before the size
for a constant number, thus become signed numbers. Verilog internally
represents negative numbers in 2's complement format. An optional signed
specifier can be added for signed arithmetic.

4. Negative Numbers

Negative numbers are specified by placing a minus (-) sign before the size of
a number. It is illegal to have a minus sign between base_format and
number.

Identifiers

The identifier is the name used to define the object, such as a function,
module, or register. Identifiers should begin with alphabetical characters or
underscore characters.

For example, A_Z and a_z.


Identifiers are a combination of alphabetic, numeric, underscore, and $
characters. They can be up to 1024 characters long.

o Identifiers must begin with an alphabetic character or the underscore


character (a-z A-Z_).
o Identifiers may contain alphabetic characters, numeric characters, the
underscore, and the dollar sign (a-z A-Z 0-9 _ $).
o Identifiers can be up to 1024 characters long.

1. Escaped Identifiers

Verilog HDL allows any character to be used in an identifier by escaping the


identifier.

Escaped identifiers are including any of the printable ASCII characters in an


identifier.

o The decimal values 33 through 126, or 21 through 7E in hexadecimal.


o Escaped identifiers begin with the backslash (\). The backslash escapes
the entire identifier.
o The escaped identifier is terminated by white space characters such as
commas, parentheses, and semicolons become part of the escaped
identifier unless preceded by white space.
o Terminate escaped identifiers with white space. Otherwise, characters
that should follow the identifier are considered part of it.

Operators

Operators are special characters used to put conditions or to operate the


variables. There are one, two, and sometimes three characters used to
perform operations on variables.

1. Arithmetic Operators

These operators perform arithmetic operations. The + and -are used as


either unary (x) or binary (z-y) operators.

The operators included in arithmetic operation are addition, subtraction,


multiplication, division, and modulus.

2. Relational Operators

These operators compare two operands and return the result in a single bit,
1 or 0. The Operators included in relational operation are:

o == (equal to)
o != (not equal to)
o (greater than)
o >= (greater than or equal to)
o < (less than)
o <= (less than or equal to)

3. Bit-wise Operators
Bit-wise operators do a bit-by-bit comparison between two operands. The
Operators included in Bit-wise operation are:

o & (Bit-wise AND)


o | (Bit-wiseOR)
o ~ (Bit-wise NOT)
o ^ (Bit-wise XOR)
o ~^ or ^~(Bit-wise XNOR)

4. Logical Operators

Logical operators are bit-wise operators and are used only for single-bit
operands. They return a single bit value, 0 or 1. They can work on integers or
groups of bits, expressions and treat all non-zero values as 1.

Logical operators are generally used in conditional statements since they


work with expressions. The operators included in Logical operation are:

o ! (logical NOT)
o && (logical AND)
o || (logical OR)

5. Reduction Operators

Reduction operators are the unary form of the bitwise operators and operate
on all the bits of an operand vector. These also return a single-bit value. The
operators included in Reduction operation are:

o & (reduction AND)


o | (reduction OR)
o ~& (reduction NAND)
o ~| (reduction NOR)
o ^ (reduction XOR)
o ~^ or ^~(reduction XNOR)

6. Shift Operators

Shift operators are shifting the first operand by the number of bits specified
by the second operand in the syntax.

Vacant positions are filled with zeros for both directions, left and right shifts
(There is no use sign extension). The Operators included in Shift operation
are:

o << (shift left)


o >> (shift right)

7. Concatenation Operator

The concatenation operator combines two or more operands to form a larger


vector. The operator included in Concatenation operation is:

o { }(concatenation)
8. Replication Operator

The replication operator is making multiple copies of an item. The operator


used in Replication operation is:

o {n{item}} (n fold replication of an item)

9. Conditional Operator

Conditional operator synthesizes to a multiplexer. It is the same kind as is


used in C/C++ and evaluates one of the two expressions based on the
condition. The operator used in Conditional operation is:

o (Condition) ?

Operands

Operands are expressions or values on which an operator operates or


works. All expressions have at least one operand.

1. Literals

Literals are constant-valued operands that are used in Verilog expressions.


The two commonly used Verilog literals are:

o String: A literal string operand is a one-dimensional array of


characters enclosed in double quotes (" ").
o Numeric: A constant number of the operand is specified in binary,
octal, decimal, or hexadecimal number.

2. Wires, Regs, and Parameters

Wires, regs, and parameters are the data types used as operands in Verilog
expressions. Bit-Selection "x[2]" and Part-Selection "x[4:2]"

Bit-selects and part-selects are used to select one bit and multiple bits,
respectively, from a wire, regs or parameter vector using square brackets
"[ ]".

3. Function Calls

In the Function calls, the return value of a function is used directly in an


expression without first assigning it to a register or wire.

It just places the function call as one of the types of operands. It is useful to
know the bit width of the return value of the function call.

A typical design flow follows the below structure and can be broken down
into multiple steps. Some of these phases happen in parallel and some in
sequentially.
Requirements

A customer of a semiconductor firm is typically some other company who


plans to use the chip in its systems or end products. So, the customer's
requirements also play an important role in deciding how the chip should be
designed.

The first step is to collect the requirements, estimate the end product's
market value, and evaluate the number of resources required to do the
project.

Specifications

The next step is to collect specifications that describe the functionality,


interface abstractly, and over all architecture of the chip to be designed. This
can be something along the lines such as:

1. Requires computational power to run imaging algorithms to support


virtual reality.
2. Requires two ARM A53 processors with coherent interconnect and
should run at 600 MHz.
3. Requires USB 3.0, Bluetooth, and PCIe 2nd gen interfaces.
4. It should support 1920x1080 pixel displays with an appropriate
controller.

Architecture

Now, the architect gives a system-level view of how the chip should operate.
They will decide what all other components are required, what clock
frequencies they should run, and how to target power and performance
requirements.

They also decide on how the data should flow inside the chip. An example
would be the data flow when a processor fetches imaging data from the
system ram and executes them. Meanwhile, the graphics engine will execute
post-processed data from the previous batch dumped into another part of
memory and so on.

Digital Design

Because of the complex nature of modern chips, it's impossible to build


something from scratch, and in many cases, many components will be
reused.

For example, company A requires a FlexCAN module to interact with other


modules in an automobile. They can either buy the FlexCAN design from
another company to save time and effort or spend resources to build one.

It's not practical to design such a system from basic building blocks such as
flip-flops and CMOS transistors.

Instead, a behavioral description is developed to analyze the design in terms


of functionality, performance, and other high-level issues using a Hardware
Description Language such as Verilog or VHDL.

This is usually done by a digital designer and is similar to a high-level


computer programmer equipped with digital electronics skills.

Verification

Once the RTL design is ready, it needs to be verified for functional


correctness.

For example, a DSP processor is expected to issue bus transactions with


fetching instructions from memory and know that this will happen as
expected.
The functional verification is required at this point, which is done
with EDA simulators' help that can model the design and apply a different
stimulus to it. This is the job of a pre-silicon verification engineer.

To save time and reach functional closure, both the design and verification
teams operate in parallel, where the designers release an RTL version. The
verification team develops a testbench environment and test cases to test
the functionality of that RTL version.

If any of these tests fail, it might indicate a problem with the design, and a
"bug" will be raised on that design element. This bug will have to be fixed in
the next version of the RTL release from the design team.

This process goes on until there is a good level of confidence in the design's
functional correctness.

Logic Synthesis

Now we will convert this design into hardware schematic with real elements
such as combinational gates and flip-flops. This step is called synthesis.

Logic synthesis tools enable the conversion of RTL description in HDL to a


gate-level netlist. This netlist is a description of the circuit in terms of gates
and connections between them.

Logic synthesis tools ensure that the netlist meets timing, area, and power
specifications. Typically, they have access to different technology node
processes and digital elements libraries and can make intelligent calculations
to meet all these different criteria.

These libraries are obtained from semiconductor fabs that provide data
characteristics for different components such as rise or fall times for flip-
flops, input-output time for combinational gates, etc.

Logic Equivalence

The gate-level netlist is checked for logical equivalence with the RTL.
Sometimes, a gate-level verification is performed where verification of
certain elements is done once again, the difference being this time it is at
the gate level and a lower level of abstraction.

Simulation times tend to be slower because of the huge number of elements


involved in the design and back annotated delay information.
Placement and Routing

Then, the netlist is inputted to the physical design flow, where automatic
place and the route are done with EDA tools' help. The Cadence
Encounter and Synopsys IC Compiler are good examples of these kinds of
tools.

This will select and place standard cells into rows, define ball maps for input
and output, create different metal layers, and place buffers to meet timing.

Once this process is done, a layout is generated and usually sent for
fabrication. This stage is usually handled by the physical design team, who
are well familiar with the technology node and physical implementation
details.

Validation

A sample chip will be made-up either by the same semiconductor firm or


sent to a third-party such as TSMC or Global Foundries.

This sample now goes through a post-silicon validation process where


another team of engineers runs different tester patterns. It is more difficult
to debug in post-silicon validation than pre-silicon verification simply
because the level of visibility into a chip's internal nodes is drastically
reduced.

A million clock cycles would have finished in a second, and tracing back to
the exact time of error will be time-consuming.

If there are any real issues or design bugs found at this stage, this will have
to be fixed in RTL, re-verified, and all the steps that follow this will have to be
performed.

Even though there are multiple steps in the design flow, a lot of the design
activity is usually concentrated on the optimization and verification of the
RTL description of the circuit.

It is important to note that although EDA tools are available to automate the
processes, improper usage will lead to inefficient designs. Hence, a designer
has to make conscious choices during the design process.

Design Abstraction Layers

The Verilog language would be essential to understand the different layers of


abstraction in chip design.

The top layer is the system-level architecture that defines the various sub-
blocks and groups them based on functionality.

For example, a processor cluster can have multiple cache blocks, cores, and
cache coherence logic. All of this will be represented as a single block with
input and output signals.
On the next level, each sub-block is written in a hardware description
language to describe each block's functionality accurately.

Lower level implementation details such as circuit schematics, technology


libraries are ignored at this stage.

For example, a controller block will have multiple Verilog files, each
describing a smaller functionality component.

HDLs are then converted to gate-level schematics that involve technology


libraries that characterize digital elements such as flip-flops.

For example, the digital circuit for a D latch contains NAND gates arranged in
a certain manner such that all combinations of D and E inputs produce an
output Q given by the truth table.

A truth table essentially gives permutation of all input signal levels and the
resulting output level.
The hardware schematic can also be derived from the truth table using K-
maps and Boolean logic. However, it is not useful to follow this method for
more complex digital blocks like controllers and processors.

Implementation of a NAND gate is done by the connection of CMOS


transistors in a particular format. At this level, the transistor channel widths,
Vdd, and the ability to drive the output capacitative load are considered
during the design process.

The final step is the layout of these transistors in silicon using EDA tools to
be fabricated. Some device and technology knowledge would be required at
this level because different layouts end up having different physical
properties like resistance and capacitance, among other implications.

Design Styles

There are primarily two styles followed in the design of digital blocks, one is
top-down, and another is bottom-up methodologies.

1. Top-Down
In this methodology, a top-level block is first defined along with identifying
sub-modules required to build the top block.

Similarly, each sub-blocks is further divided into smaller components, and


the process continues until we reach the leaf cell or a stage where it can't be
further divided.
2. Bottom-up
The first task is to identify the available building blocks. Then put them
together and connected in a certain way to build bigger cells and used to
piece together the top-level block.

We can also use the combination of both flows. Architects define the system-
level view of the design, and designers implement each of the functional
blocks' logic and get synthesized into gates.

A top-down style is followed until this point. However, these gates have been
built by following a bottom-up flow, starting with the smallest block's
physical layout in the best possible area, power, and performance.

These standard cells also have a hardware schematic. And these can be used
to obtain various information such as rise and fall in power, times, and other
delays.

These cells are made available to the synthesis tool, which picks and
instantiates them where required.

Verilog Data Types

Verilog introduces several new data types. These data types make RTL
descriptions easier to write and understand.

The data storage and transmission elements found in digital hardware are
represented using a set of Verilog Hardware Description Language (HDL)
data types.

In Verilog, data types are divided into NETS and Registers. These data
types differ in the way that they are assigned and hold values, and also they
represent different hardware structures.

The Verilog HDL value set consists of four basic values:

Integer and Real Data Types

Many data types will be familiar to C programmers. The idea is that


algorithms modeled in C can be converted to Verilog if the two languages
have the same data types.
Verilog introduces new two-state data types, where each bit is 0 or 1 only.
Using two-state variables in RTL models may enable simulators to be more
efficient. And they are not affecting the synthesis results.

Types Description

bit user-defined size

byte 8 bits, signed

shortint 16 bits, signed

int 32 bits, signed

longint 64 bits, signed

o Two-state integer types

Unlike in C, Verilog specifies the number of bits for the fixed-width types.

Types Description

reg user-defined size

logic identical to reg in every way

integer 32 bits, signed

o Four-state integer types

We preferred logic because it is better than reg. We can use logic where we
have used reg or wire.

Type Description

time 64-bit unsigned

shortreal like a float in C

shortreal like double in C

realtime identical to real

Non-Integer Data Types

Arrays

In Verilog, we can define scalar and vector nets and variables. We can also
define memory arrays, which are one-dimensional arrays of a variable type.

Verilog allowed multi-dimensioned arrays of both nets and variables and


removed some of the restrictions on memory array usage.

Verilog takes this a stage further and refines the concept of arrays and
permits more operations on arrays.
In Verilog, arrays may have either packed or unpacked dimensions, or
both.

Packed dimensions

o Are guaranteed to be laid out contiguously in memory.


o It can be copied on to any other packed object.
o Can be sliced ("part-selects").
o Are restricted to the "bit" types (bit, logic, int, etc.), some of which
(e.g., int) have a fixed size.

Unpacked dimensions

It can be arranged in memory in any way that the simulator chooses. We can
reliably copy an array on to another array of the same type.

For arrays with different types, we have to use a cast, and there are rules for
how an unpacked type is cast to a packed type.

Verilog permits several operations on complete unpacked arrays and slices


of unpacked arrays.

For these, the arrays or slices involved must have the same type and shape,
i.e., the same number and lengths of unpacked dimensions.

The packed dimensions may differ, as long as the array or slice elements
have the same number of bits. The permitted operations are:

o Reading and writing the whole array.


o Reading and writing array slices.
o Reading and writing array elements.
o Equality relations on arrays, slices, and elements

Verilog also includes dynamic arrays (the number of elements may change
during simulation) and associative arrays (which have a non-contiguous
range).

Verilog includes several arrays of querying functions and methods to support


all these array types.

Nets

Nets are used to connect between hardware entities like logic gates and
hence do not store any value.

The net variables represent the physical connection between structural


entities such as logic gates. These variables do not store values except
trireg. These variables have the value of their drivers, which changes
continuously by the driving circuit.

Some net data types are wire, tri, wor, trior, wand, triand, tri0, tri1,
supply0, supply1, and trireg. A net data type must be used when a signal
is:

o The output of some devices drives it.


o It is declared as an input or in-out port.
o On the left-hand side of a continuous assignment.

1. Wire
A wire represents a physical wire in a circuit and is used to connect gates or
modules. The value of a wire can be read, but not assigned to, in a function
or block.

A wire does not store its value but must be driven by a continuous
assignment statement or by connecting it to the output of a gate or module.

2. Wand (wired-AND)
The value of a wand depends on logical AND of all the drivers connected to
it.

3. Wor (wired-OR)
The value of wor depends on the logical OR of all the drivers connected to it.

4. Tri (three-state)
All drivers connected to a tri must be z, except one that determines the tri's
value.

5. Supply0 and Supply1


Supply0 and supply1 define wires tied to logic 0 (ground) and logic 1
(power).

Registers

A register is a data object that stores its value from one procedural
assignment to the next. They are used only in functions and procedural
blocks.

An assignment statement in a procedure acts as a trigger that changes the


value of the data storage element.

Reg is a Verilog variable type and does not necessarily imply a physical
register. In multi-bit registers, data is stored as unsigned numbers, and no
sign extension is done for what the user might have thought were two's
complement numbers.

Some register data types are reg, integer, time, and real.reg is the most
frequently used type.

o Reg is used for describing logic.


o An integer is general-purpose variables. They are used mainly loops-
indices, parameters, and constants. They store data as signed
numbers, whereas explicitly declared reg types store them as
unsigned. If they hold numbers that are not defined at compile-time,
their size will default to 32-bits. If they hold constants, the synthesizer
adjusts them to the minimum width needed at compilation.
o Real in system modules.
o Time and realtime for storing simulation times in test benches. Time
is a 64-bit quantity that can be used in conjunction with the $time
system task to hold simulation time.

Note: A reg need not always represent a flip-flop because it can also represent
combinational logic.
o The reg variables are initialized to x at the start of the simulation. Any
wire variable not connected to anything has the x value.
o The size of a register or wire may be specified during the declaration.
o When the reg or wire size is more than one bit, then register and wire
are declared vectors.

Verilog String

Strings are stored in reg, and the width of the reg variable has to be large
enough to hold the string.

Each character in a string represents an ASCII value and requires 1 byte. If


the variable's size is smaller than the string, then Verilog truncates the
leftmost bits of the string. If the variable's size is larger than the string, then
Verilog adds zeros to the left of the string.

Behavioral Modelling and Timing

In Verilog, Behavioral models contain procedural statements, which control


the simulation and manipulate variables of the data types.

These statements are contained within the procedures. Each of the


procedures has an activity flow associated with it.

During the behavioral model simulation, all the flows defined by


the always and initial statements start together at simulation time zero.

The initial statements are executed once, and the always statements are
executed repetitively.

Example

The register variables a and b are initialized to binary 1 and 0 respectively at


simulation time zero.

The initial statement is completed and not executed again during that
simulation run. This initial statement is containing a begin-end block of
statements. In this begin-end type block, a is initialized first, followed by b.

1.
module behave;
2. reg [1:0]a,b;
3.
4. initial
5. begin
6. a = 'b1;
7. b = 'b0;
8. end
9.
10. always
11. begin
12. #50 a = ~a;
13. end
14.
15. always
16. begin
17. #100 b = ~b;
18. end
19. End module

Procedural Assignments

Procedural assignments are for updating integer, reg,


time, and memory variables. There is a significant difference between a
procedural assignment and continuous assignment, such as:

1. Continuous assignments drive net variables, evaluated, and updated


whenever an input operand changes value.

The procedural assignments update the value of register variables under the
control of the procedural flow constructs that surround them.

2. The right-hand side of a procedural assignment can be any expression


that evaluates to a value. However, part-selects on the right-hand side must
have constant indices. The left-hand side indicates the variable that receives
the assignment from the right-hand side. The left-hand side of a procedural
assignment can take one of the following forms:

o Register, integer, real, or time variable: An assignment to the name


reference of one of these data types.
o Bit-select of a register, integer, real, or time variable: An assignment to
a single bit that leaves the other bits untouched.
o Part-select of a register, integer, real, or time variable: A part-select of
two or more contiguous bits that leave the rest of the bits untouched.
For the part-select form, only constant expressions are legal.
o Memory element: A single word of memory. Bit-selects and part-selects
are illegal on memory element references.
o Concatenation of any of the above: A concatenation of any of the
previous four forms can be specified, which effectively partitions the
result of the right-hand side expression and then assigns the partition
parts, in order, to the various parts of the concatenation.

Delay in Assignment

In a delayed assignment, Δt time units pass before the statement is


executed, and the left-hand assignment is made. With an intra-assignment
delay, the right side is evaluated immediately, but there is a delay
of Δt before the result is placed in the left-hand assignment.

If another procedure changes a right-hand side signal during Δt, it does not
affect the output. Synthesis tools do not support delays.

Syntax

An assignment has the following syntax, such as:


1. Procedural Assignmentvariable = expression
2. Delayed assignment#Δt variable = expression;
3. Intra-assignment delayvariable = #Δt expression;

Blocking Assignments

A blocking procedural assignment statement must be executed before


executing the statements that follow it in a sequential block.

The statement does not prevent the execution of statements that follow it in
a parallel block.

Syntax

The following syntax is for a blocking procedural assignment, such as:

1. <lvalue> = <timing_control> <expression>

o An lvalue is a data type that is valid for a procedural assignment


statement.
o = is the assignment operator, and timing control is the optional intra -
assignment delay. The timing control delay can either be a delay
control or event control. The expression is the right-hand side value
the simulator assigns to the left-hand side.
Continuous procedural assignments and continuous assignments also
use the = assignment operator used by blocking procedural
assignments.

Non-blocking (RTL) Assignments

The non-blocking procedural assignment is used to schedule assignments


without blocking the procedural flow.

We can use the non-blocking procedural statement whenever we want to


make several register assignments within the same time step without regard
to order or dependence upon each other.

Syntax

The following syntax is for a non-blocking procedural assignment:

1. <lvalue> <= <timing_control> <expression>

o An lvalue is a data type that is valid for a procedural assignment


statement.
o <= is the non-blocking assignment operator, and timing control is the
optional intra-assignment timing control.
The timing control delay can be either a delay control or event control.
The expression is the right-hand side value the simulator assigns to the
left-hand side. The non-blocking assignment operator is the same
operator the simulator uses for the less-than-or equal relational
operator.
o The simulator interprets the <= operator as a relational operator when
we use it in an expression and interprets the <= operator as an
assignment operator when you use it in a non-blocking procedural
assignment construct.

When the simulator encounters a non-blocking procedural assignment, the


simulator evaluates and executes the non-blocking procedural assignment in
two steps:

Step 1: The simulator evaluates the right-hand side and schedules the new
value assignment at a time specified by a procedural timing control.

Step 2: At the end of the time step, when the given delay has expired, or
the appropriate event has taken place, the simulator executes the
assignment by assigning the value to the left-hand side.

Conditions

The conditional statement or if-else statement is used to decide whether a


statement is executed.

Syntax

The syntax is as follows:

1. <statement>
2. ::= if ( <expression> ) <statement_or_null>
3. ||= if ( <expression> ) <statement_or_null>
4. else <statement_or_null>
5. <statement_or_null>
6.
7. ::= <statement>
8. ||= ;

o The <expression> is evaluated. If it is true (non-zero known value),


then the first statement executes. If it is false (zero value or the value
is x or z), then the first statement does not execute.
o If there is an else statement and <expression> is false, then the else
statement executes.
o Since the numeric value of the, if expression is tested for being zero,
specific shortcuts are possible.
Case Statement

The case statement is a unique multi-way decision statement that tests


whether an expression matches several other expressions, and branches
accordingly.

The case statement is useful for describing, for example, the decoding of a
microprocessor instruction.

Syntax

The case statement has the following syntax:

1. <statement>
2. ::= case ( <expression> ) <case_item>+ endcase
3. ||= casez ( <expression> ) <case_item>+ endcase
4. ||= casex ( <expression> ) <case_item>+ endcase
5. <case_item>
6. ::= <expression> <,<expression>>* : <statement_or_null>
7. ||= default : <statement_or_null>
8. ||= default <statement_or_null>

o The case expressions are evaluated and compared in the exact order
in which they are given.
o During the linear search, if one of the case item expressions matches
the expression in parentheses, then the statement associated with that
case item is executed.
o If all comparisons fail, and the default item is given, then the default
item statement is executed.
o If the default statement is not given, and all of the comparisons fail,
none of the case item statements are executed.

The case statement differs from the multi-way if-else-if construct in two
essential ways, such as:

1. The conditional expressions in the if-else-if construct are more general


than comparing one expression with several others, as in the case
statement.

2. The case statement provides a definitive result when there are x and z
values in an expression.

Looping Statements

There are four types of looping statements. They are used to controlling the
execution of a statement zero, one, or more times.

1. Forever continuously executes a statement.

2. Repeat executes a statement a fixed number of times.


3. While executes a statement until expression becomes false, if the
expression starts false, the statement is not executed at all.

4. For controls execution of its associated statements by a three-step


process are:

Step 1: Executes an assignment normally used to initialize a variable that


controls the number of loops executed.

Step 2: Evaluates an expression. Suppose the result is zero, then the for
loop exits. And if it is not zero, for loop executes its associated statements
and then performs step 3.

Step 3: Executes an assignment normally used to modify the loop control


variable's value, then repeats step 2.

Syntax

The following are the syntax rules for the looping statements, such as:

1. <statement>
2. ::= forever <statement>
3. ||=forever
4. begin
5. <statement>+
6. end
7.
8.
9. <Statement>
10. ::= repeat ( <expression> ) <statement>
11. ||=repeat ( <expression> )
12. begin
13. <statement>+
14. end
15.
16.
17. <statement>
18. ::= while ( <expression> ) <statement>
19. ||=while ( <expression> )
20. begin
21. <statement>+
22. end
23.
24.
25. <statement>
26. ::= for ( <assignment> ; <expression> ; <assignment> )
27. <statement>
28. ||=for ( <assignment> ; <expression> ; <assignment> )
29. begin
30. <statement>+
31. end

Delay Controls

Verilog handles the delay controls in the following ways, such as:

1. Delay Control

The execution of a procedural statement can be delay-controlled by using


the following syntax:

1. <statement>
2. ::= <delay_control> <statement_or_null>
3. <delay_control>
4. ::= # <NUMBER>
5. ||= # <identifier>
6. ||= # ( <mintypmax_expression> )

The following example delays the execution of the assignment by 10-time


units.

1. #10 rega = regb;

Execution of the assignment delays by the amount of simulation time


specified by the value of the expression.

2. Event Control

The execution of a procedural statement can be synchronized with a value


change on a net or register, or the occurrence of a declared event, by using
the following event control syntax:

1. <statement>
2. ::= <event_control> <statement_or_null>
3.
4. <event_control>
5. ::= @ <identifier>
6. ||= @ ( <event_expression> )
7.
8. <event_expression>
9. ::= <expression>
10. ||= posedge <SCALAR_EVENT_EXPRESSION>
11. ||= negedge <SCALAR_EVENT_EXPRESSION>
12. ||= <event_expression> <or <event_expression>>

*<SCALAR_EVENT_EXPRESSION> is an expression that resolves to a one-bit


value.

Value changes on nets and registers can be used as events to trigger the
execution of a statement. This is known as detecting an implicit event.

Verilog syntax also used to detect change based on the direction of the
change, which is toward the value 1 (posedge) or the value 0 (negedge).

The behavior of posedge and negedge for unknown expression values are:

o A negedge is detected on the transition from 1 to unknown and from


unknown to 0.
o And a posedge is detected on the transition from 0 to unknown and
from unknown to 1.

Procedures

All procedures in Verilog are specified within one of the following four Blocks.

1. Initial blocks
2. Always blocks
3. Task
4. Function

Initial Blocks

The initial and always statements are enabled at the beginning of the
simulation. The initial blocks execute only once, and its activity dies when
the statement has finished.

Syntax

The following syntax is for the initial statement:

1. <initial_statement>
2. ::= initial <statement>

Example

The following example illustrates the use of the initial statement for the
initialization of variables it the starting of simulation.

1. Initial
2. Begin
3. Areg = 0; // initialize a register
4. For (index = 0; index < size; index = index + 1)
5. Memory [index] = 0; //initialize a memory
6. Word
7. End

Another usage of the initial Blocks is the specification of waveform


descriptions that execute once to provide stimulus to the central part of the
circuit being simulated.

1. Initial
2. Begin
3. Inputs = 'b000000;
4. // initialize at time zero
5. #10 inputs = 'b011001; // first pattern
6. #10 inputs = 'b011011; // second pattern
7. #10 inputs = 'b011000; // third pattern
8. #10 inputs = 'b001000; // last pattern
9. End

Always Blocks

The always blocks repeatedly executes. Its activity dies only when the
simulation is terminated. There is no limit to the number of initial and always
blocks defined in a module.

Syntax

The always statement repeats continuously throughout the whole simulation


run. The syntax for the always statement is given below

1. <always_statement>
2. ::= always <statement>

The always statement is only useful when used in conjunction with some
form of timing control because of its looping nature.

Task and Function

Tasks and functions are procedures that are enabled by one or more places
in other procedures.

Verilog Module

A module is a block of Verilog code that implements certain functionality.


Modules can be embedded within other modules, and a higher level module
can communicate with its lower-level modules using their input and output
ports.

Syntax

A module should be enclosed within a module and endmodule keywords.


The name of the module should be given right after the module keyword,
and an optional list of ports may be declared as well.

Note: The ports declared in the list of port declarations cannot be re-declared within the
module's body.

1. module <name> ([port_list]);


2.
3. // Contents of the module
4. endmodule
5.
6. // A module can have an empty portlist
7.
8. module name;
9. // Contents of the module
10.
11. endmodule

All variable declarations, functions, tasks, dataflow statements, and lower


module instances must be defined within the module and endmodule
keywords.

Purpose of a Module

A module represents a design unit that implements specific behavioral


characteristics and will get converted into a digital circuit during synthesis.

Any combination of inputs can be given to the module, and it will provide a
corresponding output.

It allows the same module to be reused to form more significant modules


that implement more complex hardware.

Hardware Schematic

Instead of building up smaller blocks to form bigger design blocks, the


reverse process can also be done.

Consider the breakdown of a simple GPU engine into smaller components


such that each can be represented as a module that implements a specific
feature.

The below GPU engine is divided into five different sub-blocks where each
performs a specific functionality.

The bus interface unit gets data from outside into the design, which gets
processed by another unit to instructions extraction. Other units down the
line process data provided by the previous unit.
Each sub-block can be represented as a module with a specific set of input
and output signals for communication with other modules, and each sub-
block can be further divided into more sub-sub-blocks as required.

Top-level Modules

A top-level module is one that contains all other modules. A top-level module
is not instantiated within any other module.

For example, design modules are usually instantiated within top-level


testbench modules so that simulation can be run by providing input stimulus.

But, the testbench is not instantiated within any other module because it is
a block that encapsulates everything else.

1. Design Top Level

The design code shown below has a top-level module called design. It
contains all other sub-modules required to make the design complete.

The sub-module can have a more nested sub-module, such as mod3 inside
mod1 and mod4 inside mod2.

1. // Design code
2.
3. module mod3 ( [port_list] );
4. reg c;
5.
6. // Design code
7. endmodule
8.
9. module mod4 ( [port_list] );
10. wire a;
11. // Design code
12. endmodule
13.
14. module mod1 ( [port_list] );
15. wire y;
16.
17.
18. mod3 mod_inst1 ( );
19.
20. mod3 mod_inst2 ( );
21.
22. endmodule
23.
24. module mod2 ( [port_list] );
25.
26. mod4 mod_inst1 ( );
27.
28. mod4 mod_inst2 ( );
29.
30. endmodule
31.
32. // Top-level module
33.
34. module design ( [port_list]);
35.
36. wire _net;
37. mod1 mod_inst1 ( );
38.
39. mod2 mod_inst2 ( );
40.
41. endmodule

2. Testbench Top Level

The testbench module contains a stimulus to check the functionality of the


design and primarily used for functional verification by using simulation
tools.

Hence the design is instantiated and called d0 inside the testbench module.
The testbench is the top-level module from a simulator perspective.

1. //------------
2. // Testbench code
3. // this is the top-level module from simulation perspective
4. // because 'design' is instantiated within this module
5. //------------
6. module testbench;
7. design d0 ( [port_list_connections] );
8. //-----------
9.
10. endmodule

Hierarchical Names

A hierarchical structure is formed when modules can be instantiated inside


one another, and hence the top-level module is called the root.

Since each lower module instantiates within a given module, which should
have different identifier names, there will not be any ambiguity in accessing
signals.
A hierarchical name is constructed by a list of these identifiers separated by
dots (.) for each level of the hierarchy. Any signal can be accessed within any
module using the hierarchical path to that particular signal.

RTL Verilog

In the digital circuit design, register-transfer level (RTL) is a design


abstraction which models a synchronous digital circuit in terms of the data
flow between hardware register, and the logical operations performed on
those signals.

Register-transfer-level abstraction is used in HDL to create high-level


representations of a circuit, from which lower-level representations and
ultimately actual wiring can be derived. Design at the RTL level is a typical
practice in modern digital design.

A synchronous circuit consists of two elements, such as:

o Registers (Sequential logic): Registers synchronize the circuit's


operation to the edges of the clock signal, and are the only elements in
the circuit with memory properties. And they are usually implemented
as D flip-flops.
o Combinational logic: Combinational logic performs all the logical
functions in the circuit. And it consists of logic gates.

For example, a simple synchronous circuit is shown in the below image. The
inverter is connected from the output Q to the register's input D to create a
circuit. It changes its state on each rising edge of the CLK. In this circuit, the
combinational logic consists of the inverter.
While designing digital integrated circuits with a hardware description
language, the designs are usually arranged at a higher level of abstraction
than the transistor level or logic gate level.

In HDLs, the designer declares the registers, which roughly correspond to


variables in the programming languages and describes the combinational
logic by using constructs such as if-then-else and arithmetic operations.

This level is called the register-transfer level or RTL. The term RTL
focuses on describing the flow of signals between registers.

This description can usually be directly translated into an equivalent


hardware implementation file using an EDA tool for synthesis. The synthesis
tool also performs logic optimization.

At the register-transfer level, some types of circuits can be recognized. If


there is a cyclic path of logic from a register's output to its input, then the
circuit is called a state machine or sequential logic.

If there are logic paths from a register to another without a cycle, then it is
called a pipeline.

RTL Circuit Design Cycle

RTL is used in the logic design phase of the integrated circuit design cycle.
An RTL description is converted into a gate-level description of the circuit by
a logic synthesis tool.

The synthesis results are then used by placement and routing tools to create
a physical layout. Logic simulation tools may use a design's RTL description
to verify its correctness.

Power Estimation Technique

The most accurate power analysis tools are available for the circuit level, but
even with a switch rather than device-level modelling, tools at the circuit
level have disadvantages. They are either too slow or require too much
memory.

The majorities of these are simulators like SPICE and used by the designers
for many years as performance analysis tools

Due to these disadvantages, gate-level power estimation tools have begun


to gain some acceptance where faster, probabilistic techniques have begun
to gain a foothold.

But it also has its trade-off as speedup is achieved on the cost of accuracy,
especially in the presence of correlated signals.

Over the years, it has been realized that the low power design cannot come
from the circuit- and gate-level optimizations. In contrast, system,
architecture, and algorithm optimizations tend to have the largest impact on
power consumption. Therefore, there has been a shift in the tool developers'
incline towards high-level analysis and optimization tools for power.

Gate Equivalent Technique

It is a technique based on the concept of gate equivalents. The complexity of


chip architecture can be described approximately in terms of gate
equivalents, where the equivalent gate count specifies the average number
of reference gates that are required to implement the particular function.

The total power required for the particular function is estimated by


multiplying the approximated gate equivalents with the average power
consumed per gate. The reference gate can be any gate, e.g., 2-input NAND
gate. This technique is distributed in the following types, such as:

1. Class Independent Power Modeling: It is a technique which tries to


estimate chip area, speed, and power dissipation based on information about
the complexity of the design in terms of gate equivalents.

The functionality is divided among different blocks, but no distinction is


made about the functionality of the blocks.

It is class independent. This technique is used by the Chip Estimation


System (CES). This technique completes the following steps:

Step 1: Identify the functional blocks such as counters, decoders,


multipliers, memories, etc.

Step 2: Assign a complexity in terms of Gate Equivalents. The number of


GE's for each unit type are either taken directly as an input from the user or
fed from a library.

2. Class Depedent Power Modeling: This approach is slightly better than


the previous approach as it takes into account customized estimation
techniques to the different types of functional blocks.

Therefore it is trying to increase the modelling accuracy, which wasn't in the


case of previous techniques such as logic, memory, interconnects,
and clocks.

The power estimation is done in a very similar manner to the independent


case. The basic switching energy is based on a three-input AND gate and is
calculated from technology parameters, e.g., gate width, tox, and metal
width provided by the user.

Disadvantages

This approach also has the following disadvantages, such as:

1. The circuit activities are not modeled accurately as an overall activity


factor is assumed for the entire chip, which is also not trustable as
provided by the user.
2. The activity factors will vary throughout the chip; hence this is not very
accurate and prone to error. This leads to the problem that even if the
model gives a correct estimate for the chip's total power consumption,
the module-wise power distribution is relatively inaccurate.
3. The chosen activity factor gives the correct total power, but the
breakdown of power into logic, clock, memory, etc. is less accurate.
4. This tool is not much different or improved in comparison with CES.

Pre-characterized Cell Libraries Technique

This technique further customizes the power estimation of various functional


blocks by having a separate power model for logic, memory, and
interconnects. These suggest a Power Factor Approximation (PFA)
method for individually characterizing an entire library of functional blocks
such as multipliers, adders, etc. instead of a single gate-equivalent model for
"logic" blocks.

Advantages

Pre-characterized cell libraries technique provides the following advantages:

o Customization is possible in terms of whatever complexity parameters


which are appropriate for that block. For a multiplier, the square of the
word length was appropriate.
o The storage capacity is used in bits for memory, and the word length
alone is adequate for the I/O drivers.

Verilog Scalar and Vector

Verilog needs to represent individual bits as well as groups of bits. A single


bit sequential element is a flip-flop, and a 16-bit sequential element is a
register. For these kinds of tasks, Verilog has scalar and vector.

Scalar and Vector

A net or reg declaration without a range specification is considered 1 bit


wide and is a scalar. If a range is specified, the net or reg becomes a multibit
entity known as a vector.

Vector range specification contains two constant expressions such as:

1. MSB: The most significant bit of constant expression, which is the left-
hand value of the range.
2. LSB: The least significant bit of constant expression, which is the right-
hand value of the range.

A colon should separate the MSB and LSB constant expressions.


The MSB constant expression and the LSB constant expression can be any
value from positive, negative, and zero.

The LSB constant expression can be higher, equal, or less than the MSB
constant expression.

Both the MSB and the LSB expressions should be constant expressions.

Vectors can be declared for all types of net data types and reg data types.
Specifying vectors for integer, real, realtime, and time data types is
illegal. Vector nets and registers are treated as unsigned values.

Syntax

The following is the simplified syntax of the vectors, such as:

1. net_type [msb:lsb] list_of_net_identifiers;


2. reg [msb:lsb] list_of_register_identifiers;

Examples

1. wire o_nor; // single bit scalar net


2. wire [7:0] o_flop; // 8-bit vector net
3. reg parity; // single bit scalar variable
4. reg [31:0] addr; // 32 bit vector variable to store address

The range gives the ability to address individual bits in a vector. The most
significant bit of the vector should be specified as the left-hand value in the
range. While the least significant bit of the vector should be specified on the
right.

1. wire [msb:lsb] name;


2. integer my_msb;
3. wire [15:0] priority; // MSB = 15, LSB = 0
4. wire [my_msb: 2] prior; // illegal

The MSB and LSB should be a constant expression and cannot be substituted
by a variable. But they can be any integer value such as positive, negative,
or zero.

The LSB value can be higher than, less than, or equal to the MSB value.

Bit Selects

Any bit in a vectored variable can be individually selected and assigned a


new value, as shown in the below image. This is called a bit select.

If the bit select is out of bounds or the bit select is x or z, then the value
returned will be x.

1. reg [7:0] addr; // 8-bit reg variable [7, 6, 5, 4, 3, 2, 1, 0]


2. addr [0] = 1; // assign 1 to bit 0 of addr
3. addr [3] = 0; // assign 0 to bit 3 of addr
4. addr [8] = 1; // illegal : bit8 does not exist in addr

Part Selects

The selection of the range of contiguous bits is called the part selected.
There are two types of part selects.

1. Constant part select


2. Indexed part select

1. reg [31:0] addr;


2. addr [23:16] = 8'h23; // bits 23 to 16 will be replaced by the new value 'h2
3 -> constant part-select.

A variable part select allows it to be used effectively in loops to select parts


of the vector. Although the starting bit can be varied, the width has to be
constant.

Syntax

1. [<start_bit> +: <width>] // part-select increments from start-


bit
2. [<start_bit> -: <width>] // part-select decrements from start-
bit

Example

1. module block;
2. reg [31:0] data;
3. int i;
4. initial begin
5. data = 32'hFACE_CAFE;
6. for (i = 0; i < 4; i++) begin
7. $display ("data[8*%0d +: 8] = 0x%0h", i, data[8*i +: 8]);
8. end
9. $display ("data[7:0] = 0x%0h", data[7:0]);
10. $display ("data[15:8] = 0x%0h", data[15:8]);
11. $display ("data[23:16] = 0x%0h", data[23:16]);
12. $display ("data[31:24] = 0x%0h", data[31:24]);
13. end
14. endmodule
Verilog Arrays

Verilog arrays are used to group elements into multi-dimensional objects to


be manipulated more easily. The Verilog does not have user-defined types,
and we are restricted to arrays of built-in Verilog types such as nets,
regs, and other Verilog variable types.

An array is a collection of the same types of variables and accessed using


the same name plus one or more indices.

Each array dimension is declared by having the min and max indices within
the square brackets. Array indices can be written in either direction:

1. array_name[least_significant_index:most_significant_index]
2. array_name[most_significant_index:least_significant_index]

A multi-dimensional array can be declared by having multiple dimensions


after the array declaration.

Any square brackets before the array identifier are part of the data type
replicated in the array.

Verilog arrays are synthesizable so that we can use them in a synthesizable


RTL code.

In C, arrays are indexed from 0 by integers, or converted to pointers. But the


whole array can be initialized, and each element must be read or separately
written in procedural statements.

In Verilog-2001, arrays are indexed from left-bound to right-bound. If they


are vectors, they can be assigned as single units, but not if they are arrays.
Verilog-2001 allows for multiple dimensions.

In Verilog-2001, all data types can be declared as arrays. The wire, reg, and
all other net types can also have a vector width declared. A dimension
declared before the object name is referred to as the vector
width dimension.

The Verilog-2005 specification also calls a one-dimensional array with


elements of type reg a memory. It is beneficial for modeling memory
elements such as read-only memory (ROM), and random access memory
(RAM).

The dimensions declared after the object name is referred to as


the array dimensions. Arrays hold a fixed number of equally-sized data
elements.

Individual elements are accessed by index using a consecutive range of


integers. Some arrays allow access to individual elements using non-
consecutive values of any data types.

Arrays can be classified as fixed-sized arrays, also known as static arrays


whose size cannot change once their declaration is made, or dynamic arrays
can be resized.

Verilog had only one type of array. Verilog arrays can be


either packed or unpacked. Packed array refers to dimensions declared
after the type and before the data identifier name. Unpacked array refers to
the dimensions declared after the data identifier name.
Packed or Fixed Arrays

In Verilog, the term packed array refers to the dimensions declared before
the object name.

A one-dimensional packed array is also called a vector. Packed array divides


a vector into subfields, which can be accessed as array elements. A packed
array is guaranteed to be represented as a contiguous set of bits in
simulation and synthesis.

Packed arrays can be made of only the single-bit data types bit, logic, reg,
enumerated types, and other packed arrays and packed structures. This also
means we cannot have packed arrays of integer types with predefined
widths.

The maximum size of a packed array can be limited but shall be at least
65536 (216) bits.

A packed array is guaranteed to be represented as a contiguous set of bits.

Unpacked Arrays

In Verilog, the term unpacked array is used to refer to the dimensions


declared after the object name.

Unpacked arrays can be made of any data type. Each fixed-size dimension is
represented by an address range, such as [0:1023].

Or a single positive number to specify the size of a fixed-size unpacked


array, such as [1024]. The notation size is equivalent to [0:size-1].

An unpacked array may or may not be so represented as a contiguous set


of bits.

Multi-dimensional Arrays

Multi-dimensional arrays can be declared with both packed and unpacked


dimensions. Creating a multi-dimensional packed array is analogous to
slicing up a continuous vector into multiple dimensions.

When an array has multiple dimensions that can be logically grouped, it is


useful to use the typedef to define the multi-dimensional array in stages to
enhance readability.

Verilog Arrays Indexing and Slicing

Verilog arrays could only be accessed one element at a time. In Verilog


arrays, we can also select one or more contiguous elements of an array. This
is called a slice.

An array slice can only apply to one dimension; other dimensions must have
single index values in an expression.

Verilog Array Operations

Verilog arrays support many more operations than their traditional Verilog
counterparts.

o +: and -: Notation
When accessing a range of a Verilog array slice, we can specify a variable
slice by using the [start+: increment width] and [start-: decrement width]
notations.

They are simpler than needing to calculate the exact start and end indices
when selecting a variable slice. The increment or decrement width must be a
constant.

1. bit signed [31:0] car A [7:0]; // unpacked array of 8 32-bit vec


tors
2. int car B [1:0]; // unpacked array of 2 integers
3. car B = car A [7:6]; // select a 2-vector slice from car
A
4. car B = car A [6+:2]; // equivalent to car A[7:6]

o Assignments and Copying Operations

Verilog arrays support many more operations. The following operations can
be performed on both packed and unpacked arrays.

1. A = B; // reading and writing the array


2. A[i:j] = B[i:j]; // reading and writing a slice of the array
3. A[x+:c] = B[y+:d]; // reading and writing a variable slice of the array
4. A[i] = B[i]; // accessing an element of the array
5. A == B; // equality operations on the array
6. A[i:j] != B[i:j]; // equality operations on slice of the array

o Packed Array Assignment

A Verilog packed array can be assigned at once, such as a multi-bit vector,


an individual element or slice, and more.

1. logic [1:0][1:0][7:0] packed_3d_array;


2.
3. always_ff @(posedge clk, negedge rst_n)
4. if (!rst_n) begin
5. packed_3d_array <= '0; // assign 0 to all elements of arr
ay
6. end
7.
8. else begin
9. packed_3d_array[0][0][0] <= 1'b0; // assign one bit
10. packed_3d_array[0][0] <= A0a; // assign one element

11. packed_3d_array[0][0][3:0] <= 4'ha; // assign part select

12. packed_3d_array[0] <= 16'habcd; // assign slice


13. packed_3d_array <= 32'h01234567; // assign entire arra
y as vector
14. end

o Unpacked Array Assignment


All or multiple elements of a Verilog unpacked array can be assigned to a list
of values.

The list can contain values for individual array elements, or a default value
for the entire array.

1. logic [7:0] a, b, c;
2. logic [7:0] d_array[0:3];
3. logic [7:0] e_array[3:0]; // note index of unpacked dimension is reve
rsed
4.
5. logic [7:0] mult_array_a[3:0][3:0];
6. logic [7:0] mult_array_b[3:0][3:0];
7.
8. always_ff @(posedge clk, negedge rst_n)
9. if (!rst_n) begin
10. d_array <= '{default:0}; // assign 0 to all elem
ents of array
11. end
12.
13. else begin
14. d_array <= '{A00, c, b, a}; // d_array[0]=A00, d_arr
ay[1]=c,
15. d_array[2]=b, d_array[
3]=a
16.
17. e_array <= '{A00, c, b, a}; // e_array[3]=A00, e_arr
ay[2]=c,
18. e_array[1]=b, d_array[
0]=a
19.
20. mult_array_a <= '{'{A00, A01, A02, A03}, '{A04, A05, A06, A07},
21. '{A08, A09, A0a, A0b},
22. '{A0c, A0d, A0e, A0f}}; // assign to full
array
23. mult_array_b[3] <= '{A00, A01, A02, A03}; // assign to slice of a
rray
24. end

Verilog Ports

Port is an essential component of the Verilog module. Ports are used to communicate for a
module with the external world through input and output.

It communicates with the chip through its pins because of a module as a fabricated chip placed
on a PCB.
Every port in the port list must be declared as input, output or inout. All ports declared as
one of them is assumed to be wire by default to declare it, or else it is necessary to declare it
again.

Ports, also referred to as pins or terminals, are used when wiring the module to other modules.

o If the module does not exchange any signals with the environment,
there are no ports in the list.
o Consider a 4-bit full adder that is instantiated inside a top-level
module.
o The module fulladd4 takes input on ports a, b, and c_in and produces
an output on ports sum and c_out.

Port Declaration

Each port in the port list is defined as input, output, or inout based on the
port signal's direction.

If a port declaration includes the net or variable types, then that port is
considered completely declared. It is illegal to declare the same port in a net
or variable type declaration.

And if the port declaration does not include a net or variable type, then the
port can be declared again in a net or variable type declaration.

For example, consider the ports for top and full adder shown in the above
image.

1. module fulladd4(sum, c_out, a, b, c_in); //Begin port declarations section


2. output [3 : 0] sum;
3. output c_out;
4. input [3:0] a, b;
5. input c_in;
6. //End port declarations section
7. <module internals>
8. endmodule
NOTE: By convention, outputs of the module are always first in the port list. This
convention is also used in the predefined modules in Verilog.

Wire and Reg

In Verilog, all port declarations are implicitly declared as wire. If a port is


intended to be a wire, it is sufficient to declare it as output, input, or inout.

Input and inout ports are generally declared as wires. However, if output
ports hold their value, they must be declared as reg as shown below:

1. module DFF(q, d, clk, reset);


2. output q;
3. reg q; // Output port q holds value; so it is declared as reg input d, clk, reset;
4. endmodule

NOTE: Ports of the type input and inout cannot be declared as reg.

Port Connection Rules

There are two methods of making connections between signals specified in


the module instantiation and the ports in a module definition.

1. Connecting by ordered list: It is the simple method for beginners. The


signals to be connected must appear in the module instantiation in the same
order as the ports in the module definition.

2. Connecting ports by name: For large designs where modules have


approx 50 ports or above. In this situation, remembering the order of the
ports in the module definition is complicated and impractical.

Verilog provides the capability to connect external signals to ports by the


port names, rather than by position.

Another main reason for connecting ports by name is that as long as the port
name is not changed, the order of ports in the port list of a module can be
rearranged without changing the port connections in module instantiations.

Ports Variations

o Verilog has undergone a few research, and the original IEEE version
in 1995 had the following way for port declaration.

Here the module declaration had to first list of the names of ports within the
brackets. And then the direction of those ports defined later within the body
of the module.

1. module test (a, b, c);


2.
3. input [3:0] a; // inputs "a" and "b" are wires
4. input [3:0] b;
5. output [3:0] c; // output "c" by default is a wire
6.
7. // Still, we declare them again as wires
8. wire [3:0] a;
9. wire [3:0] b;
10. wire [3:0] c;
11. endmodule
12. module test (a, b, c);
13.
14. input [3:0] a, b;
15. output [3:0] c; // By default c is of type wire
16.
17. // port "c" is changed to a reg type
18.
19. reg [3:0] c;
20.
21. endmodule

o ANSI-C style port naming was introduced in 2001. It allowed the type
to be specified inside the port list.

Verilog assign Statement

Assign statements are used to drive values on the net. And it is also used
in Data Flow Modeling.

Signals of type wire or a data type require the continuous assignment of a


value. As long as the +5V battery is applied to one end of the wire, the
component connected to the other end of the wire will get the required
voltage.

This concept is realized by the assign statement where any wire or other
similar wire (data-types) can be driven continuously with a value. The value
can either be a constant or an expression comprising of a group of signals.

Syntax

The assignment syntax starts with the keyword assign, followed by the signal
name, which can be either a signal or a combination of different signal nets.

The drive strength and delay are optional and mostly used for dataflow
modeling than synthesizing into real hardware.

The signal on the right-hand side is evaluated and assigned to the net or
expression of nets on the left-hand side.

1. assign <net_expression> = [drive_strength] [delay] <expression of different


signals or constant value>

Delay values are useful for specifying delays for gates and are used to model
timing behavior in real hardware. The value dictates when the net should be
assigned with the evaluated value.

Rules

Some rules need to be followed during the use of an assign statement:

o LHS should always be a scalar, vector, or a combination of scalar and


vector nets but never a scalar or vector register.
o RHS can contain scalar or vector registers and function calls.
o Whenever any operand on the RHS changes in value, LHS will be
updated with the new value.
o Assign statements are also called continuous assignments.

Assign reg Variables

We cannot drive or assign reg type variables with an assign statement


because a reg variable is capable of storing data and is not driven
continuously.

Reg signals can only be driven in procedural blocks such as always and
initial.

Implicit Continuous Assignment

When an assign statement is used to assign the given net with some value, it
is called an explicit assignment

If an assignment to be done during the net is declared, it is called


an implicit assignment.

1. wire [1:0] a;
2. assign a = x & y; // Explicit assignment
3. wire [1:0] a = x & y; // Implicit assignment

Combinational Logic Design

Consider the following digital circuit made from combinational gates and the
corresponding Verilog code.

Combinational logic requires the inputs to be continuously driven to maintain


the output, unlike sequential elements like flip flops where the value is
captured and stored at the edge of a clock.

An assigned statement satisfies the purpose because the output o is updated


whenever any of the inputs on the right-hand side change.

1. // This module takes four inputs and performs a Boolean


2. // operation and assigns output to o.
3. // logic is realized using assign statement.
4. module combo (input a, b, c, d, output o);
5. assign o = ~((a & b) | c ^ d);
6. endmodule
Hardware Schematic

After design elaboration and synthesis, a combinational circuit behaves the


same way as modeled by the assign statement.

The signal o becomes 1 whenever the combinational expression on the RHS


becomes true.

Similarly, o becomes 0 when RHS is false. Output o is X from 0ns to 10ns


because inputs are X during the same time.

Verilog Operators

Operators perform an operation on one or more operands within an


expression. An expression combines operands with appropriate operators to
produce the desired functional expression.

1. Arithmetic Operators

For the FPGA, division and multiplication are very expensive, and sometimes
we cannot synthesize division. If we use Z or X for values, the result is
unknown. The operations treat the values as unsigned.

Character Operation performed Example

+ Add b + c = 11

- Subtrac b - c = 9, -b=-10

/ Divide b/a=2

* Multiply a * b = 50

% Modulus b%a=0

2. Bitwise Operators

Each bit is operated, the result is the size of the largest operand, and the
smaller operand is left extended with zeroes to the bigger operand's size.

Character Operation performed Example

~ Invert each bit ~a = 3'b010


& And each bit b & c = 3'b010

| Or each bit a | b = 3'b111

^ Xor each bit a ^ b = 3'b011

^~ or ~^ Xnor each bit a ^~ b = 3'b100

3. Reduction Operators

These operators reduce the vectors to only one bit. If there are the
characters z and x, the result can be a known value.

4. Relational Operators

These operators compare operands and results in a 1-bit scalar Boolean


value. The case equality and inequality operators can be used for unknown
or high impedance values (z or x), and if the two operands are unknown, the
result is a 1.

Character Operation performed Example

> Greater than a > b = 1'b0

< Smaller than a < b = 1'b1

>= Greater than or equal a >= d = 1'bX

<= Smaller than or equal a <= e = 1'bX

== Equality a == b = 1'b0

!= Inequality a != b = 1'b1

=== Case equality e === e = 1'b1

!=== Case inequality a !== d = 1'b1

5. Logical Operators

These operators compare operands and results in a 1-bit scalar Boolean


value.
Character Operation performed Example

! Not true !(a && b) = 1'b1

&& Both expressions true a && b = 1'b0

|| One ore both expressions true a || b = 1'b1

6. Shift Operators

These operators shift operands to the right or left, the size is kept constant,
shifted bits are lost, and the vector is filled with zeroes.

Character Operation performed Example

>> Shift right b >> 1 results 4?b010X

<< Shift left a << 2 results 4?b1000

7. Assignment Operators

There are three assignment operators, each of which performs different


tasks, and are used with different data types:

o assign (continuous assignment)


o <= (non-blocking assignment)
o = (blocking assignment)

8. Other Operators

These are operators used for condition testing and to create vectors.

Character Operation performed Example

?: Conditions testing test cond. ? if true do this or if not do this

{} Concatenate c = {a,b} = 8'101010x0

{{}} Replicate {3{2'b10}}= 6'b101010

9. Operators Precedence

The order of the table tells what operation is made first. The first one has the
highest priority. The () can be used to override the default.

Operators precedence

+, -, !, ~ (Unary)

+,- (Binary)

<<, >>

<,>,<=,>=
==, !=

&

^, ^~ or ~^

&&

||

?:

Verilog Always Block

In Verilog, the always block is one of the procedural blocks. Statements


inside an always block are executed sequentially.

An always block always executes, unlike initial blocks that execute only once
at the beginning of the simulation. The always block should have a sensitive
list or a delay associated with it

The sensitive list is the one that tells the always block when to execute the
block of code.

Syntax

Competitive questions on Structures in HindiKeep Watching

The Verilog always block the following syntax

1. always @ (event)
2. [statement]
3.
4. always @ (event) begin
5. [multiple statements]
6. end

Examples

The symbol @ after reserved word always, indicates that the block will be
triggered at the condition in parenthesis after symbol @.

1. always @ (x or y or sel)
2. begin
3. m = 0;
4. if (sel == 0) begin
5. m = x;
6. end else begin
7. m = y;
8. end
9. end
In the above example, we describe a 2:1 mux, with input x and y. The sel is
the select input, and m is the mux output.

In any combinational logic, output changes whenever input changes. When


this theory is applied to always blocks, then the code inside always blocks
needs to be executed whenever the input or output variables change.

NOTE: It can drive reg and integer data types but cannot drive wire data types.

There are two types of sensitive list in the Verilog, such as:

1. Level sensitive (for combinational circuits).


2. Edge sensitive (for flip-flops).

The code below is the same 2:1 mux, but the output m is now a flip-flop
output.

1. always @ (posedge clk )


2. if (reset == 0) begin
3. m <= 0;
4. end
5. else if (sel == 0) begin
6. m <= x;
7. end
8. else begin
9. m <= y;
10. end

NOTE: The always block is executed at some particular event. A sensitivity list defines
the event.

Sensitivity List

A sensitivity list is an expression that defines when the always block


executed, and it is specified after the @ operator within the parentheses ( ).
This list may contain either one or a group of signals whose value change will
execute the always block.

In the code shown below, all statements inside the always block executed
whenever the value of signals x or y change.

1. // execute always block whenever value of "x" or "y" change


2. always @ (x or y) begin
3. [statements]
4. end

Need of Sensitivity List

The always block repeats continuously throughout a simulation. The


sensitivity list brings a certain sense of timing, i.e., whenever any signal in
the sensitivity list changes, the always block is triggered.

If there are no timing control statements within an always block, the


simulation will hang because of a zero-delay infinite loop.
For example, always block attempts to invert the value of the signal clk. The
statement is executed after every 0-time units. Hence, it executes forever
because of the absence of a delay in the statement.

1. // always block started at time 0 units


2. // But when is it supposed to be repeated
3. // There is no time control, and hence it will stay and
4. // be repeated at 0-time units only and it continues
5. // in a loop and simulation will hang
6.
7. always clk = ~clk;

If the sensitivity list is empty, there should be some other form of time delay.
Simulation time is advanced by a delay statement within the always
construct.

1. always #10 clk = ~clk;

Now, the clock inversion is done after every 10-time units. That's why the
real Verilog design code always requires a sensitivity list.

NOTE: Explicit delays are not synthesizable into logic gates.

Uses of always block

An always block can be used to realize combinational or sequential elements.


A sequential element like flip flop becomes active when it is provided with a
clock and reset.

Similarly, a combinational block becomes active when one of its input values
change. These hardware blocks are all working concurrently independently
of each other. The connection between each is what determines the flow of
data.

An always block is made as a continuous process that gets triggered and


performs some action when a signal within the sensitivity list becomes
active.

In the following example, all statements within the always block executed at
every positive edge of the signal clk

1. // execute always block at the positive edge of signal "clk"


2. always @ (posedge clk) begin
3. [statements]
4. end

Sequential Element Design

The below code defines a module called tff that accepts a data input, clock,
and active-low reset. Here, the always block is triggered either at the
positive edge of the clk or the negative edge of rstn.

1. The positive edge of the clock

The following events happen at the positive edge of the clock and are
repeated for all positive edge of the clock.
Step 1: First, if statement checks the value of active-low reset rstn.

o If rstn is zero, then output q should be reset to the default value of 0.


o If rstn is one, then it means reset is not applied and should follow
default behavior.

Step 2: If the previous step is false, then

o Check the value of d, and if it is found to be one, then invert the value
of q.
o If d is 0, then maintain value of q.

1. module tff (input d, clk, rstn, output reg q);


2. always @ (posedge clk or negedge rstn) begin
3. if (!rstn)
4. q <= 0;
5. else
6. if (d)
7. q <= ~q;
8. else
9. q <= q;
10. end
11. endmodule

2. Negative edge of reset

The following events happen at the negative edge of rstn.

Step 1: First, if statement checks the value of active-low reset rstn. At the
negative edge of the signal, its value is 0.

o If the value of rstn is 0, then it means reset is applied, and output


should be reset to the default value of 0.
o And if the value of rstn is 1, then it is not considered because the
current event is a negative edge of the rstn.

Combinational Element Design

An always block can also be used in the design of combinational blocks.

For example, the digital circuit below represents three different logic gates
that provide a specific output at signal o.
The code shown below is a module with four input ports and a single output
port called o. The always block is triggered whenever any of the signals in
the sensitivity list changes in value.

The output signal is declared as type reg in the module port list because it is
used in a procedural block. All signals used in a procedural block should be
declared as type reg.

1. module combo (input a, input b, input c, input d, output reg o);


2. always @ (a or b or c or d) begin
3. o <= ~((a & b) | (c^d));
4. end
5. endmodule

The signal o becomes 1 whenever the combinational expression on the RHS


becomes true. Similarly, o becomes 0 when RHS is false.

Verilog Initial Block

The always block indicates a free-running process, but the initial block
indicates a process executes exactly once. Both constructs begin execution
at simulator time 0, and both execute until the end of the block.

Initial blocks can be used in either synthesizable or non-synthesizable blocks.


They are commonly used in test benches.

Initial blocks cause particular instructions to be performed at the beginning


of the simulation before any other instructions operate. Initial blocks only
operate once.

A synthesizable initial block is used to set the power-on value of


registers, RAM, and ROM within FPGAs. However, initial blocks cannot be
synthesized in ASICs or CPLDs.

Initial and always block describe independent processes, which means the
statements in one process execute autonomously.

Both types of processes consist of procedural statements, and both start


immediately as the simulator is started.

The main differences between them are:

o The initial processes execute once, whereas always process repeatedly


execute forever.
o An always process must contain timing statements that will
occasionally block execution and allow time to advance.

Syntax

Verilog initial block follows the following syntax:

1. initial
2. [single statement]
3.
4. initial begin
5. [multiple statements]
6.
7. end

Initial block uses

An initial block is not synthesizable and cannot be converted into a hardware


schematic with digital elements.

The initial blocks do not have more purpose than to be used in simulations.
These blocks are primarily used to initialize variables and drive design ports
with specific values.

Initial Block Execution

An initial block is started at the beginning of a simulation at time 0 unit. This


block will be executed only once during the entire simulation. Execution of
an initial block finishes once all the statements within the block are
executed, as shown in the following image.

The image shown above has a module called behave, which has a and b
internal signals.

The initial block has only one statement, and hence it is not necessary to
place the statement within begin and end.

This statement assigns the value 2'b10 to a when the initial block is started
at time 0 units.

Initial Block Delay Element


The code shown below has an additional statement that assigns some value
to the signal b. However, this happens only after 10-time units from the
execution of the previous statement.

For example, If a is assigned first with the given value and then after 10-time
units, b is assigned to 0.

Initial Blocks in a module

There are no limits to the number of initial blocks that can be defined inside
a module. The code shown below has three initial blocks, all of which are
started at the same time and run in parallel.

However, depending on the statements and the delays within each initial
block, the time taken to finish the block may vary.

NOTE: $finish is a Verilog system task that tells the simulator to terminate the current
simulation.

In the above image, the first block has a delay of 20 units, while the second
has a total delay of 50 units (10 + 40), and the last block has a delay of 60
units. Hence the simulation takes 60-time units to complete since there is
atleast one initial block still running until 60-time units.

If the last block had a delay of 30-time units, as shown below, the simulation
would have ended at 30-time units, thereby killing all the other initial blocks
that are active at that time.

1. initial begin
2. #30 $finish;
3. end
Verilog Block Statements
The block statements are the grouping of two or more statements together,
which act syntactically like a single statement. There are two types of blocks
in the Verilog:

o Sequential block
o Parallel block

These blocks can be used if more than one statement should be executed.
All statements in the sequential blocks will be executed sequentially in the
given order.

If a timing control statement appears within a block, then the next statement
will be executed after that delay. The sequential block shall be delimited by
the keywords begin and end.

All statements in the parallel blocks are executed at the same time or
concurrently. It means that the next statement's execution will not be
delayed even if the previous statement contains a timing control statement.
The parallel block shall be delimited by the keywords fork and join.

Sequential Block
Statements are wrapped using begin and end keywords and executed
sequentially in the given order. Delay values are treated relative to the time
of execution of the previous statement.

After all the statements within the block are executed, control may be
passed to some other place.

Syntax

Sequential block statement follows the following syntax:

1. begin: name
2. statement1;
3. …………..
4. end

Characteristics
The sequential block has the following characteristics, such as:

o Statements will be executed in the sequence, one after another.


o Delay values for each statement are treated relative to the simulation
time of the previous statement's execution.
o Control can pass out of the block after the last statement executes.

Example

1. module design0;
2. bit [31:0] data;
3.
4. // initial block starts at time 0
5.
6. initial begin
7.
8. // After 10 time units, data becomes 0xfe
9. #10 data = 8'hfe;
10. $display ("[Time=%0t] data=0x%0h", $time, data);
11.
12. // After 20 time units, data becomes 0x11
13. #20 data = 8'h11;
14. $display ("[Time=%0t] data=0x%0h", $time, data);
15. end
16. endmodule

In the above example, first statement in begin and end block will be
executed at 10 time units, and the second statement at 30 time units
because of the relative nature. It is 20 time units after execution of the
previous statement.

1. ncsim> run
2. [Time=10] data=0xfe
3. [Time=30] data=0x11
4. ncsim: *W,RNQUIE: Simulation is complete.

Parallel Block
A parallel block can execute statements concurrently, and delay control can
be used to provide the assignments' time-ordering. Statements are launched
in parallel by wrapping them within the fork and join keywords.

Syntax

The parallel block has the following simplified syntax:

1. fork: name
2. statement;
3. …………
4. join
Characteristics

A parallel block has the following characteristics:

o Statements will execute concurrently.


o Delay values for each statement are considered relative to the
simulation time of entering the block.
o Delay control can be used to provide time-ordering for assignments.
o Control can pass out of the block when the last time-ordered statement
executes.

Example

1. initial begin
2. #10 data = 8'hfe;
3. fork
4. #20 data = 8'h11;
5. #10 data = 8'h00;
6. join
7. end

In the above example, fork and join block will be launched after executing
the statement at 10-time units.

Statements within this block will be executed in parallel, and the first
statement will be the one where data is assigned a value of 8'h00 since the
delay for that is 10-time units after the fork-join launch.

After 10 more time units, the first statement will be launched and data will
get the value 8'h11.

1. initial begin
2. #10 data = 8'hfe;
3. fork
4. #10 data = 8'h11;
5. begin
6. #20 data = 8'h00;
7. #30 data = 8'haa;
8. end
9. join
10. end

There is a begin-end block in the above example, and all statements within
the begin-end block will be executed sequentially. Still, the block itself will be
launched in parallel, along with the other statements. Data will get 8'h11 at
20-time units, 8'h00 at 30-time units, and 8'haa at 60-time units.

Naming of blocks
Both sequential and parallel blocks can be named by
adding name_of_block after the begin and fork keywords. By doing this,
the block can be referenced in a disable statement.

1. begin: name_seq
2. [statements]
3. end
4. fork: name_fork
5. [statements]
6. join

Verilog Assignments
Placing values onto variables and nets are called assignments. There are
three necessary forms:

1. Procedural
2. Continuous
3. Procedural continuous

Legal LHS values


An assignment has two parts, right-hand side (RHS) and left-hand side (LHS)
with an equal symbol (=) or a less than-equal symbol (<=) in between.
Assignment Type Left-hand Side

Procedural
o Variables (vector or scalar)
o Bit-select or part-select of an integer, vector reg, or
time variable.
o Memory word.
o Concatenation of any of the above.

Continuous
o Net (vector or scalar)
o Bit-select or part-select of a vector net.
o Concatenation of bit-selects and part-selects.

Procedural
o Variable or net (scalar/vector)
Continuous
o Part-select or bit-select of a vector net.

The RHS can contain any expression that evaluates to a final value while the
LHS indicates a variable or net to which RHS's value is being assigned.

Procedural Assignment
Procedural assignments occur within procedures such as initial, always,
task, and functions are used to place values onto variables. The variable
will hold the value until the next assignment to the same variable.

The value will be placed onto the variable when the simulation executes this
statement during simulation time. This can be modified and controlled the
way we want by using control flow statements such as if-else-if, looping,
and case statement mechanisms.

1. reg [7:0] data;


2. integer count;
3. real period;
4.
5. initial begin
6. data = 8'h3e;
7. period = 4.23;
8. count = 0;
9. end
10.
11. always @ (posedge clk)
12. count++;

Variable Declaration Assignment

An initial value can be placed onto a variable at the time of its declaration.
The assignment does not have the duration and holds the value until the
next assignment to the same variable happens.
NOTE: The variable declaration assignments to an array are not allowed.

1. module my_book;
2. reg [31:0] data = 32'hdead_cafe;
3. initial begin
4. #20 data = 32'h1234_5678; // data will have dead_cafe from time 0 to 20

5. end
6. endmodule
7. reg [3:0] a = 4'b4;
8. reg [3:0] a;
9. initial a = 4'b4;

If the variable is initialized during declaration and at 0 times in an initial


block as shown below, the order of evaluation is not guaranteed, and hence
can have either 8'h05 or 8'hee.

1. module my_book;
2. reg [7:0] addr = 8'h05;
3. initial
4. addr = 8'hee;
5. endmodule
6. reg [3:0] array [3:0] = 0; // illegal
7. integer i = 0, j; // declares two integers i,j and 0 is assigned
to i
8. real r2 = 4.5, r3 = 8; // declares two real numbers r2,r3 and are assigne
d 4.5, 8 resp.
9. time startTime = 40; // declares time variable with initial value 40

Continuous Assignment
This is used to assign values onto scalar and vector nets. And it happens
whenever there is a change in the RHS.

It provides a way to model combinational logic without specifying an


interconnection of gates and makes it easier to drive the net with logical
expressions.

1. // Example model of an AND gate


2. wire a, b, c;
3. assign a = b & c;

Whenever b or c changes its value, the whole expression in RHS will be


evaluated and updated with the new value.

Net Declaration Assignment

This allows us to place a continuous assignment on the same statement that


declares the net.

NOTE: Only one declaration assignment is possible because a net can be declared only
once.
1. wire penable = 1;

Procedural Continuous Assignment


These are procedural statements that allow expressions to be continuously
assigned to variables or nets. And these are the two types.

1. Assign deassign: It will override all procedural assignments to a variable


and deactivate it using the same signal with deassign.

The value of the variable will remain the same until the variable gets a new
value through a procedural or procedural continuous assignment.

The LHS of an assign statement cannot be a part-select, bit-select, or an


array reference, but it can be a variable or a combination of the variables.

1. reg q;
2. initial begin
3. assign q = 0;
4. #10 deassign q;
5. end

2. Force release: These are similar to the assign deassignstatements but


can also be applied to nets and variables.

The LHS can be a bit-select of a net, part-select of a net, variable, or a net


but cannot be the reference to an array and bit or part select of a variable.

The force statement will override all other assignments made to the variable
until it is released using the release keyword.

1. reg o, a, b;
2. initial begin
3. force o = a & b;
4. ……….
5. release o;
6. end

Verilog Blocking and Non-blocking


Verilog supports blocking and non-blocking assignments statements within the always block
with their different behaviors.

The blocking assignment is similar to software assignment statements found in most popular
programming languages. The non-blocking assignment is the more natural assignment statement
to describe many hardware systems, especially for synthesis.

The blocking assignments can only be used in a few situations, such as modeling combinational
logic, defining functions, or implementing testbench algorithms. All IEEE P1364.1 compliant
synthesis tools are required to support both blocking and non-blocking assignments in explicit-
style code, with the restriction that each variable and each block may use only one or the other
kind of assignment.

Blocking Assignment
Blocking assignment statements are assigned using (=) operator and are
executed one after the other in a procedural block. But, it will not prevent
the execution of statements that run in a parallel block.

1. module Block;
2. reg [7:0] a, b, c, d, e;
3.
4. initial begin
5. a = 8'hDA;
6. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
7. b = 8'hF1;
8. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
9. c = 8'h30;
10. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
11. end
12.
13. initial begin
14. d = 8'hAA;
15. $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
16. e = 8'h55;
17. $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
18. end
19. endmodule

There are two initial blocks which are executed in parallel. Statements are executed
sequentially in each block and both blocks finish at time 0ns.

To be more specific, variable is assigned first, that followed by the display statement which is
then followed by all other statements.

This is visible in the output where variable b and c are 8'hxx in the first display statement. This is
because variable b and c assignments have not been executed yet when the first $display is
called.

1. ncsim> run
2. [0] a=0xda b=0xx c=0xx
3. [0] a=0xda b=0xf1 c=0xx
4. [0] a=0xda b=0xf1 c=0x30
5. [0] d=0xaa e=0xx
6. [0] d=0xaa e=0x55
7. ncsim: *W,RNQUIE: Simulation is complete.

In the below example, we'll add a few delays into the same set of statements to see how it reacts
and behaves.

1. module Block;
2. reg [7:0] a, b, c, d, e;
3. initial begin
4. a = 8'hDA;
5. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
6. #10 b = 8'hF1;
7. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
8. c = 8'h30;
9. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
10. end
11. initial begin
12. #5 d = 8'hAA;
13. $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
14. #5 e = 8'h55;
15. $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
16. end
17. endmodule

After execution, it gives the following data.

1. ncsim> run
2. [0] a=0xda b=0xx c=0xx
3. [5] d=0xaa e=0xx
4. [10] a=0xda b=0xf1 c=0xx
5. [10] a=0xda b=0xf1 c=0x30
6. [10] d=0xaa e=0x55
7. ncsim: *W,RNQUIE: Simulation is complete.

Non-blocking Assignment
Non-blocking assignment statements are allowed to be scheduled without blocking the execution
of the following statements and is specified by a (<=) symbol.

The same symbol is used as a relational operator in expressions, and as an assignment operator in
the context of a non-blocking assignment.

Take the same example as above, replace all (=) symbols with a non-blocking assignment
operator (<=), we'll get the difference in the output.

1. module Block;
2. reg [7:0] a, b, c, d, e;
3. initial begin
4. a <= 8'hDA;
5. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
6. b <= 8'hF1;
7. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
8. c <= 8'h30;
9. $display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
10. end
11. initial begin
12. d <= 8'hAA;
13. $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
14. e <= 8'h55;
15. $display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
16. end
17. endmodule

Now, all the $display statements printed 'h'x. The reason for this behavior
is the execution of the non-blocking assignment statements.

The RHS of every non-blocking statement of a particular time-step is


captured and moves onto the next statement.

The captured RHS value is assigned to the LHS variable only at the end of
the time-step.

1. ncsim> run
2. [0] a=0xx b=0xx c=0xx
3. [0] a=0xx b=0xx c=0xx
4. [0] a=0xx b=0xx c=0xx
5. [0] d=0xx e=0xx
6. [0] d=0xx e=0xx
7. ncsim: *W,RNQUIE: Simulation is complete.

Verilog Control Blocks


Hardware behavior cannot be implemented without conditional statements
and other ways to control the flow of logic. Verilog has a set of mechanisms
and control flow blocks.

if-else-if
This conditional statement is used to decide whether certain statements will
be executed or not. It is very similar to the if-else-if statements in C
language. If the expression evaluates to true, then the first statement will be
executed.

If the expression evaluates to false and if an else part exists, the else part
will be executed.

Syntax

Following is the most simplified syntax of the if-else-if conditional statement:

1. // if statement without else part


2. if (expression)
3. [statement]
4.
5. // if statement with an else part
6. if (expression)
7. [statement]
8. else
9. [statement]
10.
11. // if else for multiple statements should be
12. // enclosed within "begin" and "end"
13.
14. if (expression) begin
15. [multiple statements]
16. end else begin
17. [multiple statements]
18. end
19.
20. // if-else-if statement
21. if (expression)
22. [statement]
23. else if (expression)
24. [statement]
25.
26. else
27. [statement]

The else part of an if-else is optional, and it can create confusion. To avoid
this confusion, it's easier to always associate the else to the previous if that
lacks an else. Another way is to enclose statements within a begin-end block.
The last else part handles the none-of-the-above or default case where none
of the other conditions were satisfied.

Loops provide a way of executing single or multiple statements within a


block one or more number of times. In Verilog, there are four different types
of looping statements.

1. Forever loop
This loop will continuously execute the statements within the block.

Syntax

1. Forever
2. [statement]
3. forever begin
4. [multiple statements]
5. end

Example

1. module my_block;
2. initial begin
3. forever begin
4. $display ("This will be printed forever, simulation can hang ...");
5. end
6. end
7. endmodule

After the execution of the above example, it produces the following data.

1. ncsim> run
2. This will be printed forever, simulation can hang
3. This will be printed forever, simulation can hang
4. ...
5. ...
6. This will be printed forever, simulation can hang
7. This will be printed forever, simulation can hang
8. This will be printed forever, simulation can hang
9. This will be printed forever, simulation can hang
10. …
11. …
12. Result reached a maximum of 5000 lines.
13. Killing process.

2. Repeat loop
This will execute statements a fixed number of times. If the expression
evaluates to an X or Z, it will be treated as zero and not executed.

Syntax

1. repeat ([num_of_times]) begin


2. [statements]
3. end
4. repeat ([num_of_times]) @ ([some_event]) begin
5. [statements]
6. end

Example

1. module my_block;
2. initial begin
3. repeat(5) begin
4. $display("This is a new iteration ...");
5. end
6. end
7. endmodule

The above code generates the following outcome.

1. ncsim> run
2. This is a new iteration
3. This is a new iteration
4. This is a new iteration
5. This is a new iteration
6. This is a new iteration
7.
8. ncsim: *W,RNQUIE: Simulation is complete.

3. ile loop
This will execute statements as long as an expression is true and will exit
once the condition becomes false. If the condition is false from the start,
statements will not be executed at all.

Syntax

1. while (expression) begin


2. [statements]
3. end

Example

1. module my_block;
2. integer i = 5;
3. initial begin
4. while (i > 0) begin
5. $display ("Iteration#%0d", i);
6. i = i - 1;
7. end
8. end
9. endmodule

Run the above code, and we will get the following output.

1. ncsim> run
2. Iteration#5
3. Iteration#4
4. Iteration#3
5. Iteration#2
6. Iteration#1
7. ncsim: *W,RNQUIE: Simulation is complete.

4. For loop
For loop controls the statements using a three-step process:

o Initialize a loop counter variable.


o Evaluate the expression, usually involving the loop counter variable.
o Increment loop counter variable so that the expression will become
false at a later time, and the loop will exit.

Syntax

1. for ( initial_assignment; condition; increment_variable) begin


2. [statements]
3. end

Example

1. module my_block;
2. integer i = 5;
3. initial begin
4. for (i = 0; i < 5; i = i + 1) begin
5. $display ("Loop #%0d", i);
6. end
7. end
8. endmodule

After execution the for loop code, the output looks like

1. ncsim> run
2. Loop #0
3. Loop #1
4. Loop #2
5. Loop #3
6. Loop #4
7. ncsim: *W,RNQUIE: Simulation is complete.

Verilog Functions
The purpose of a function is to return a value that is to be used in an
expression. A function definition always starts with the function keyword
followed by the return type, name, and a port list enclosed in parentheses.
And it ends with the endfunction keyword.

Functions should have at least one input declaration and a statement that
assigns a value to the register with the same name as the function. And the
return type can be void if the function does not return anything.

Functions can only be declared inside a module declaration and can be


called through always blocks, continuous assignments, or other functions. In
a continuous assignment, they are evaluated when any of its declared inputs
change. In a procedure, they are evaluated when invoked.

Functions describe combinational logic. Functions are an excellent way to


reuse procedural code since modules cannot be invoked from a procedure.

The returned type or range declaration followed by a function identifier and


semicolon should appear after the function keyword. A function can contain
declarations of returned type, parameters, range, registers, events, and
input arguments. These declarations are similar to module items declaration.

Net declarations are illegal. Declaration of registers, parameters, events,


range, and returned type is not required. A function without a range or return
type declaration returns a one-bit value.

Any expression can be used as a function call argument. Functions cannot


contain any time-controlled statements, and they cannot enable tasks.
Functions can return only one value.

When we find specific pieces of code to be repetitive and called multiple


times within the RTL, they mostly do not consume simulation time. They
might involve complex calculations that need to be done with different data
values.

In this situation, we can declare a function and place the repetitive code
inside the function and allow it to return the result.
It will reduce the number of lines in the RTL. Now we need to do a function
call and pass data on which the computation needs to be performed.

Syntax

Following is the syntax to use a function in the Verilog:

1. function [automatic] [return_type] name ([port_list]);


2. [statements]
3.
4. endfunction

The keyword automatic will make the reentrant function and items declared
within the task that are dynamically allocated rather than shared between
different invocations of the task. This will be useful for recursive functions
and when the same function is executed concurrently by N processes.

Function declarations
A function declaration specifies the function's name, the function input
arguments, the variables (reg) used within the function, the width of the
function return value, and the function local parameters and integers.

Syntax

Following is the specified syntax to declare function in the Verilog:

1. function [msb: lsb] function_name;


2. input [msb: lsb] input_arguments;
3. reg [msb: lsb] reg_variable_list;
4. parameter [msb: lsb] parameter_list;
5. integer [msb: lsb] integer_list;
6. [statements]
7. Endfunction

Example

1. function [7:0] sum;


2. input [7:0] a, b;
3. begin
4. sum = a + b;
5. end

Function Return Value


The function definition will implicitly create an internal variable of the same
name as that of the function.

Hence it is illegal to declare another variable of the same name inside the
scope of the function. The return value is initialized by assigning the function
result to the internal variable.

1. sum = a + b;
Calling a Function
A function call is an operand with an expression. A function call must specify
in its terminal list all the input parameters.

Example

1. reg [7:0] result;


2. reg [7:0] a, b;
3. initial begin
4. a = 4;
5. b = 5;
6. #10 result = sum (a, b);
7. end

Rules
The following are some of the general rules for the Verilog functions:

o A function cannot contain any time-controlled statements such as #,


@, wait, posedge, and negedge.
o A function cannot start a task because it may consume simulation time
but can call other functions.
o A function should have atleast one input argument.
o A function cannot have non-blocking assignments or force-release or
assign-deassign.
o A function cannot have any triggers.
o A function cannot have an inout or output declaration.
o Functions must contain a statement that assigns the return value to
the implicit function name register.

Verilog Task
A function is meant to do some processing on the input and return a single
value. In contrast, a task is more general and can calculate multiple result
values and return them using output and inout type arguments.

Tasks can contain time-consuming simulation elements such as @,


posedge, and others. Tasks are used in all programming languages,
generally known as procedures or subroutines.

Data is passed to the task, the processing is done, and the result returned.
They have to be specifically called, with the data ins and outs, rather than
just wired into the general netlist.

Included in the main body of code, they can be called many times, reducing
code repetition.

o Tasks are defined in the module in which they are used. It is possible
to define a task in a separate file and use the compiled directive,
including the task in the file, which instantiates the task.
o Tasks can include timing delays, such as posedge, negedge, #
delay, and wait.
o Tasks can have any number of inputs and outputs.
o The variables declared within the task are local to that task. The order
of declaration within the task defines how the variables passed to the
task by the caller are used.
o Tasks can take, drive, and source global variables when no local
variables are used. When local variables are used, the output is
assigned only at the end of task execution.
o Tasks can call another task or function.
o Tasks can be used for modeling both combinational and sequential
logic.

A task must be specifically called with a statement. It cannot be used within


an expression as a function can.

Syntax

A task begins with keyword task and ends with keyword endtask. Inputs and
outputs are declared after the keyword task.

Local variables are declared after input and output declaration.

1. // Style 1
2.
3. task [name];
4. input [port_list];
5. inout [port_list];
6. output [port_list];
7. begin
8. [statements]
9. end
10. endtask
11.
12.
13. // Style 2
14.
15. task [name] (input [port_list], inout [port_list], output [port_list]);
16. begin
17. [statements]
18. end
19. endtask

The keyword automatic will make the reentrant task. Otherwise, it will be
static by default. If a task is static, all its member variables will be shared
across different invocations of the same task that has been launched
concurrently.

NOTE: Hierarchical references cannot access an automatic task item.


Calling a task
If the task does not need any arguments, then a list of arguments can be
avoided. If the task needs arguments, they can be provided in the same
statement as its invocation.

1. //Style 1
2. task sum (input [7:0] a, b, output [7:0] c);
3. begin
4. c = a + b;
5. end
6. endtask
7.
8. // Style 2
9. task sum;
10. input [7:0] a, b;
11. output [7:0] c;
12. begin
13. c = a + b;
14. end
15. endtask
16. initial begin
17. reg [7:0] x, y , z;
18. sum (x, y, z);
19. end

The task-enabling arguments (x, y, z) correspond to the arguments (a, b, c)


defined by the task.

Since a and b are inputs, values of x and y will be placed in a and b,


respectively. Because c is declared output and connected with z during
invocation, the sum will automatically be passed to the variable z from c.

Global tasks
Tasks that are declared outside all modules are called global tasks as they
have a global scope and can be called within any module.

1. // This task is outside all modules


2. task display();
3. $display("Hello");
4. endtask
5. module des;
6. initial begin
7. display();
8. end
9. endmodule

After executes the above code, it produces the following output.

1. xcelium> run
2. Hello
3. xmsim: *W,RNQUIE: Simulation is complete.

Difference between Function and Task

Function Task

It cannot have time-controlling It can contain time-controlling


statements/delay and hence executes in statements/delay and may only
the same simulation time unit. complete at some other time.

It cannot enable a task. It can enable other tasks and


functions.

The function should have atleast one Tasks can have zero or more
input argument and cannot have output arguments of any type.
or inout arguments.

A function can return only a single value. It cannot return a value but can
achieve the same effect using output
arguments.

Verilog Case Statement


The case statement checks if the given expression matches one among the
other expressions inside the list and branches. It's typically accustomed
implement a device.

The if-else construct may not be applicable if there unit of measurement


many conditions to be checked and would synthesize into a priority encoder
instead of a device.

In Verilog, a case statement includes all of the code between the Verilog
keywords, case ("casez", "casex"), and endcase. A case statement can be a
select-one-of-many construct that is roughly like Associate in nursing if-else-
if statement.

Syntax

A Verilog case statement starts with the case keyword and ends with the
endcase keyword.

The expression within parentheses area unit aiming to be evaluated


specifically once and is compared with the list of alternatives inside the order
they are written.

And the statements that the selection matches the given expression unit of
measurement dead. A block of multiple statements ought to be sorted and
be within begin and end.

1. case ()
2. case_item1 :
3. case_item2,
4. case_item3 :
5. case_item4 :
6. begin
7.
8. end
9. default:
10.
11. endcase

If none of the case things match the given expression, statements within the
default item unit of measurement dead. The default statement is non
mandatory, and there's only one default statement throughout a case
statement. Case statements are nested.

Execution will exit the case block whereas not doing one thing if none of the
items match the expression, and a default statement is not given.

Example

The following vogue module includes a 2-bit opt for signal to route one
among the three different 3-bit inputs to the sign stated as out.

A case statement is used to assign the correct input to output supported the
value of sel. Since sel can be a 2-bit signal, it'll have twenty 2 combos, zero
through 3. The default statement helps to line output to zero if sel is 3.

1. module my_mux (input [2:0] a, b, c, // three 3-bit inputs


2. [1:0]sel, // 2-bit opt for signal to choose on from a, b, c

3. output reg [2:0] out); // Output 3-bit signal


4. // invariably block is dead whenever a, b, c or sel changes in value
5. invariably @ (a, b, c, sel) begin
6. case(sel)
7. 2'b00 : out = a; // If sel=0, output can be a
8. 2'b01 : out = b; // If sel=1, output is b
9. 2'b10 : out = c; // If sel=2, output is c
10. default : out = 0; // If sel is something, out is commonly zero
11. endcase
12. end
13. endmodule

Case Statement Header


A case statement header consists of the case ("casez", "casex") keyword
followed by the case expression, usually all on one line of code.

When adding full_case or parallel_case directives to a case statement, the


directives unit of measurement added as a comment in real time following
the case expression at the tip of the case statement header and before any
of the case things on ensuing code lines.

Case item
The case item is that the bit, vector, or Verilog expression accustomed
compare against the case expression.

Unlike different high-level programming languages like 'C', the Verilog case
statement includes implicit break statements.
The first case item that matches this case expression causes the
corresponding case item statement to be dead, thus all of the rest of the
case things unit of measurement skipped for this undergo the case
statement.

Case item statement


A case item statement is one or plenty of Verilog statements dead if the case
item matches this case expression. Not like VHDL, Verilog case things can
themselves be expressions.

To alter the parsing of Verilog code document, Verilog case item statements
ought to be enclosed between the keywords "begin" and "end" if over one
statement is to be dead for a specific case item.

Casez
In Verilog, there is a casez statement, a variation of the case statement that
enables "z" and "?" values to be treated throughout case-comparison as
"don't care" values.

"Z" and "?" unit of measurement treated as a don't care if they are inside the
case expression or if they are inside the case item.

When secret writing a case statement with "don't care," use a casez
statement and use "?" characters instead of "z" characters inside the case
things to purpose "don't care" bits.

Casex
In Verilog, there is a casex statement, a variation of the case statement that
enables "z", "?", and "x" values to be treated throughout comparison as
"don't care" values.

"x", "z" and "?" unit of measurement treated as a don't care if they are inside
the case expression or if they are inside the case item.

Full Case Statement


A full case statement can be a case statement inside that all getable case-
expression binary patterns are matched to a case item or a case default.

If a case statement does not embrace a case default, and it's getable to go
looking out a binary case expression that does not match any of the printed
case things, the case statement is not full.

A full case statement can be a case statement inside that every getable
binary, non-binary, and mixture of binary and non-binary patterns is boxed in
as a case item inside the case statement.

Verilog does not would like case statements to be either synthesis or high-
density lipoprotein simulation full, but Verilog case statements is made full
by adding a case default. VHDL desires case statements to be high-density
lipoprotein simulation full, that usually desires Associate in Nursing "others"
clause.

Parallel Case Statement


A parallel case statement can be a case statement inside that it's only
getable to match a case expression to only one case item.
If it's getable to go looking out a case expression which may match over one
case item, the matching case things unit of measurement stated as
overlapping case things, and so the case statement is not parallel.

Hardware Schematic
The RTL code is elaborated to get a hardware schematic that represents a 4
to 1 multiplexer.

After executes the above design, the output is zero when sel is 3 and
corresponds to the assigned inputs for other values.

1. ncsim> run
2. [0] a=0x4 b=0x1 c=0x1 sel=0b11 out=0x0
3. [10] a=0x5 b=0x5 c=0x5 sel=0b10 out=0x5
4. [20] a=0x1 b=0x5 c=0x6 sel=0b01 out=0x5
5. [30] a=0x5 b=0x4 c=0x1 sel=0b10 out=0x1
6. [40] a=0x5 b=0x2 c=0x5 sel=0b11 out=0x0
7. ncsim: *W,RNQUIE: Simulation is complete.

In a case statement, the comparison only succeeds when each bit of the
expression matches one of the alternatives including 0, 1, x and z. In the
above example, if any of the bits in sel is either x or z,
the default statement will be executed because none of the other
alternatives matched. In such a case, output will be all zeros.

1. ncsim> run
2. [0] a=0x4 b=0x1 c=0x1 sel=0bxx out=0x0
3. [10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x0
4. [20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x0
5. [30] a=0x5 b=0x6 c=0x5 sel=0bzx out=0x0
6. [40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x0
7. [50] a=0x6 b=0x5 c=0x2 sel=0bxz out=0x0
8. [60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x0
9. [70] a=0x7 b=0x2 c=0x6 sel=0bzz out=0x0
10. [80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x0
11. [90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x0
12. ncsim: *W,RNQUIE: Simulation is complete.

If the case statement in design has x and z in the case item alternatives, the
results will differ.
1. module mux (input [2:0] a, b, c, output reg [2:0] out);
2.
3. // Case items have x and z, and sel has to match the exact value for
4. // output to be assigned with the corresponding input
5.
6. always @ (a, b, c, sel) begin
7. case(sel)
8. 2'bxz: out = a;
9. 2'bzx: out = b;
10. 2'bxx: out = c;
11. default: out = 0;
12. endcase
13. end
14.
15. endmodule

Differentiation between the case and if-else


The case statement is different from if-else-if in two ways, such as:

o Expressions given in an if-else block are more general, while in a case


block, a single expression is matched with multiple items.
o The case will provide a definitive result when there are X and Z values
in an expression.

Verilog Parameters
In Verilog, parameters are constants and do not belong to any other data
type such as register or net data types.

A constant expression refers to a constant number or previously defined


parameter. We cannot modify parameter values at runtime, but we can
modify a parameter value using the defparam statement.

The defparam statement can modify parameters only at the compilation


time. Parameter values can be modified using # delay specification with
module instantiation.

In Verilog, there are two methods to override a module parameter value


during a module instantiation.

1. By using the defparam keyword.


2. And module instance parameter value assignment.

After the defparam keyword, the hierarchical path is specified to the


parameter and the parameter's new value. This new value should be a
constant expression. If the right-hand side expression references any
parameters, it should be declared within the module where defparam is
invoked.

The module instance parameter value assignment method seems like an


assignment of delay to gate instance. This method overrides parameters
inside instantiated modules as they appear in the module. Using this format,
parameters cannot be skipped.

Constant expressions can contain previously declared parameters. When


changes are detected on the previously declared parameters, all parameters
that depend on this value are updated automatically.

Consider, a 4-bit adder can be parameterized to accept a value for the


number of bits, and new parameter values can be passed during module
instantiation. So, an N-bit adder converts into a 4-bit, 8-bit or 16-bit adder.
They are like arguments to a function that is passed during a function call.

1. parameter MSB = 7; // MSB is a parameter with the constant value


7
2. parameter REAL = 4.5; // REAL holds the real number
3. parameter FIFO_DEPTH = 256,
4. MAX_WIDTH = 32; // Declares two parameters
5. parameter [7:0] f_const = 2'b3; // 2 bit value is converted into 8 bits; 8'b3

There are two types of parameters, module and specify, and both accept a
range specification. But, they are made as wide as the value to be stored
them, and hence a range specification is not necessary.

Module parameters
It can be used to override parameter definitions within a module and makes
the module have a different set of parameters at compile time. A parameter
can be modified with the defparam statement. It is common to use
uppercase letters in names for the parameter to notice them instantly.

The below module uses parameters to specify the bus width, data width and
the depth of FIFO within the design, and can be overridden with new values
when the module is instantiated or by using defparam statements.

1. module design_ip ( addr, wdata, write, sel, rdata);


2. parameter BUS_WIDTH = 32,
3. DATA_WIDTH = 64,
4. FIFO_DEPTH = 512;
5. input addr;
6. input wdata;
7. input write;
8. input sel;
9. output rdata;
10. wire [BUS_WIDTH-1:0] addr;
11. wire [DATA_WIDTH-1:0] wdata;
12. reg [DATA_WIDTH-1:0] rdata;
13. reg [7:0] fifo [FIFO_DEPTH];
14. endmodule

In the new ANSI style of Verilog port declaration, we may declare parameters
such as:
1. module design_ip
2. #(parameter BUS_WIDTH=32,
3. parameter DATA_WIDTH=64)
4. (input [BUS_WIDTH-1:0] addr,
5. // other port declarations
6. );

Overriding parameters
Parameters can be overridden with new values during module instantiation.
The first part is the module called design_ip by the name d0 where new
parameters are passed within # ( ).

The second part is use a Verilog construct called defparam to set the new
parameter values. The first method is commonly used to pass new
parameters in RTL designs. And the second method is used in testbench
simulations to quickly update the design parameters without having to
reinstantiate the module.

1. module tb;
2. // Module instantiation override
3. design_ip #(BUS_WIDTH = 64, DATA_WIDTH = 128) d0 ( [port list]);
4. // Use of defparam to override
5. defparam d0.FIFO_DEPTH = 128;
6. endmodule

The module counter has two parameters N and DOWN, which is declared to
have a default value of 2 and 0.

N controls the number of bits in the output, effectively controlling the width
of the counter. It is a 2-bit counter by default.

Parameter DOWN controls whether the counter should increment or


decrement. The counter will decrement because the parameter is set to 0.

2-bit up Counter

1. module counter
2. # ( parameter N = 2, parameter DOWN = 0)
3.
4. (input clk, input rstn, input en, output reg [N-1:0] out);
5.
6. always @ (posedge clk) begin
7. if (!rstn) begin
8. out <= 0;
9. end else begin
10. if (en)
11. if (DOWN)
12. out <= out - 1;
13. else
14. out <= out + 1;
15. else
16. out <= out;
17. end
18. end
19. endmodule

The module counter is instantiated with N as 2 even though it is not required


because the default value is anyway 2.

DOWN is not passed during module instantiation. And it takes the default
value of 0 making it an up-counter.

1. module design_top (input clk, input rstn, input en, output [1:0] out);
2. counter #(.N(2)) u0 (.clk(clk), .rstn(rstn), .en(en));
3. endmodule

The default parameters are used to implement the counter where N equals
two, making it a 2-bit counter, and DOWN equals zero, making it an up-
counter. The output from the counter is left unconnected at the top level.

4-bit down Counter

In this case, the module counter is instantiated with N as 4 making it a 4-bit


counter. DOWN is passed a value of 1 during the module instantiation and
hence a down-counter is implemented.

1. module design_top (input clk, input rstn, input en, output [3:0] out);
2. counter #(.N(4), .DOWN(1))
3. u1 (.clk(clk), .rstn(rstn), .en(en));
4. endmodule
Specify Parameters
These parameters are used to provide time and delay values and declared
using the specparam keyword. It is allowed to use both within the specified
block and the main module body.

1. // Use of specify block


2. Specify
3. specparam t_rise = 200, t_fall = 150;
4. specparam clk_to_q = 70, d_to_q = 100;
5. endspecify
6. // Within main module
7. module my_block ( );
8. specparam dhold = 2.0;
9. specparam ddly = 1.5;
10. parameter WIDTH = 32;
11. endmodule

Difference between Specify and Module Parameters

Specify parameter Module parameter

Specify the specparam keyword The module parameter is declared by


declares parameter. parameter.

It can be declared inside a specific It can only be declared within the main
block or within the main module. module.

This parameter may be assigned This may not be assigned specparams.


specparams and parameters.

SDF can be used to override values. Instance declaration parameter values or


defparam can be used to override.

Notes

Here are some important notes for the Verilog parameters, such as:
o If we are using the defparam statement, we must specify a
hierarchical path to the parameter.
o We cannot skip over a parameter in a module instance parameter
value assignment. If we need to do this, use the initial value for a not
overwritten parameter.
o When one parameter depends on the other, then the second will
automatically be updated if we change the first one.

Verilog Timing Control


Timing control statements are required in simulation to advance time. The
time at which procedural statements will get executed shall be specified
using timing controls.

There are the following types of timing controls in Verilog:

o Delay control
o Edge sensitive event control
o Level sensitive event control
o Named events

The delay control is a way of adding a delay between when the simulator
encounters the statement and when it executes.

The event expression allows the statement to be delayed until the


occurrence of some simulation event, which can change of value on a net or
variable or an explicitly named event triggered in another procedure.

Competitive questions on Structures in HindiKeep Watching

Simulation time can be advanced by one of the following methods. Nets and
gates have been modeled to have internal delays, also advance simulation
time.

Delay Control
Delay control is achieved by specifying the waiting time to execution when
the statement is encountered. The symbol # is used to specify the delay.

We can specify the delay based timing control in three ways:

1. Regular delay control: It will be specified on the procedural


assignment left as a non-zero number.
2. Intra- assignment delay control: In this case, delays will be
specified on the assignment operator's right-hand side. The right-hand
side expression will be evaluated at the current time, and the
assignment will occur only after the delay.
3. Zero delay control: Zero delay control statement specifies zero delay
value to the left-hand side of a procedural assignment. This method is
used to ensure the statement is executed at the end of the simulation
time. It means, zero delay control statement is executed after all other
statements in that simulation time are executed.

Event Control
An event controls the execution of a statement or a block of a statement.
Value changes on variables and nets can be used as a synchronization event
to trigger the execution of other procedural statements and is
an implicit event.

The event is based on the direction of change like towards 0, which makes it
a negedge and change towards 1 make it a posedge.

o A negedge is a transition from 1 to X, Z or 0 and from X or Z to 0


o A posedge is a transition from 0 to X, Z or 1 and from X or Z to 1

A transition from the same state to the same state does not suppose to be
an edge. The edge event can be detected only on the LSB of a vector signal
or variable.

If an expression evaluates to the same result, then it cannot be considered


as an event. There are different types of event-based controls.

1. Regular event control: Execution of statement will happen on signal


changes or at positive or negative transitions of signals. For example,
posedge of a clock, and the negedge of reset, etc.

1. @(posedge clock) out = in;


2. @(clock) z = n << 2;

2. Named Event Control: The event keyword can be used to declare a


named event that can be triggered explicitly.

An event cannot hold any data, no time duration, and can be made to occur
at any particular time.

A named event is triggered by the -> operator by prefixing it before the


named event handle. A named event can be waited upon through the @
operator.

1. module tb;
2. event a_event;
3. event b_event[5];
4.
5. initial begin
6. #20 -> a_event;
7.
8. #30;
9. ->a_event;
10.
11. #50 ->a_event;
12. #10 ->b_event[3];
13. end
14.
15. always @ (a_event) $display ("T=%0t [always] a_event is triggered",
$time);
16.
17. initial begin
18. #25;
19. @(a_event) $display ("T=%0t [initial] a_event is triggered", $time);
20.
21. #10 @(b_event[3]) $display ("T=%0t [initial] b_event is triggered", $
time);
22. end
23.
24. endmodule

Named events are used to synchronize two or more concurrently running


processes. Events can be declared as arrays.

For example, the always block and the second initial block are
synchronized by a_event. Events can be declared as arrays such as in the
case of b_event, which is an array of size 5, and index 3 is used for trigger
and wait for purpose.

3. Event OR control: The transitions of signal or event can trigger


statements' execution, as shown below.

The or operator can wait until any one of the listed events is triggered in an
expression. The comma (,) can be used instead of the or operator.

1. always @(clock or in) //Wait for the clock or in to change


2. OR
3. always @(clock, in)

Edge Sensitive Event Control


In Verilog, the @ character specifies an edge-sensitive event control that
blocks until there is a transition in value (an edge) for one of the event's
identifiers.

The edge events are queued and then serviced by @(...) guards, rather than
@(?) being a guard that waits on edge events, blocking until an edge event
happens.

The only edge events that matter to an @(...) guard are those that happen
while it is waiting. Edge events that happen before reaching the guard are
irrelevant to the guard.

Level Sensitive Event Control


A procedural statement's execution can be delayed until a condition
becomes true and accomplished with the wait keyword. And it is a level-
sensitive control.

The wait statement evaluates a condition, and if it is false, the procedural


statements remain blocked until the condition becomes true.

1. module tb;
2. reg [3:0] ctr;
3. reg clk;
4.
5. initial begin
6. {ctr, clk} <= 0;
7. wait (ctr);
8. $display ("T=%0t Counter reached non-zero value 0x%0h", $time, ctr);
9. wait (ctr == 4) $display ("T=%0t Counter reached 0x%0h", $time, ctr);
10. $finish;
11. end
12. always #10 clk = ~clk;
13. always @ (posedge clk)
14. ctr <= ctr + 1;
15. endmodule

After completion the execution, it produces the following output:

1. ncsim> run
2. T=10 Counter reached non-zero value 0x1
3. T=70 Counter reached 0x4
4. T=90 Counter reached 0x5
5. T=170 Counter reached 0x9
6. Simulation complete via $finish(1) at time 170 NS + 1

Implicit Event Expression List


The event expression list or sensitivity list is often a common cause for many
functional errors in the RTL because the user may forget to update the
sensitivity list after introducing a new signal in the procedural block.

1. module tb;
2. reg a, b, c, d;
3. reg x, y;
4.
5. // signals inside () after @ operator
6. // it is a, b, c or d
7.
8. always @ (a, b, c, d) begin
9. x = a | b;
10. y = c ^ d;
11. end
12.
13. initial begin
14. $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b x=%0b y=
%0b", $time, a, b, c, d, x, y);
15. {a, b, c, d} = 0;
16.
17. #10 {a, b, c, d} = $random;
18. #10 {a, b, c, d} = $random;
19. #10 {a, b, c, d} = $random;
20. end
21.
22. endmodule

Now execute the above code and we will get the following output:

ncsim> run
T=0 a=0 b=0 c=0 d=0 x=0 y=0
T=10 a=0 b=1 c=0 d=0 x=1 y=0
T=20 a=0 b=0 c=0 d=1 x=0 y=1
T=30 a=1 b=0 c=0 d=1 x=1 y=1
ncsim: *W,RNQUIE: Simulation is complete.

If the user decides to add new signal e and capture the inverse into z, then
special care must be taken to add e also into the sensitivity list.

1. module tb;
2. reg a, b, c, d, e;
3. reg x, y, z;
4.
5. // Add "e" also into sensitivity list
6. always @ (a, b, c, d, e) begin
7. x = a | b;
8. y = c ^ d;
9. z = ~e;
10. end
11.
12. initial begin
13. $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y
=%0b z=%0b",
14. $time, a, b, c, d, e, x, y, z);
15. {a, b, c, d, e} = 0;
16.
17. #10 {a, b, c, d, e} <= $random;
18. #10 {a, b, c, d, e} <= $random;
19. #10 {a, b, c, d, e} <= $random;
20. end
21.
22. endmodule

And the output is given below:

ncsim> run
T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1
T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1
T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0
T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0
ncsim: *W,RNQUIE: Simulation is complete.

Verilog allows the sensitivity list to be replaced by * which is a convenient


shorthand that eliminates these problems by adding all nets and variables
read by the statement as shown in the below code.
1. module tb;
2. reg a, b, c, d, e;
3. reg x, y, z;
4.
5. // Use @* or @(*)
6. always @ * begin
7. x = a | b;
8. y = c ^ d;
9. z = ~e;
10. end
11.
12. initial begin
13. $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y
=%0b z=%0b",
14. $time, a, b, c, d, e, x, y, z);
15. {a, b, c, d, e} = 0;
16.
17. #10 {a, b, c, d, e} <= $random;
18. #10 {a, b, c, d, e} <= $random;
19. #10 {a, b, c, d, e} <= $random;
20. end
21.
22. endmodule

The output looks as below:

ncsim> run
T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1
T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1
T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0
T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0
ncsim: *W,RNQUIE: Simulation is complete.

Verilog Inter and Intra Delay


Verilog delay statements can have delays specified either on the left-hand
side of the assignment operator's right-hand side.

Inter Assignment Delays


An inter-assignment delay statement has delay value on the left-hand side of
the assignment operator.

Inter assignment are those delay statements where the execution of the
entire statement or assignment got delayed.

In Verilog, Inter assignment delays often correspond to the inertial delay or


the VHDL's regular delay statements.

1. // Delay is specified on the left side


2. #<delay> <LHS> = <RHS>
It indicates that the statement itself is executed after the delay expires, and
is the most commonly used form of delay control.

Example

1. module vd;
2. reg a, b, c, q;
3.
4. initial begin
5. $monitor("[%0t] a=%0b b=%0b c=%0b q=%0b", $time, a, b, c, q);
6.
7. // Now initialize all signals to 0 at time 0
8. a <= 0;
9. b <= 0;
10. c <= 0;
11. q <= 0;
12.
13. // Inter-assignment delay. Wait for #5 time units
14. // and then assign a and c to 1. Note that 'a' and 'c'
15. // gets updated at the end of current timestep
16. #5 a <= 1;
17. c <= 1;
18.
19. // Inter-assignment delay. Wait for #5 time units
20. // and then assign 'q' with whatever value RHS gets
21. // evaluated to
22. #5 q <= a & b | c;
23. #20;
24. end
25. endmodule

Here, q becomes 1 at time 10 units because the statement gets evaluated at


10 time units and RHS which is a combination of a, b and c evaluates to 1.
After completion the execution, it gives the following output.

xcelium> run
[0] a=0 b=0 c=0 q=0
[5] a=1 b=0 c=1 q=0
[10] a=1 b=0 c=1 q=1
xmsim: *W,RNQUIE: Simulation is complete.

Intra Assignment Delays


Intra assignment delay indicates that the statement itself is
executed after the delay expires, and it is the most commonly used form of
delay control.

They can be used with blocking and non-blocking assignments. If a


statement with intra-assignment timing controls is encountered during
simulation, then the expression will be evaluated, and its value will be
stored.
Then, the statement's execution will be suspended until the time specified by
the delay control expires. Changes in the expression value up to the time of
the event will be ignored.

1. // Delay is specified on the right side


2. <LHS> = #<delay> <RHS>

An intra-assignment delay is declared to the right-hand side of the


assignment operator. This indicates that the statement is evaluated, and the
values of all signals on RHS are captured first.

Example

1. module vd;
2. reg a, b, c, q;
3. initial begin
4. $monitor("[%0t] a=%0b b=%0b c=%0b q=%0b", $time, a, b, c, q);
5. // Initialize all signals to 0 at time 0
6. a <= 0;
7. b <= 0;
8. c <= 0;
9. q <= 0;
10. // Inter-assignment delay: Wait for #5 time units
11. // and then assign a and c to 1. Note that 'a' and 'c'
12. // gets updated at the end of current timestep
13. #5 a <= 1;
14. c <= 1;
15. // Intra-assignment delay: First execute the statement
16. // then wait for 5 time units and then assign the evaluated
17. // value to q
18. q <= #5 a & b | c;
19. #20;
20. end
21. endmodule

The above code gives the below output:

xcelium> run
[0] a=0 b=0 c=0 q=0
[5] a=1 b=0 c=1 q=0
xmsim: *W,RNQUIE: Simulation is complete.

At 5 time units, a and c are assigned using non-blocking statements. And the
behavior of non-blocking statements is evaluated but gets assigned to the
variable only at the end of the time step.

So the value of a and c is evaluated to 1 but not assigned when the next
non-blocking statement q is executed. So when RHS of q is evaluated, a and
c still has an old value of 0, and hence $monitor does not detect a change to
display the statement.

To observe the change, let us change assignment statements to a and c


from non-blocking to blocking.
1. // Non-blocking changed to blocking
2. // rest of the code remains the same
3. #5 a = 1;
4. c = 1;
5. q <= #5 a & b | c;

And the output looks like as:

xcelium> run
[0] a=0 b=0 c=0 q=0
[5] a=1 b=0 c=1 q=0
[10] a=1 b=0 c=1 q=1
xmsim: *W,RNQUIE: Simulation is complete.

Verilog Gate Delay


Verilog gate delays specify how values propagate through nets or gates. The
gate delay declaration specifies a time needed to propagate a signal change
from the gate input to its output.

The gate delay declaration can be used in gate instantiations. The delays can
also be used for delay control in procedural statements.

Digital elements are binary entities and only hold either of the two values, 0
and 1. The transition from 0 to 1 and 1 to 0 has a transitional delay, and
therefor each gate element propagates the value from input to its output.

For example, a two-input AND gate has to switch the output to 1 if both
inputs become 1 and back to 0 when inputs become 0.

The net delay declaration specifies a time needed to propagate values from
drivers through the net. It can be used in continuous assignments and net
declarations.

This gate and pin to pin delays can be specified in Verilog when instantiating
logic primitives.

Rise, Fall, and Turn-Off Delays


The delays declaration can contain up to three values, such as rise,
fall, and turn-off delays.

o The time taken for the output of a gate to change from some value to 1
is called a rise delay.
o The time taken for the output of a gate to change form some value to 0
is called a fall delay.
o The time taken for the output of a gate to change from some value to
high impedance is called turn-off delay.

If only one delay value is specified, then it is used for all signal changes. The
default delay is zero.

If two delays are specified, then the first delay specifies the rise delay, and
the second delay specifies the fall delay.

If the signal changes to high-impedance or unknown, then the smaller value


will be used.
If three values are given, then the first value specifies the rise delay, the
second specifies the fall delay, and the third specifies the turn-off delay. If
the signal changes to an unknown value, then the smallest of these three
values will be used.

These delays apply to any signal as they all can rise or fall anytime in real
circuits and are not restricted to only outputs of gates. There are three ways
to represent gate delays.

1. One delay format


2. Two delay format
3. Three delay format

The two delay format can be applied to most primitives whose outputs do
not transition to high impedance.

A three delay format cannot be applied to an AND gate because the output
will not go to Z for any input combination.

1. // Single delay specified - used for all three types of transition delays
2. or #(<delay>) o1 (out, a, b);
3.
4. // Two delays specified - used for Rise and Fall transitions
5. or #(<rise>, <fall>) o1 (out, a, b);
6. // Three delays specified - used for Rise, Fall and Turn-off transitions
7. or #(<rise>, <fall>, <turn_off>) o1 (out, a, b);

If only a single delay is specified, all three types of delays will use the same
given value.

If there are two delays specified, the first one represents the rise, and the
second one represents the fall delay.
If there are three delays specified, they represent rise, fall, and turn-
off delays, respectively.

1. One Delay Format

1. module des (input a, b, output out1, out2);


2. // AND gate has 2 time unit gate delay
3. and #(2) o1 (out1, a, b);
4. // BUFIF0 gate has 3 time unit gate delay
5. bufif0 #(3) b1 (out2, a, b);
6. endmodule

Now, See that the output of AND gates change 2 time units after one of its
inputs change.

1. module tb;
2. reg a, b;
3. wire out1, out2;
4. des d0 (.out1(out1), .out2(out2), .a(a), .b(b));
5. initial begin
6. {a, b} <= 0;
7. $monitor ("T=%0t a=%0b b=%0b and=%0b bufif0=%0b", $time, a, b, out
1, out2);
8. #10 a <= 1;
9. #10 b <= 1;
10. #10 a <= 0;
11. #10 b <= 0;
12. end
13. endmodule

For example, b becomes 1 while a is already 1 at T=20. But the output


becomes 1 only at T=22. Similarly, a goes back to zero at T=30, and the
output gets the new value at T=32.

Gate delay is specified as 3-time units for BUFIF0. If b changes from 0 to 1


while a is already at 1, then the output takes 3-time units to get updated to Z
and finally does so at T=23.

Output

ncsim> run
T=0 a=0 b=0 and=x bufif0=x
T=2 a=0 b=0 and=0 bufif0=x
T=3 a=0 b=0 and=0 bufif0=0
T=10 a=1 b=0 and=0 bufif0=0
T=13 a=1 b=0 and=0 bufif0=1
T=20 a=1 b=1 and=0 bufif0=1
T=22 a=1 b=1 and=1 bufif0=1
T=23 a=1 b=1 and=1 bufif0=z
T=30 a=0 b=1 and=1 bufif0=z
T=32 a=0 b=1 and=0 bufif0=z
T=40 a=0 b=0 and=0 bufif0=z
T=43 a=0 b=0 and=0 bufif0=0
ncsim: *W,RNQUIE: Simulation is complete.

2. Two Delay Format


Let's apply the same testbench shown above to a different Verilog model
shown below where rise and fall delays are explicitly mentioned.

1. module des (input a, b, output out1, out2);


2. and #(2, 3) o1 (out1, a, b);
3. bufif0 #(4, 5) b1 (out2, a, b);
4. endmodule

And it produces the following output, such as:

1. ncsim> run
2. T=0 a=0 b=0 and=x bufif0=x
3. T=3 a=0 b=0 and=0 bufif0=x
4. T=5 a=0 b=0 and=0 bufif0=0
5. T=10 a=1 b=0 and=0 bufif0=0
6. T=14 a=1 b=0 and=0 bufif0=1
7. T=20 a=1 b=1 and=0 bufif0=1
8. T=22 a=1 b=1 and=1 bufif0=1
9. T=24 a=1 b=1 and=1 bufif0=z
10. T=30 a=0 b=1 and=1 bufif0=z
11. T=33 a=0 b=1 and=0 bufif0=z
12. T=40 a=0 b=0 and=0 bufif0=z
13. T=45 a=0 b=0 and=0 bufif0=0
14. ncsim: *W,RNQUIE: Simulation is complete.

3. Three Delay Format

1. module des ( input a, b, output out1, out2);


2. and #(2, 3) o1 (out1, a, b);
3. bufif0 #(5, 6, 7) b1 (out2, a, b);
4. endmodule

The three delay format code gives the following output:

1. ncsim> run
2. T=0 a=0 b=0 and=x bufif0=x
3. T=3 a=0 b=0 and=0 bufif0=x
4. T=6 a=0 b=0 and=0 bufif0=0
5. T=10 a=1 b=0 and=0 bufif0=0
6. T=15 a=1 b=0 and=0 bufif0=1
7. T=20 a=1 b=1 and=0 bufif0=1
8. T=22 a=1 b=1 and=1 bufif0=1
9. T=27 a=1 b=1 and=1 bufif0=z
10. T=30 a=0 b=1 and=1 bufif0=z
11. T=33 a=0 b=1 and=0 bufif0=z
12. T=40 a=0 b=0 and=0 bufif0=z
13. T=46 a=0 b=0 and=0 bufif0=0
14. ncsim: *W,RNQUIE: Simulation is complete.
Min, Typ, and Max Delays
Delays are neither the same in different parts of the fabricated chip nor the
same for different temperatures and other variations. So Verilog also
provides an extra level of control for each of the delay types mentioned
above.

Every digital gate and transistor cell has a minimum, typical, and maximum
delay specified based on process node and is typically provided by libraries
from fabrication foundry.

For rise, fall, and turn-off delays, the three values min, typ, and max can be
specified and stand for minimum, typical and maximum delays.

This is another level of delay control in Verilog. Only one of the min, typ, and
max values can be used in the entire simulation run.

It is specified at the start of the simulation and depends on the simulator


used. The typ is the default value.

The min value is the minimum delay value that the gate is expected to have.

The typ value is the typical delay value that the gate is expected to have.

The max value is the maximum delay value that the gate is expected to
have.

1. module des (input a, b, output out1, out2);


2. and #(2:3:4, 3:4:5) o1 (out1, a, b);
3. bufif0 #(5:6:7, 6:7:8, 7:8:9) b1 (out2, a, b);
4. endmodule

The output looks like:

1. ncsim> run
2. T=0 a=0 b=0 and=x bufif0=x
3. T=4 a=0 b=0 and=0 bufif0=x
4. T=7 a=0 b=0 and=0 bufif0=0
5. T=10 a=1 b=0 and=0 bufif0=0
6. T=16 a=1 b=0 and=0 bufif0=1
7. T=20 a=1 b=1 and=0 bufif0=1
8. T=23 a=1 b=1 and=1 bufif0=1
9. T=28 a=1 b=1 and=1 bufif0=z
10. T=30 a=0 b=1 and=1 bufif0=z
11. T=34 a=0 b=1 and=0 bufif0=z
12. T=40 a=0 b=0 and=0 bufif0=z
13. T=47 a=0 b=0 and=0 bufif0=0
14. ncsim: *W,RNQUIE: Simulation is complete.

You might also like