File - Sidhartha Sankar Rout

advertisement
Summer Training
(03rd Aug, 2011 to 23rd Aug, 2011)
FPGA Programming using
Verilog HDL Language
Training Manual
1
Training Coordinator
Er. Sidhartha Sankar Rout
Lecturer,
ECE Department,
BIT, Bhubaneswar
2
Dedicated to,
~~~Maa~~~
3
Contents:
i)
ii)
iii)
iv)
v)
vi)
vii)
viii)
Acknowledgement……………………………. p.5
Course Objective………………………………. p.6
ISE 10.1………………………………………... p.07 to p.22
Verilog Programming Language………............. p.23 to p.61
Function and Task………………………........... p.61 to p.64
Modeling Finite State Machine……………….. p.64 to p.67
Writing Test Benches…………………………. p.67 to p.73
Basics of FPGA……………………………….. p.74 to p.75
4
Acknowledgement
We convey our heartiest gratitude to Prof. Rabi N Mahapatra (Chairman, BIT) for his words of
inspiration. He willingly extracted time from his busy schedule while helping us technically by
providing esteemed tutorials (of his own that he uses to teach undergraduates of Texas A&M
Univ., USA) regarding FPGA programming.
We are highly thankful to Mr. Arun Prusty (H.O.D, ECE Dept, BIT) and the system support
team for their full time support in configuring ISE and ADEPT to all the systems. The successful
design of this training manual would not have happened without the full time endeavor of Mr.
Prabhas Nanda (Placement & Training Officer).
We tried our level best in designing an error free manual for the trainees. We welcome
everyone’s feedback regarding improvement of this manual.
Sidhartha Sankar Rout
Lecturer,
ECE Dept, BIT.
Dharanidhar Dang
Lecturer,
ECE Dept, BIT.
5
COURSE OBJECTIVE
 Create and implement designs by using the ISE software design
environment and Basys-2 Spartan3E FPGA board.
 Verilog code for synthesis
 Functions and Tasks
 Creating Finite State Machine (FSM) by using Verilog
 Verilog test fixtures for simulation
 Introduction to FPGA
 Project Work
6
1. ISE 10.1
Before you start using the FPGA board you will have to be familiar with ISE 10.1.It would lead
you to create, verify and implement logic on FPGA board.
This tutorial contains the following sections:
• “Getting Started”
• “Create a New Project”
• “Create an HDL Source”
• “Design Simulation”
• “Create Timing Constraints”
• “Implement Design and Verify Constraints”
• “Reimplement Design and Verify Pin Locations”
• “Download Design to the Spartan™-3 Demo Board”
For an in-depth explanation of the ISE design tools, see the ISE In-Depth Tutorial on the
Xilinx® web site at: http://www.xilinx.com/support/techsup/tutorials/
1.1 GETTING STARTED
Starting the ISE Software
To start ISE, double-click the desktop icon,
or start ISE from the Start menu by selecting:
Start → All Programs →Xilinx ISE Design Suite 10.1→ISE→ Project Navigator
Accessing Help
At any time you can access online help for additional information about the ISE software and
related tools. To open Help, do either of the following:
 Press F1 to view Help for the specific tool or function that you have selected or
highlighted.
 Launch the ISE Help Contents from the Help menu. It contains information about
creating and maintaining your complete design flow in ISE.
7
1.2 CREATE A NEW PROJECT
Create a new ISE project which will target the FPGA device.
To create a new project:
1. Select File > New Project... The New Project Wizard appears.
2. Type tutorial (any Project name) in the Project Name field.
3. Enter or browse to a location (directory path) for the new project. A tutorial
subdirectory is created automatically.
4. Verify that HDL is selected from the Top-Level Source Type list.
5. Click Next to move to the device properties page.
6. Fill in the properties in the table as shown below:
 Product Category: All
 Family: Spartan3E
 Device: XC3S100E
 Package: CP132
 Speed Grade: -5
 Top-Level Source Type: HDL
 Synthesis Tool: XST (VHDL/Verilog)
 Simulator: ISE Simulator (VHDL/Verilog)
 Preferred Language: Verilog
 Verify that Enable Enhanced Design Summary is selected.
Leave the default values in the remaining fields.
When the table is complete, your project properties will look like the following:
7. Click Next to proceed to the Create New Source window in the New Project Wizard.
At the end of the next section, your new project will be complete.
8
1.3 CREATE AN HDL SOURCE
In this section, you will create the top-level HDL file for your design. Determine the language as
Verilog to use for the tutorial.
 Create the top-level Verilog source file for the project as follows:
 Click New Source in the New Project dialog box.
 Select Verilog Module as the source type in the New Source dialog box.
 Type in the file name counter.
 Verify that the Add to Project checkbox is selected.
 Click Next.
 Declare the ports for the counter design by filling in the port information as shown
below:


Click Next, then Finish in the New Source Information dialog box to complete the new
source file template.
Click Next, then Next, then Finish.
The source file containing the counter module displays in the Workspace, and the counter
displays in the Sources tab, as shown below:
9
Using Language Templates (Verilog)
The next step in creating the new source is to add the behavioral description for counter. Use a
simple counter code example from the ISE Language Templates and customize it for the counter
design.
 Place the cursor on the line below the output [3:0] COUNT_OUT; statement. Open the
Language Templates by selecting Edit → Language Templates…
 You can tile the Language Templates and the counter file by selecting Window → Tile
Vertically to make them both visible.
 Using the “+” symbol, browse to the following code example:
 Verilog → Synthesis Constructs → Coding Examples → Counters → Binary →
Up/Down Counters → Simple Counter
 With Simple Counter selected, select Edit → Use in File. This step copies the template
into the counter source file.
 Close the Language Templates.

Final Editing of the Verilog Source
 To declare and initialize the register that stores the counter value, modify the declaration
statement in the first line of the template as follows:
 replace “reg [<upper>:0] <reg_name>;” with “reg [3:0] count_int = 0;”
 Customize the template for the counter design by replacing the port and signal name.
10
o replace all occurrences of <clock> with CLOCK
o replace all occurrences of <up_down> with DIRECTION
o replace all occurrences of <reg_name> with count_int
 Add the following line just above the endmodule statement to assign the register value to
the output port:
assign COUNT_OUT = count_int;
 Save the file by selecting File → Save.
When you are finished, the code for the counter will look like the following:
module counter(CLOCK, DIRECTION, COUNT_OUT);
input CLOCK;
input DIRECTION;
output [3:0] COUNT_OUT;
);
reg [3:0] count_int = 0;
always @(posedge CLOCK)
if (DIRECTION)
count_int <= count_int + 1;
else
count_int <= count_int - 1;
assign COUNT_OUT = count_int;
endmodule
Checking the Syntax of the New Counter Module
 When the source files are complete, check the syntax of the design to find errors.
 Verify that Implementation is selected from the drop-down list in the Sources window.
 Select the counter design source in the Sources window to display the related processes
in the Processes window.
 Click the “+” next to the Synthesize-XST process to expand the process group.
 Double-click the Check Syntax process.
 You must correct any errors found in your source files. You can check for errors in the
Console tab of the Transcript window. If you continue without valid syntax, you will not
be able to simulate or synthesize your design.
 Close the HDL file.
1.4 DESIGN SIMULATION
Create a test bench waveform containing input stimulus where you can verify the functionality of
the counter module. The test bench waveform is a graphical view of a test bench.
Create the test bench waveform as follows:
 Select the counter HDL file in the Sources window.
 Create a new test bench source by selecting Project → New Source.
 In the New Source Wizard, select Test Bench Waveform as the source type, and type
counter_tbw in the File Name field.
 Click Next.
 The Associated Source page shows that you are associating the test bench waveform with
the source file counter. Click Next.
11
 The Summary page shows that the source will be added to the project, and it displays the
source directory, type, and name. Click Finish.
 You need to set the clock frequency, setup time and output delay times in the Initialize
Timing dialog box before the test bench waveform editing window opens.
The requirements for this design are the following:
 The counter must operate correctly with an input clock frequency = 25 MHz.
 The DIRECTION input will be valid 10 ns before the rising edge of CLOCK.
 The output (COUNT_OUT) must be valid 10 ns after the rising edge of CLOCK.
The design requirements correspond with the values below.
Fill in the fields in the Initialize Timing dialog box with the following information:
 Clock High Time: 20 ns.
 Clock Low Time: 20 ns.
 Input Setup Time: 10 ns.
 Output Valid Delay: 10 ns.
 Offset: 0 ns.
 Global Signals: GSR (FPGA)
Note: When GSR (FPGA) is enabled, 100 ns. is added to the Offset value automatically.
 Initial Length of Test Bench: 1500 ns.
Leave the default values in the remaining fields.
 Click Finish to complete the timing initialization.
 The blue shaded areas that precede the rising edge of the CLOCK correspond to the Input
Setup Time in the Initialize Timing dialog box. Toggle the DIRECTION port to define
the input stimulus for the counter design as follows:
12
o Click on the blue cell at approximately the 300 ns to assert DIRECTION high so
that the counter will count up.
o Click on the blue cell at approximately the 900 ns to assert DIRECTION low so
that the counter will count down.
 Save the waveform.
 In the Sources window, select the Behavioral Simulation view to see that the test bench
waveform file is automatically added to your project.
 Close the test bench waveform.
Simulating Design Functionality
Verify that the counter design functions as you expect by performing behavior simulation as
follows:
 Verify that Behavioral Simulation and counter_tbw are selected in the Sources
window.
 In the Processes tab, click the “+” to expand the Xilinx ISE Simulator process and
double-click the Simulate Behavioral Model process.
The ISE Simulator opens and runs the simulation to the end of the test bench.
 To view your simulation results, select the Simulation tab and zoom in on the transitions.
The simulation waveform results will look like the following:
13


Verify that the counter is counting up and down as expected.
Close the simulation view. If you are prompted with the following message, “You have
an active simulation open. Are you sure you want to close it?” Click Yes to continue.
You have now completed simulation of your design using the ISE Simulator.
1.5 CREATE TIMING CONSTRAINTS
Specify the timing between the FPGA and its surrounding logic as well as the frequency the
design must operate at internal to the FPGA. The timing is specified by entering constraints that
guide the placement and routing of the design. It is recommended that you enter global
constraints. The clock period constraint specifies the clock frequency at which your design must
operate inside the FPGA. The offset constraints specify when to expect valid data at the FPGA
inputs and when valid data will be available at the FPGA outputs.
Entering Timing Constraints
To constrain the design do the following:
 Select Implementation from the drop-down list in the Sources window.
 Select the counter HDL source file.
 Click the “+” sign next to the User Constraints processes group, and double-click the
Create Timing Constraints process.
ISE runs the Synthesis and Translate steps and automatically creates a User Constraints File
(UCF). You will be prompted with the following message:
 Click Yes to add the UCF file to your project.
 The counter.ucf file is added to your project and is visible in the Sources window.
 The Xilinx Constraints Editor opens automatically.
Note: You can also create a UCF file for your project by selecting Project → Create New
Source.
14
In the Timing Constraints dialog, enter the following in the Period, Pad to Setup, and CLock to
Pad fields:
♦ Period: 40
♦ Pade to Setup: 10
♦ Clock to Pad: 10
Press Enter.
After the information has been entered, the dialog should look like what is shown below.
Select Timing Constraints under Constraint Type in the Timing Constraints tab and the newly
created timing constraints are displayed as follows:
15
Save the timing constraints. If you are prompted to rerun the TRANSLATE or XST step, click
OK to continue.
9. Close the Constraints Editor.
1.6 IMPLEMENT DESIGN AND VERIFY CONSTRAINTS
Implement the design and verify that it meets the timing constraints specified in the previous
section.
Implementing the Design
 Select the counter source file in the Sources window.
 Open the Design Summary by double-clicking the View Design Summary process in the
Processes tab.
 Double-click the Implement Design process in the Processes tab.
 Notice that after Implementation is complete, the Implementation processes have a green
check mark next to them indicating that they completed successfully without Errors or
Warnings.
16
Post Implementation Design Summary
 Locate the Performance Summary table near the bottom of the Design Summary.
 Click the All Constraints Met link in the Timing Constraints field to view the Timing
Constraints report. Verify that the design meets the specified timing requirements.
All Constraints Met Report
 Close the Design Summary.
Assigning Pin Location Constraints
Specify the pin locations for the ports of the design so that they are connected correctly on the
Spartan-3E Basys-2 board.
To constrain the design ports to package pins, do the following:
 Verify that counter is selected in the Sources window.
 Double-click the Floorplan Area/IO/Logic - Post Synthesis process found in the User
Constraints process group. The Xilinx Pinout and Area Constraints Editor (PACE) opens.
 Select the Package View tab.
 In the Design Object List window, enter a pin location for each pin in the Loc column
using the following information:
♦ CLOCK input port connects to FPGA pin C8 (RCCLK signal on board)
♦ COUNT_OUT<0> output port connects to FPGA pin M5 (LD0 signal on board)
♦ COUNT_OUT<1> output port connects to FPGA pin M11 (LD1 signal on board)
♦ COUNT_OUT<2> output port connects to FPGA pin P7 (LD2 signal on board)
♦ COUNT_OUT<3> output port connects to FPGA pin P6 (LD3 signal on board)
♦ DIRECTION input port connects to FPGA pin P11 (SW0 signal on board)
17

Select File → Save. You are prompted to select the bus delimiter type based on the
synthesis tool you are using. Select XST Default <> and click OK.
 Close PACE.
Notice that the Implement Design processes have an orange question mark next to them,
indicating they are out-of-date with one or more of the design files. This is because the UCF file
has been modified.
1.7 REIMPLEMENT DESIGN AND VERIFY PIN LOCATIONS
Reimplement the design and verify that the ports of the counter design are routed to the package
pins specified in the previous section. First, review the Pinout Report from the previous
implementation by doing the following:

Open the Design Summary by double-clicking the View Design Summary process in the
Processes window.

Select the Pinout Report and select the Signal Name column header to sort the signal
names. Notice the Pin Numbers assigned to the design ports in the absence of location
constraints.
18



Reimplement the design by double-clicking the Implement Design process.
Select the Pinout Report again and select the Signal Name column header to sort the
signal names.
Verify that signals are now being routed to the correct package pins.

Close the Design Summary.
19
1.8 DOWNLOAD DESIGN TO THE SPARTA-3E BASYS-2 BOARD
This is the last step in the design verification process. This section provides simple instructions
for downloading the counter design to the Spartan-3E Basys-2 board.
 Select Implementation from the drop-down list in the Sources window.
 Select counter in the Sources window.
 In the Process window, double-click the Configure Target Device process.
 Click OK in the following warning.

Click finish on the iMPACT window
20




Now connect the Basys-2 board to the PC through the USB cable.
Power on the board through the power sliding switch.
Then open the Adept software.
Then click “Initialize Chain”.
To program a device:
 Click the Browse button next to the device icon in the window. An Open dialog box
appears.
 Select the appropriate configuration file (.bit file) in the Open dialog box and click the
Open button. Adept displays a history of configuration files in the drop-down list box
next to the device.
 Click the Program button or right-click on the device icon and select Program Device.
21
The Test Tab
Adept can run simple diagnostic tests on boards so that you can verify that the board is
functioning properly. To begin a test of the board, click the Start Test button. This automatically
loads a diagnostic test configuration to the FPGA. The red button changes to green. You see
"PASS" and "128" appear alternately on the 7-seg display. Play with the 8 switches and notice
that the 8 singular LEDs on the board follow the switches. The test can be stopped any time by
pressing the Stop Test button, switching to a different tab, or changing the connected device.
22
VERILOG
PROGRAMMING
LANGUAGE
23
1. INTRODUCTION
The complexity of hardware design has grown exponentially in the last decade. The exponential
growth is fueled by new advances in design technology as well as the advances in fabrication
technology. The usage of Hardware Description Language (HDL) to model, simulate,
synthesize, analyze, and test the design has been a cornerstone of this rapid development.
Verilog HDL is a Hardware Description Language. A Hardware Description Language is a
language used to describe a digital system: for example, a network switches, a microprocessor or
a memory or a simple flip-flop. HDL allows the design to be simulated earlier in the design cycle
in order to correct errors or experiment with different architectures. Designs described in HDL
are technology-independent, easy to design and debug, and are usually more readable than
schematics, particularly for large circuits. One may describe a digital system at several levels.
For example, an HDL might describe the layout of the wires, resistors and transistors on an
Integrated Circuit (IC) chip, i.e., the switch level. Or, it might describe the logical gates and
flip flops in a digital system, i.e., the gate level. An even higher level describes the registers and
the transfers of vectors of information between registers. This is called the Register Transfer
Level (RTL). Verilog supports all of these levels.
1.1 WHAT IS VERILOG?
Verilog is one of the two major Hardware Description Languages (HDL) used by hardware
designers in industry and academia. VHDL is the other one. The industry is currently split on
which is better. Many feel that Verilog is easier to learn. Verilog is very C-like and liked by
electrical and computer engineers as most learn the C language in college. VHDL is very Adalike and most engineers have no experience with Ada.
Verilog was introduced in 1985 by Gateway Design System Corporation, now a part of Cadence
Design Systems. Until May, 1990, with the formation of Open Verilog International (OVI),
Verilog HDL was a proprietary language of Cadence. Cadence was motivated to open the
language to the Public Domain with the expectation that the market for Verilog HDL-related
software products would grow more rapidly with broader acceptance of the language.
Verilog HDL allows a hardware designer to describe designs at a high level of abstraction such
as at the architectural or behavioral level as well as the lower implementation levels (i.e., gate
and switch levels) leading to Very Large Scale Integration (VLSI) Integrated Circuits (IC)
layouts and chip fabrication. A primary use of HDLs is the simulation of designs before the
designer must commit to fabrication.
1.2 WHY USE VERILOG HDL?
Digital systems are highly complex. At their most detailed level, they may consist of millions of
elements, i.e., transistors or logic gates. Therefore, for large digital systems, gate-level design is
dead. For many decades, logic schematics served as the bridge language of logic design, but not
anymore. Today, hardware complexity has grown to such a degree that a schematic with logic
gates is almost useless as it shows only a web of connectivity and not the functionality of design.
24
Since the 1970s, Computer engineers and electrical engineers have moved toward hardware
description languages (HDLs). The most prominent modern HDLs in industry are Verilog and
VHDL. Verilog is the top HDL used by over 10,000 designers at such hardware vendors as Sun
Microsystems, Apple Computer and Motorola. The Verilog language provides the digital
designer with a means of describing a digital system at a wide range of levels of abstraction and
at the same time provides access to computer-aided design tools to aid in the design process at
these levels. Verilog allows hardware designers to express their design with behavioral
constructs, deterring the details of implementation to a later stage of design. An abstract
representation helps the designer explore architectural alternatives through simulations and to
detect design bottlenecks before detailed design begins. Computer-aided-design tools, i.e.,
programs, exist which will “compile” programs in the Verilog notation to the level of circuits
consisting of logic gates and flip flops. One could then go to the lab and wire up the logical
circuits and have a functioning system. And, other tools can “compile” programs in Verilog
notation to a description of the integrated circuit masks for very large scale integration (VLSI).
Verilog also allows the designer to specific designs at the logical gate level using gate
constructs and the transistor level using switch constructs. Verilog allows engineers to
optimize the logical circuits and VLSI layouts to maximize speed and minimize area of the VLSI
chip.
1.3 HIERARCHICAL MODELING CONCEPTS
Before we discuss the details of the Verilog language, we must first understand basic hierarchical
modeling concepts in digital design.
Design Methodologies
There are two basic types of digital design methodologies: a top-down design methodology and a
bottom-up design methodology. In a top-down design methodology, we define the top-level
block and identify the sub-blocks necessary to build the top-level block. We further subdivide
the sub-blocks until we come to leaf cells, which are the cells that cannot further be divided.
Figure 1-1 shows the top-down design process.
Figure 1-1. Top-down Design Methodology
25
In a bottom-up design methodology, we first identify the building blocks that are available to us.
We build bigger cells, using these building blocks. These cells are then used for higher-level
blocks until we build the top-level block in the design.
Figure 1-2. Bottom-up Design Methodology
Typically, a combination of top-down and bottom-up flows is used. Design architects define the
specifications of the top-level block. Logic designers decide how the design should be structured
by breaking up the functionality into blocks and sub-blocks. At the same time, circuit designers
are designing optimized circuits for leaf-level cells. They build higher-level cells by using these
leaf cells. The flow meets at an intermediate point where the switch-level circuit designers have
created a library of leaf cells by using switches, and the logic level designers have designed from
top-down until all modules are defined in terms of leaf cells.
26
2. VERILOG FOR DESCRIPTION
2.1 MODULE DEFINITION
Verilog provides the concept of a module. A module is the basic building block in Verilog. A
module can be an element or a collection of lower-level design blocks. Typically, elements are
grouped into modules to provide common functionality that is used at many places in the design.
A module provides the necessary functionality to the higher-level block through its port interface
(inputs and outputs), but hides the internal implementation. This allows the designer to modify
module internals without affecting the rest of the design. Modules communicate with the outside
world through input, output and bi-directional (inout) ports. In Verilog, a module is declared by
the keyword module. A corresponding keyword endmodule must appear at the end of the
module definition. Each module must have a module_name, which is the identifier for the
module, and a port list, which describes the input and output terminals of the module. Design
functionality is implemented inside module, after port declaration. The design functionality
implementation part is represented as “body” here.
Syntax:
27
Example:
module MAT (enable, data, all_zero, result, status);
input
input
enable;
[3:0] data;
output
// scalar input
// vector input
all_zero; // scalar output
output [3:0] result;
// vector output
Inout [1:0]
// bi-directional port
status
……
endmodule
To make code easy to read, use self-explanatory port names. For the purpose of conciseness, use
short port names. In vector port declaration, MSB can be smaller index.
For e.g. output [0:3] result (result[0] is the MSB)
2.1.1 THE MODULE NAME
The module name, formally called an identifier serves documentation purpose. Based on this in
your module names you should use such noun phrases that best describes what the system is
doing. Each identifier in Verilog, including module names must follow these rules:





It can be composed of letters, digits, dollar sign ($), and underscore characters (_) only.
It must start with a letter or underscore.
No spaces are allowed inside an identifier.
Upper and lower case characters are distinguished (Verilog is case sensitive)
Reserved keywords cannot be used as identifiers.
Ex: Counter_4Bit, ALU, Receiver, UART_Transmit
2.1.2 PORTS
Ports provide a means for a module to communicate through input and output. Every port in the
port list must be declared as input, output or inout, in the module. All ports declared as one of the
above is assumed to be a wire by default, to declare it otherwise it is neccessary to declare it again.
For example in the D-type flip flop we want the ouput to hold on to its value until the next clock edge
so it has to be a register.
28
module d_ff(q, d, reset, clock);
output q; // all ports must be declared
input d, reset, clock; // as input or output
reg q; // the ports can be declared again as required.
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.
Input ports:
In an inner module inputs must always be of a net type, since values will be driven into them. In the
outer module the input may be a net type or a reg.
Output ports:
In an inner module outputs can be of a net type or a reg. In an outer module the output must be of a
net type since values will be driven into them by the inner module.
Inout ports:
Inouts must always be of a net type.
Port Matching:
When calling a module the width of each port must be the same, eg, a 4-bit register cannot be
matched to a 2-bit register. However, output ports may remain unconnected, by missing out their
name in the port list. This would be useful if some outputs were for debugging purposes or if
some outputs of a more general module were not required in a particular context. However input
ports cannot be omitted.
For example:
d_ff dff0( , d, reset, clock); // the output (q) has been omitted
// the comma is ESSENTIAL
2.2 ONE LANGUAGE, MANY CODING STYLES
Verilog is both a behavioral and a structural language. Internals of each module can be defined at
four levels of abstraction, depending on the needs of the design. The module behaves identically
with the external environment irrespective of the level of abstraction at which the module is
described. The internals of the module are hidden from the environment. Thus, the level of
abstraction to describe a module can be changed without any change in the environment.
2.2.1 BEHAVIORAL OR ALGORITHMIC LEVEL
This is the highest level of abstraction provided by Verilog HDL. A module can be implemented
in terms of the desired design algorithm without concern for the hardware implementation
details. Designing at this level is very similar to C programming.
29
2.2.2 DATAFLOW LEVEL
At this level, the module is designed by specifying the data flow. The designer is aware of how
data flows between hardware registers and how the data is processed in the design. Here a circuit
can be specified with assignment statements and can be expressed as a list of outputs and
expressions that transform the input values to the desired outputs. The expressions can be based
on a broad range of operators such as logical operators, bit-wise, reduction, arithmetic,
conditional and concatenation operators. All of them can be applied to the assignment statements
making data flow style more universal than logical equations.
30
2.2.3 GATE LEVEL or STRUCTURAL LEVEL
The module is implemented in terms of logic gates and interconnections between these gates.
Design at this level is similar to describing a design in terms of a gate-level logic diagram. It
resembles a schematic drawing with components connected with signals. The functionality of the
design is hidden inside the component. The components in structural specification can be either
primitives or instantiated modules. The former ones are such simple components as gates and
flip-flops (or other small scale blocks), and the latter ones can be of any complexity.
A change in the value of any input signal of a component activates the component. If two or
more components are activated concurrently, they will perform their actions concurrently as
well.
31
2.2.4 SWITCH LEVEL
This is the lowest level of abstraction provided by Verilog. A module can be implemented in
terms of transistors, switches, storage nodes, and the interconnections between them. Design at
this level requires knowledge of switch-level implementation details.
Verilog allows the designer to mix and match all four levels of abstractions in a design. In the
digital design community, the term register transfer level (RTL) is frequently used for a Verilog
description that uses a combination of behavioral and dataflow constructs and is acceptable to
32
logic synthesis tools. If a design contains four modules, Verilog allows each of the modules to be
written at a different level of abstraction. As the design matures, most modules are replaced with
gate-level implementations. Normally, the higher the level of abstraction, the more flexible and
technology-independent the design. As one goes lower toward switch-level design, the design
becomes technology-dependent and inflexible. A small modification can cause a significant
number of changes in the design. Consider the analogy with C programming and assembly
language programming. It is easier to program in a higher-level language such as C. The
program can be easily ported to any machine. However, if you design at the assembly level, the
program is specific for that machine and cannot be easily ported to another machine.
2.3 MODULE INSTANTIATION
A module provides a template from which you can create actual objects. When a module is
invoked, Verilog creates a unique object from the template. Each object has its own name,
variables, parameters, and I/O interface. The process of creating objects from a module template
is called instantiation, and the objects are called instances. A module can be instantiated in
another module thus creating hierarchy.
Syntax:
Module_name Instance_name (Port_Association_List)
Module instantiation consists of module_name followed by instance_name and
port_association_list. Need of instance_name is, we can have multiple instance of same module
in the same program. Instance name should be unique for each instance of the same module.
Port_association_list shows how ports are mapped. Port mapping can be done in two different
ways i.e. “Port mapping by order” and “Port mapping by name’. Let us take an example of 4-bit
adder for explaining module instantiation.
Example of 4-bit adder:
module fulladder_4bit(sum, cout, a, b, cin);
//input output port declarations
output [3:0] sum;
output cout;
input [3:0] a, b;
input cin;
wire c1, c2, c3;
// Instantiate four 1-bit full adders
fulladd f0 (sum[0], c1, a[0], b[0], cin);
fulladd f1 (sum[1], c2, a[1], b[1], c1);
fulladd f2 (sum[2], c3, a[2], b[2], c2);
fulladd f3 (sum[3], cout, a[3], b[3], c3);
endmodule;
33
Module for 1-bit adder is fulladd. This 1-bit adder is instantiated ‘4’ times to get functionality of
4-bit adder i.e. fulladder_4bit. Each instance of full adder has different instance name and port
association list.
AN EXAMPLE:
SYNCHRO
DFF1
DFF2
C1_ASYNC
ASYNC
D
D
DFF
CLOCK
Q
DFF
CLK
Q
SYNC
CLK
Figure shows an example for module instantiation. Figure shows module “SYNCHRO” which
consists of 2 ‘D’ flip-flops and are connected in serial fashion. Module “SYNCHRO” has 2 input
ports “ASYNC” and “CLOCK” and 1 output port “SYNC”. The first ‘D’ flip-flop has 2 inputs
“ASYNC” and “CLOCK” and 1 output “C1_ASYNC”. The second ‘D’ flip-flop 2 inputs
“C1_ASYNC” and “CLOCK” and 1 output “SYNC”.
2.3.1 MODULE PORT CONNECTION BY ORDER
module SYNCHRO(ASYNC,SYNC,CLOCK);
input ASYNC;
input CLOCK;
output SYNC;
wire C1_ASYNC;
DFF DFF1 (C1_ASYNC, ASYNC, CLOCK);
DFF DFF2 (SYNC, C1_ASYNC, CLOCK);
//DFF DFF1 (ASYNC, C1_ASYNC, CLOCK);
//DFF DFF2 (SYNC, C1_ASYNC, CLOCK);
endmodule
Here first instance name of ‘D’ flip-flop is “DFF1” and second instance name is “DFF2”. In this
module ports are connected by order. Order of ports in instantiation of DFF1 and DFF2 is
34
same as order of ports in DFF. If the number of ports increased, then it is very difficult to do
“module ports connection by order”.
2.3.2 MODULE PORT CONNECTION BY NAME
Module SYNCHRO (ASYNC, SYNC, CLOCK);
input ASYNC;
input CLOCK;
output SYNC;
wire C1_ASYNC;
DFF DFF1 (.D (ASYNC), .CLK (CLOCK), .Q (C1_ASYNC));
DFF DFF2 (.D (C1_ASYNC), .Q (SYNC), .CLK (CLOCK));
endmodule;
In this module ports are connected by Name. Order of ports in instantiation of DFF1 and DFF2 is
different from order of ports in DFF. In this ‘.’ Is used to represent port name followed by
associated port name in small brackets i.e. “()”. Advantage of using “port connection by name”
is, it is easy to port map for large number of ports in a design.
Note: Always connect ports by name.
Inside the same module, instance names of particular module should be different. In the same
way, inside the same module, instance name of different modules should be different.
For example:
module A();
DFF DFF1();
DFFE DFF1();
endmodule
IS NOT ALLOWED
Above example shows module ‘A’ is top-level module, in that ‘2’ different modules “DFF” and
“DFFE” are instantiated with same instance name “DFF1”. Since instance name is same, this is
not allowed.
35
NOT ALLOWED
ALLOWED
Figure shows a Design hierarchy. In this “ABC” is the top-level module. Two scenarios are
shown here. In first scenario “ABC” is the top-level module and “SYNCHRO1” and
“SYNCHRO2” are instance names. Under “SYNCHRO1”, “DFF1” and “DFF2” are instance
names. Under “SYNCHRO2”, “DFF1” and “DFF2” are instance names. In this instance names
are different at same hierarchy. Hence this is allowed in scenario-1.
In second scenario “ABC” is the top-level module and “SYNCHRO1” and “SYNCHRO1” are
instance names. Under “SYNCHRO1”, “DFF1” and “DFF2” are instance names. Under another
“SYNCHRO1”, “DFF1” and “DFF2” are instance names. In this instance names “SYNCHRO1”
and “SYNCHRO1” are same at the same hierarchy. Hence this is not allowed in scenario2
Note: At the same hierarchy the instance name should not be same.
2.4 LEXICOGRAPHY
Verilog, like any high level language has a number of tokens like comments, operators,
numbers, strings, identifiers and keywords.
2.4.1 COMMENTS
Comments can be inserted in the code for readability and documentation. There are two ways to
write comments. A one line comment starts with "//". Verilog skips from that point to the end of
line. A multiple-line comment block comment starts with "/*" and ends with "*/". Multiple-line
comments cannot be nested. However, one-line comments can be embedded in multiple-line
comments.
36
a = b && c; // This is a one-line comment
/* This is a multiple line
comment */
/* This is /* an illegal */ comment */
/* This is //a legal comment */
2.4.2 OPERATORS
Verilog has three types of operators; they take either one, two or three operands. Unary operators
appear on the left of their operand, binary in the middle, and ternary separates its three operands
by two operators.
clock = ~clock; // ~ is the unary bitwise negation operator, clock is the operand
c = a || b; // || is the binary logical or, a and b are the operands
r = s ? t : u; // ?: is the ternary conditional operator, which
// reads r = [if s is true then t else u]
2.4.3 NUMBERS
Integers
Integers can be in binary ( b or B ), decimal ( d or D ), hexidecimal ( h or H ) or octal ( o or O ).
Numbers are specified by
1. <size>'<base><number> : for a full description
2. <base><number>: this is given a default size which is machine dependant but at least 32 bits.
3. <number> : this is given a default base of decimal
The size specifies the exact number of bits used by the number. For example, a 4 bit binary will
have 4 as the size specification and a 4 digit hexadecimal will have 16 as the size specification
since each hexadecimal digit requires 4 bits.
8'b10100010 // 8 bit number in binary representation
8'hA2 // 8 bit number in hexadecimal representation
X and Z values
x represents an unknown, and z a high impedance value. An x declares 4 unknown bits in
hexadecimal, 3 in octal and 1 in binary. z declares high impedance values similarly.
4'b10x0 // 4 bit binary with 2nd least sig. fig. unknown
4'b101z // 4 bit binary with least sig. fig. of high impedance
12'dz // 12 bit decimal high impedance number
37
Negative numbers
A number can be declared to be negative by putting a minus sign in front of the <size>. It must
not appear between the <size> and <base>, nor between the <base> and the number.
-8'd5 // 2's compliment of 5, held in 8 bits
8'b-5 // illegal syntax
Underscore
Underscores can be put anywhere in a number, except the beginning, to improve readability.
16'b0001_1010_1000_1111 // use of underscore to improve readability
8'b_0001_1010 // illegal use of underscore
Real
Real numbers can be in either decimal or scientific format, if expressed in decimal format they
must have at least one digit either side of the decimal point.
1.8
3_2387.3398_3047
3.8e10 // e or E for exponent
2.1e-9
3. // illegal
2.4.4 STRINGS
Strings are delimited by " ... ", and cannot be on multiple lines.
"hello world"; // legal string
"good
b
y
e
wo
rld"; // illegal string
2.4.5 IDENTIFIERS
Identifiers are user-defined words for variables, function names, module names, block names and
instance names. Identifiers begin with a letter or underscore (Not with a number or $) and can
include any number of letters, digits and underscores. Identifiers in Verilog are case-sensitive.
_ABC_ /* is not the same as */ _abc_
38
2.4.6 VERILOG KEYWORDS
These are words that have special meaning in Verilog. Some examples are assign, case, while,
wire, reg, and, or, nand, module etc. They should not be used as identifiers. All keywords must
be in lower case
2.5 DATA TYPES
2.5.1 DATA VALUES
Value Level
Condition in Hardware Circuits (Logic State)
0
Logic Zero/ False Condition/ Ground
1
Logic One/ True Condition/ Power
x
Unknown/ Uninitialized
z
High Impedance/ Floating
Values ‘x’ and ‘z’ are case insensitive.
2.5.2 NETS
Keywords: wire, tri, supply0, supply1
Default value: z
Default size: 1 bit
Nets represent the continuous updating of outputs with respect to their changing inputs. For
example in the figure below, c is connected to a by a not gate. If c is declared and initialized as
shown, it will continuously be driven by the changing value of a, its new value will not have to
be explicitly assigned to it.
If the drivers of a wire have the same value, the wire assumes this value. If the drivers have
different values it chooses the strongest, if the strengths are the same the wire assumes the value
of unknown, ‘x’. If no driver is connected it defaults to value of ‘z’.
39
The net types wire and tri shall be identical in their syntax and functions. A wire net can be used
for nets that are driven by a single gate or continuous assignment. The tri net type can be used
where multiple drivers drive a net.
Supply0 and supply1 define wires tied to logic 0 (ground) and logic 1 (power), respectively.
supply0 my_gnd; // equivalent to a wire assigned 0
supply1 a, b;
2.5.3 REGISTERS
Keywords: reg
Default value: x
Default size: 1 bit
Registers represent data storage elements. Registers retain value until another value is placed
onto them. Do not confuse the term registers in Verilog with hardware registers built from edgetriggered flip-flops in real circuits. In Verilog, the term register merely means a variable that can
hold a value. Unlike a net, a register does not need a driver. Verilog registers do not need a clock
as hardware registers do. Values of registers can be changed anytime in a simulation by
assigning a new value to the register. Register data types are commonly declared by the keyword
reg. The default value for a reg data type is x.
reg a; // single 1-bit register variable
reg [7:0] b; // an 8-bit vector; a bank of 8 registers.
An example of how registers are used is shown. Any object assigned within a Verilog procedural
block (always and initial) must be declared as a reg data type.
reg reset; // declare a variable reset that can hold its value
initial // this construct will be discussed later
begin
reset = 1'b1; //initialize reset to 1 to reset the digital circuit.
#100 reset = 1'b0; // after 100 time units reset is deasserted.
end
Comparing reg versus wire:
Comparison between reg and wire can be done through an example for multiplexure.
module MUX21 (input A, B, SEL, output wire OUT1);
assign OUT1 = (A & SEL) | (B & ~SEL);
endmodule
40
OUT1 is a wire by default
module MUX21 (input A, B, SEL, output reg OUT1);
always @ (A, B, SEL)
if (SEL)
OUT1 = A;
else
OUT1 = B;
endmodule
OUT1 must be declared as reg
2.5.4 VECTORS and ARRAYS
Nets or reg data types can be declared as vectors (multiple bit widths). If bit width is not
specified, the default is scalar (1-bit).
wire a; // scalar net variable
wire [7:0] bus; // 8-bit bus
wire [31:0] busA,busB,busC; // 3 buses of 32-bit width.
reg clock; // scalar register
reg [0:40] virtual_addr; // Vector register, virtual address 41 bits wide
Vectors can be declared at [high# : low#] or [low# : high#], but the left number in the squared
brackets is always the most significant bit of the vector. In the example shown above, bit 0 is the
most significant bit of vector virtual_addr.
Vector Part Select:
For the vector declarations shown above, it is possible to address bits or parts of vectors.
busA[7] // bit # 7 of vector busA
busB[2:0]
/* Three least significant bits of vector bus. Using busB[0:2] is illegal because the significant bit
should always be on the left of a range specification. */
virtual_addr[0:1] // Two most significant bits of vector virtual_addr
Multi-dimensional arrays can also be declared with any number of dimensions.
reg [4:0] port_id[0:7]; // Array of 8 port_ids; each port_id is 5 bits wide
41
2.5.5 MEMORIES
In digital simulation, one often needs to model register files, RAMs, and ROMs. Memories are
modeled in Verilog simply as a one-dimensional array of registers. Each element of the array is
known as an element or word and is addressed by a single array index. Each word can be one or
more bits. It is important to differentiate between n 1-bit registers and one n-bit register. A
particular word in memory is obtained by using the address as a memory array subscript.
reg mem1bit[0:1023]; // Memory mem1bit with 1K 1-bit words
reg [7:0] membyte[0:1023]; // Memory membyte with 1K 8-bit words(bytes)
membyte[511] // Fetches 1 byte word whose address is 511.
2.5.6 “INTEGER”, “REAL”, AND “TIME” REGISTER DATA TYPES
Integer, real, and time register data types are supported in Verilog.
Integer:
An integer is a general purpose register data type used for manipulating quantities. Integers are
declared by the keyword integer. Although it is possible to use reg as a general-purpose variable,
it is more convenient to declare an integer variable for purposes such as counting. The default
width for an integer is the host-machine word size, which is implementation-specific but is at
least 32 bits. Registers declared as data type reg store values as unsigned quantities, whereas
integers store values as signed quantities.
integer counter; // general purpose variable used as a counter.
initial
counter = -1; // A negative one is stored in the counter
Real:
Real number constants and real register data types are declared with the keyword real. They can
be specified in decimal notation (e.g., 3.14) or in scientific notation (e.g., 3e6, which is 3x106).
Real numbers cannot have a range declaration, and their default value is 0. When a real value is
assigned to an integer, the real number is rounded off to the nearest integer.
real delta; // Define a real variable called delta
initial
begin
delta = 4e10; // delta is assigned in scientific notation
delta = 2.13; // delta is assigned a value 2.13
end
integer i; // Define an integer i
42
initial
i = delta; // i gets the value 2 (rounded value of 2.13)
Time:
Verilog simulation is done with respect to simulation time. A special time register data type is
used in Verilog to store simulation time. A time variable is declared with the keyword time. The
width for time register data types is implementation-specific but is at least 64 bits. The system
function $time is invoked to get the current simulation time.
time save_sim_time; // Define a time variable save_sim_time
initial
save_sim_time = $time; // Save the current simulation time
Simulation time is measured in terms of simulation seconds. The unit is denoted by s, the same
as real time. However, the relationship between real time in the digital circuit and simulation
time is left to the user.
2.5.7 PARAMETERS
Verilog allows constants to be defined in a module by the keyword parameter. Parameters
cannot be used as variables. Parameter types and sizes can also be defined.
parameter port_id = 5; // Defines a constant port_id
parameter signed [15:0] WIDTH; // Fixed sign and range for parameter WIDTH
2.5.8 STRINGS
Strings can be stored in reg. The width of the register variables must be large enough to hold the
string. Each character in the string takes up 8 bits (1 byte). If the width of the register is greater
than the size of the string, Verilog fills bits to the left of the string with zeros. If the register
width is smaller than the string width, Verilog truncates the leftmost bits of the string. It is
always safe to declare a string that is slightly wider than necessary.
reg [8*18:1] string_value; // Declare a variable that is 18 bytes wide
initial
string_value = "Hello Verilog World"; // String can be stored in variable
2.6 EXPRESSIONS, OPERANDS AND OPERATORS
Dataflow modeling describes the design in terms of expressions instead of primitive gates.
Expressions, operators, and operands form the basis of dataflow modeling.
43
2.6.1 EXPRESSIONS
Expressions are constructs that combine operators and operands to produce a result.
// Examples of expressions. Combines operands and operators
a^b
addr1[20:17] + addr2[20:17]
in1 | in2
2.6.2 OPERANDS
Operands can be any one of the data types defined in Section 2.5, Data Types. Operands can be
constants, integers, real numbers, nets, registers, times, bit-select (one bit of vector net or a
vector register), part-select (selected bits of the vector net or register vector), and memories or
function calls.
integer count, final_count;
final_count = count + 1; //count is an integer operand
2.6.3 OPERATORS
Operators act on the operands to produce desired results. Verilog provides various types of
operators. Operators can be arithmetic, logical, relational, equality, bitwise, reduction, shift,
concatenation, or conditional.
Operator Types and Symbols:
Operator Type
Arithmetic
Logical
Relational
Equality
Operator Symbol
*
/
+
%
**
!
&&
||
Operation Performed
multiply
divide
add
subtract
modulus
power (exponent)
Number of Operands
two
two
two
two
two
two
logical negation
logical and
logical or
one
two
two
>
<
>=
<=
==
!=
===
!==
greater than
less than
greater than or equal
less than or equal
equality
inequality
case equality
case inequality
two
two
two
two
two
two
two
two
44
Bitwise
~
&
|
^
^~ or ~^
Reduction
&
~&
|
~|
^
^~ or ~^
Shift
>>
<<
>>>
<<<
bitwise negation
bitwise and
bitwise or
bitwise xor
bitwise xnor
reduction and
reduction nand
reduction or
reduction nor
reduction xor
reduction xnor
Right shift
Left shift
Arithmetic right shift
Arithmetic left shift
{}
{{}}
Concatenation
Replication
any number
any number
Conditional
three
Concatenation
Replication
?:
Conditional
Operator Order of Precedence:
45
one
two
two
two
two
one
one
one
one
one
one
two
two
two
two
2.6.3.1 Arithmetic Operators:
There are two types of arithmetic operators: binary and unary.
Binary operators
Binary arithmetic operators are multiply (*), divide (/), add (+), subtract (-), power (**), and
modulus (%). Binary operators take two operands.
A = 4'b0011; B = 4'b0100; // A and B are register vectors
D = 6; E = 4; F=2; // D and E are integers
A * B // Multiply A and B. Evaluates to 4'b1100
D / E // Divide D by E. Evaluates to 1. Truncates any fractional part.
F = E ** F; //E to the power F, yields 16
If any operand bit has a value x, then the result of the entire expression is x.
Modulus operators produce the remainder from the division of two numbers. They operate
similarly to the modulus operator in the C programming language.
13 % 3 // Evaluates to 1
16 % 4 // Evaluates to 0
-7 % 2 // Evaluates to -1, takes sign of the first operand
7 % -2 // Evaluates to +1, takes sign of the first operand
Unary operators
The operators + and - can also work as unary operators. They are used to specify the positive or
negative sign of the operand. Unary + or - operators have higher precedence than the binary + or
- operators.
-4 // Negative 4
+5 // Positive 5
Negative numbers are represented as 2's complement internally in Verilog.
2.6.3.2 Logical Operators:
Logical operators are logical-and (&&), logical-or (||) and logical-not (!). Operators && and ||
are binary operators. Operator ! is a unary operator. Logical operators always evaluate to a 1-bit
value, 0 (false), 1 (true), or x (ambiguous).
// Logical operations
A = 3; B = 0;
A && B // Evaluates to 0. Equivalent to (logical-1 && logical-0)
A || B // Evaluates to 1. Equivalent to (logical-1 || logical-0)
!A // Evaluates to 0. Equivalent to not(logical-1)
// Unknowns
A = 2'b0x; B = 2'b10;
A && B // Evaluates to x. Equivalent to (x && logical 1)
// Expressions
(a == 2) && (b == 3) // Evaluates to 1 if both a == 2 and b == 3 are true.
// Evaluates to 0 if either is false.
46
2.6.3.3 Relational Operators:
Relational operators are greater-than (>), less-than (<), greater-than-or-equal-to (>=), and lessthan-or-equal-to (<=). If relational operators are used in an expression, the expression returns a
logical value of 1 if the expression is true and 0 if the expression is false. If there are any
unknown or z bits in the operands, the expression takes a value x. These operators function
exactly as the corresponding operators in the C programming language.
// A = 4, B = 3
// X = 4'b1010, Y = 4'b1101, Z = 4'b1xxx
A <= B // Evaluates to a logical 0
A > B // Evaluates to a logical 1
Y >= X // Evaluates to a logical 1
Y < Z // Evaluates to an x
2.6.3.4 Equality Operators:
Equality operators are logical equality (==), logical inequality (!=), case equality (===), and
case inequality (!==). When used in an expression, equality operators return logical value 1 if
true, 0 if false. These operators compare the two operands bit by bit, with zero filling if the
operands are of unequal length.
It is important to note the difference between the logical equality operators (==, !=) and case
equality operators (===, !==). The logical equality operators (==, !=) will yield an x if either
operand has x or z in its bits. However, the case equality operators (===, !== ) compare both
operands bit by bit and compare all bits, including x and z. The result is 1 if the operands match
exactly, including x and z bits. The result is 0 if the operands do not match exactly. Case equality
operators never result in an x.
// A = 4, B = 3
// X = 4'b1010, Y = 4'b1101
// Z = 4'b1xxz, M = 4'b1xxz, N = 4'b1xxx
A == B // Results in logical 0
X != Y // Results in logical 1
X == Z // Results in x
Z === M // Results in logical 1 (all bits match, including x and z)
Z === N // Results in logical 0 (least significant bit does not match)
M !== N // Results in logical 1
2.6.3.5 Bitwise Operators:
Bitwise operators are negation (~), and (&), or (|), xor (^), xnor (^~, ~^). Bitwise operators
perform a bit-by-bit operation on two operands. They take each bit in one operand and perform
the operation with the corresponding bit in the other operand. If one operand is shorter than the
other, it will be bit-extended with zeros to match the length of the longer operand. A z is treated
as an x in a bitwise operation. The exception is the unary negation operator (~), which takes only
one operand and operates on the bits of the single operand.
47
// X = 4'b1010, Y = 4'b1101
// Z = 4'b10x1
~X // Negation. Result is 4'b0101
X | Y // Bitwise or. Result is 4'b1111
X ^~ Y // Bitwise xnor. Result is 4'b1000
X & Z // Result is 4'b10x0
It is important to distinguish bitwise operators ~, &, and | from logical operators !, &&, ||.
Logical operators always yield a logical value 0, 1, x, whereas bitwise operators yield a bit-by-bit
value. Logical operators perform a logical operation, not a bit-by-bit operation.
// X = 4'b1010, Y = 4'b0000
X | Y // bitwise operation. Result is 4'b1010
X || Y // logical operation. Equivalent to 1 || 0. Result is 1.
2.6.3.6 Reduction Operators:
Reduction operators are and (&), nand (~&), or (|), nor (~|), xor (^), and xnor (~^, ^~).
Reduction operators take only one operand. Reduction operators perform a bitwise operation on
a single vector operand and yield a 1-bit result. The logic tables for the operators are the same as
shown for Bitwise Operators. The difference is that bitwise operations are on bits from two
different operands, whereas reduction operations are on the bits of the same operand. Reduction
operators work bit by bit from right to left. Reduction nand, reduction nor, and reduction xnor
are computed by inverting the result of the reduction and, reduction or, and reduction xor,
respectively.
// X = 4'b1010
48
&X //Equivalent to 1 & 0 & 1 & 0. Results in 1'b0
^X //Equivalent to 1 ^ 0 ^ 1 ^ 0. Results in 1'b0
2.6.3.7 Shift Operators:
Shift operators are right shift ( >>), left shift (<<), arithmetic right shift (>>>), and arithmetic left
shift (<<<). Regular shift operators shift a vector operand to the right or the left by a specified
number of bits. When the bits are shifted, the vacant bit positions are filled with zeros.
Arithmetic shift operators use the context of the expression to determine the value with which to
fill the vacated bits.
// X = 4'b1100
Y = X >> 1; //Y is 4'b0110. Shift right 1 bit. 0 filled in MSB position.
Y = X << 2; //Y is 4'b0000. Shift left 2 bits.
2.6.3.8 Concatenation Operator:
The concatenation operator ( {, } ) provides a mechanism to append multiple operands. The
operands must be sized. Unsized operands are not allowed because the size of each operand must
be known for computation of the size of the result.
// A = 1'b1, B = 2'b00, C = 2'b10, D = 3'b110
Y = {B , C} // Result Y is 4'b0010
Y = {A , B[0], C[1]} // Result Y is 3'b101
2.6.3.9 Replication Operator:
Repetitive concatenation of the same number can be expressed by using a replication constant. A
replication constant specifies how many times to replicate the number inside the brackets ( { } ).
A = 1'b1; B = 2'b00; C = 2'b10;
Y = { 4{A} } // Result Y is 4'b1111
Y = { 4{A} , 2{B} , C } // Result Y is 8'b1111000010
2.6.3.10 Conditional Operator:
The conditional operator (?:) takes three operands.
Usage: condition_expr ? true_expr : false_expr ;
The condition expression (condition_expr) is first evaluated. If the result is true (logical 1), then
the true_expr is evaluated. If the result is false (logical 0), then the false_expr is evaluated.
49
i0
i1
s
2-to-1 Multiplexer, Using Logic Equations
// Module 2-to-1 multiplexer using data flow. Logic equation
module mux2_to_1 (out, i0, i1, s);
// Port declarations from the I/O diagram
output out;
input i0, i1;
input s;
//Logic equation for out
assign out = (~s & i0) | (s & i1);
endmodule
2-to-1 Multiplexer, Using Conditional Operators
// Module 2-to-1 multiplexer using data flow. Conditional operator.
module multiplexer2_to_1 (out, i0, i1, s);
// Port declarations from the I/O diagram
output out;
input i0, i1;
input s;
assign out = s ? i1 : i0;
endmodule
2.7 SYSTEM TASKS AND COMPILER DIRECTIVES
In this section, we introduce two special concepts used in Verilog: system tasks and compiler
directives.
2.7.1 SYSTEM TASKS
Verilog provides standard system tasks for certain routine operations. All system tasks appear in
the form $<keyword>.
50
System Task
Description
Usage
$display
Prints the formatted message once
when the statement is executed
during simulation. A new line is
automatically added at the end of its
output.
Example
initial $display(“Hello World”);
Prints the string between the
quotation marks with a new line
added at the end of the string.
$write
Prints the formatted message once
when the statement is executed
during simulation. No newline is
added at the end of its output.
Example
initial $write(“Hello World”);
Prints the string between the
quotation marks with no new line
added at the end of the string.
$strobe
It executes after all simulation events
in the current time step have
executed. Prints the formatted
message once when executed. This
task guarantees that the printed
values for the signals/variables are
the final values the signals/ variables
can have at that time step.
Example
initial $strobe(“Current values of A,
B and C are A=%b, B=%b, C=%b”,
A, B, C);
Prints A, B and C and prints their
value in binary format after all
simulation events in the current time
step have executed.
$monitor
Invokes a background process that
continuously monitors the signals
listed, and prints the formatted
message whenever one of the signals
changes. A newline is automatically
added at the end of its output. Only
one $monitor can be active at a
time.
Returns the current simulation time
as a 64-bit integer.
Example
initial $monitor(“Current values of
A, B and C are A=%b, B=%b,
C=%b”, A, B, C);
Monitors A, B and C and prints their
value in binary format whenever one
of the signals (i.e A or B or C)
changes its value during simulation.
Example
initial $monitor(“time = %d, A =
%d”, $time, A);
Prints the current simulation time as
a 64-bit decimal whenever $monitor
gets executed.
$time
$finish
Finishes a simulation and exits the Example
simulation process.
initial $finish;
$stop
Halts a simulation and enters an Example
interactive debug mode.
initial $stop;
51
2.7.2 COMPILER DIRECTIVES
Compiler Directives direct the pre-processor part of Verilog Parser or Simulator. They are not
bound by modules or files. When a Simulator encounters a compiler directive, the directive
remains in effect until another compiler directive either modifies it or turns it off. All compiler
directives are defined by using the `<keyword> construct.
Compiler Directive
‘include
Description
Usage
File inclusion. The contents of
another Verilog source file is
inserted where the ‘include
directive appears.
Here the contents of ‘define.h’ are
included within the ‘idu’ module.
‘define
Allows a text string to be defined Example
as a macro name.
‘define gate_regression 1
Allows ‘gate_regression’ to be
substituted by 1 where ever it gets
used.
2.8 WHAT ARE THE TIMING CONTROL STATEMENTS IN VERILOG
Timing Controls
Description
#
It delays execution for a specific amount of time, ‘delay’ may be a number,
a variable or an expression. Example of a procedural block using ‘#” is
shown below.
Here ‘(c & d)’ gets evaluated at time 0 but gets
assigned to ‘a’ after 2 time units whereas gets
evaluated after 3 time units and gets assigned to
‘e’ immediately.
52
@
It delays execution until there is a transition on any one of the signals in the
sensitivity list. ‘edge’ may be either a posedge or negedge. If no edge is
specified then any logic transition is used. Signals here may be scalar or
vector, and any data type. Example of a procedural block using ‘@’ is
shown below.
Here the statement within the always block gets
evaluated whenever there is a transition on ‘b’ or ‘c’.
wait
It delays execution until the condition evaluates as true. Example of a
procedural block using ‘wait’ is shown below.
Here we wait for ‘a’ to be equal to 2 before
evaluating ‘e’.
2.9 BASIC BLOCKS
2.9.1 INTRODUCTION TO PROCEDURAL CONSTRUCTS
There are two kinds of assignment, continuous and procedural. Continuous assignments can only
be made to nets, or a concatenation of nets. The operands can be of any data type. If one of the
operands on the right hand side (RHS) of the assignment change, as the name suggests, the net
on the left hand side (LHS) of the assignment is updated. In this way values are said to be driven
into nets. Continuous assignments can be used to replace gate level descriptions with a higher
level of abstraction.
Procedural assignments are made to reg, integer, real or time, they need updating constantly to
reflect any change in the value of the operands on the RHS. Procedural blocks in Verilog are
used to model both combinatorial and sequential logic. They are also used in building a test
bench environment for a design. There are two types of Procedural blocks which are ‘initial’ and
‘always’.
2.9.2 INITIAL BLOCK
Keywords: initial
53
An initial block consists of a statement or a group of statements enclosed in begin... end which
will be executed only once at simulation time 0. If there is more than one block they execute
concurrently and independently. The initial block is normally used for initialization, monitoring,
generating wave forms (eg, clock pulses) and processes which are executed once in a simulation.
An example of initialization and wave generation is given below.
initial
clock = 1'b0; // variable initialization
initial
begin // multiple statements have to be grouped
alpha = 0;
#10 alpha = 1; // waveform generation
#20 alpha = 0;
#5 alpha = 1;
end;
2.9.3 ALWAYS BLOCK
Keywords: always
An always block is similar to the initial block, but the statements inside an always block will
repeated continuously, in a looping fashion, until stopped by $finish or $stop.
Note: the $finish command actually terminates the simulation where as $stop merely pauses it
and awaits further instructions. Thus $finish is the preferred command unless you are using an
interactive version of the simulator.
One way to simulate a clock pulse is shown in the example below.
module pulse;
reg clock;
initial clock = 1'b0; // start the clock at 0
always #10 clock = ~clock; // toggle every 10 time units
initial #5000 $finish // end the simulation after 5000 time units
endmodule
2.10 BEHAVIORAL MODELING
The Behavioral or procedural level statements are used to model a design at a higher level of
abstraction than the other levels. They provide powerful ways of doing complex designs.
However small changes n coding methods can cause large changes in the hardware generated.
2.10.1 PROCEDURAL ASSIGNMENTS
Procedural assignments are assignment statements used within Verilog procedures (always and
initial blocks). Only reg variables and integers (and their bit/part-selects and concatenations) can
be placed left of the “=” in procedures. The right hand side of the assignment is an expression
which may use any of the operator types.
54
2.10.2 DELAY IN ASSIGNMENT (not for synthesis)
In a delayed assignment Δt time units pass before the statement is executed and the left-hand
assignment is made. With intra-assignment delay, the right side is evaluated immediately but
there is a delay of Δt before the result is place in the left hand assignment. Delays are not
supported by synthesis tools.
Syntax for Procedural Assignment
variable = expression
Delayed assignment
#Δt variable = expression;
Intra-assignment delay
variable = #Δt expression;
2.10.3 BLOCKING ASSIGNMENTS
Procedural (blocking) assignments (=) are done sequentially in the order the statements are
written. A second assignment is not started until the preceding one is complete.
Syntax
variable = expression;
variable = #Δt expression;
grab inputs now, deliver ans. later.
#Δt variable = expression;
grab inputs later, deliver ans. later
Example: For simulation
initial
begin
a=1; b=2; c=3;
#5 a = b + c; // wait for 5 units, and execute a= b + c =5.
d = a; // Time continues from last line, d=5 = b+c at t=5.
2.10.4 NONBLOCKING (RTL) ASSIGNMENTS
RTL (nonblocking) assignments (<=), which follow each other in the code, are started in parallel.
The right hand side of nonblocking assignments is evaluated starting from the completion of the
last blocking assignment or if none, the start of the procedure. The transfer to the left hand side is
made according to the delays. An intra-assignment delay in a non-blocking statement will not
delay the start of any subsequent statement blocking or non-blocking. However normal delays
will are cumulative and will delay the output.
For synthesis

One must not mix “<=” or “=” in the same procedure.
55


“<=” best mimics what physical flip-flops do; use it for “always @ (posedge clk ..) type
procedures.
“=” best corresponds to what c/c++ code would do; use it for combinational procedures.
Syntax
variable <= expression;
variable <= #Δt expression;
#Δt variable <= expression;
Example: For simulation
initial
begin
#3 b <= a; /* grab a at t=0 Deliver b at t=3.
#6 x <= b + c; // grab b+c at t=0, wait and assign x at t=6.
x is unaffected by b’s change. */
The following example shows interactions between blocking and non-blocking for simulation.
Do not mix the two types in one procedure for synthesis.
Example: for simulation only
initial
begin
a=1; b=2; c=3; x=4;
#5 a = b + c; // wait for 5 units, then grab b,c and execute a=2+3.
d = a; // Time continues from last line, d=5 = b+c at t=5.
x <= #6 b + c; // grab b+c now at t=5, don’t stop, make x=5 at t=11.
b <= #2 a; /* grab a at t=5 (end of last blocking statement).
Deliver b=5 at t=7. Previous x is unaffected by b change. */
y <= #1 b + c; // grab b+c at t=5, don’t stop, make y=5 at t=6.
#3 z = b + c; // grab b+c at t=8 (#5+#3), make z=8 (b =5 at t=7 and c = 3) at t=8.
w <= x // make w=4 at t=8. Starting at last blocking assignm.
2.10.5 PROCEDURAL ASSIGNMENT GROUPS
If a procedure block contains more than one statement, those statements must be enclosed within
Sequential begin - end block
Parallel fork - join block
When using begin-end, we can give name to that group. This is called named blocks.
Example: "begin-end" and "fork - join"
56
Begin: clk gets 0 after 1 time unit, reset gets 0 after 6 time units, enable after 11 time units, data
after 13 units. All the statements are executed in sequentially.
Fork: clk gets value after 1 time unit, reset after 5 time units, enable after 5 time units, data after
2 time units. All the statements are executed in parallel.
Sequential Statement Groups
The begin - end keywords group several statements together and cause the statements to be
evaluated in sequentially (one at a time). Any timing within the sequential groups is relative to
the previous statement. Delays in the sequence accumulate (each delay is added to the previous
delay). Block finishes after the last statement in the block.
Parallel Statement Groups
The fork - join keywords group several statements together and cause the statements to be
evaluated in parallel (all at the same time). Timing within parallel group is absolute to the
beginning of the group. Block finishes after the last statement completes (Statement with high
delay, it can be the first statement in the block).
2.10.6 CONTINUOUS ASSIGNMENT STATEMENTS
Continuous assignment statements drive nets (wire data type). They represent structural
connections. They can be used for modeling combinational logic. They are outside the
procedural blocks (always and initial blocks). The left-hand side of a continuous assignment
must be net data type.
Syntax :
assign # delay net = expression;
Example: 1-bit Adder
module adder (a,b,sum,carry);
input a, b;
output sum, carry;
assign #5 {carry,sum} = a+b;
endmodule
57
2.10.7 VARIOUS PROGRAMMING STATEMENTS USED IN VERILOG
Progra
mming
Statem
ents
if
if-else
Usage
Example
It executes the statement or statement_group if
the condition evaluates as true. If we need
more than one statement (i.e statement_group)
then we need to use begin-end or a fork-join
block. The condition here can be an expression
or a single value. If the condition evaluates to
‘0’ or unknown then the condition is
considered false, and if the condition evaluates
to ‘1’ or more then the condition is considered
true.
if (alu_func == 2’b00)
aluout = a + b;
else if (alu_func == 2’b01)
aluout = a - b;
else if (alu_func == 2’b10)
aluout = a & b;
else // alu_func == 2’b11
aluout = a | b;
if (a == b)
begin
x = 1;
ot = 4’b1111;
It executes the first statement or
end
statement_group if the condition evaluates as
true and executes the second statement or
statement_group if the condition evaluates as
false. The condition here can be an expression
or a single value. If the condition evaluates to
‘0’ or unknown then the condition is
considered false, and if the condition evaluates
to ‘ 1’ or more then the condition is considered
true.
58
case (alu_ctr)
2’b00: aluout = a + b;
2’b01: aluout = a - b;
2’b10: aluout = a & b;
default: aluout = 1’bx; /* Treated as
don’t cares for minimum logic
generation. */
endcase
case
It compares the expression with each of the
case_item’s and executes the statement or
statement_group associated with the first
matching case_item.
It executes the default if none of the
case_item’s match. Here the default case is
optional.
casez
case (x, y, z)
2’b00: aluout = a + b; /*case if x or y or z
is 2’b00.*/
2’b01: aluout = a - b;
2’b10: aluout = a & b;
default: aluout = a | b;
endcase
casez (d)
3’b1??: b = 2’b11; /* b = 11 if d = 100
or 101 or 110 or 111 */
3’b01?: b = 2’b10; // b = 10 if d = 010 or
011
It is special version of case statement where ‘z’ default: b = 2’b00;
and ‘?’ are treated as don’t cares. Similar to endcase
case statement it compares the expression with
each of the case_item’s and executes the
statement or statement_group associated with
the first matching case_item. It executes the
default if none of the case_item’s match. Here
the default case is optional.
59
casex
forever
repeat
while
for
casex (a)
2’b1x: msb = 1; // msb = 1 if a = 10 or a
= 11
/* If this were case(a) then only a=1x
would match.*/
It is special version of case statement where default: msb = 0;
‘x’, ‘z’ and ‘?’ are treated as don’t cares. endcase
Similar to case statement it compares the
expression with each of the case_item’s and
executes the statement or statement_group
associated with the first matching case_item. It
executes the default if none of the case_item’s
match. Here the default case is optional.
forever begin
@(posedge clk); // or use a= #9 a+1;
a = a + 1;
It is an infinite loop that continuously executes end
the statement or statement_group.
Syntax
repeat(expression)
repeat (2) begin // after 50, a = 00,
statement or statement _group
#50 a = 2’b00; // after 100, a = 01,
#50 a = 2’b01; // after 150, a = 00,
Like forever it is a loop that executes the
end// after 200, a = 01
statement or statement _group a fixed number
of times based on the expression.
Syntax
while(condition)
while (!overflow) begin
statement or statement_group
@(posedge clk);
a = a + 1;
It executes the statement or statement_group as end
long as the condition evaluates as true.
Syntax
for (j = 0; j <= 7; j = j + 1)
for(initial_value; condition; step)
begin
statement or statement_group
c[j] = a[j] & b[j];
The for loop here uses three expressions d[j] = a[j] | b[j];
separated by semicolons to control the loop. end
The first expression (initial_value) is executed
60
disable
once before entering the loop the first time.
The second expression (condition) is evaluated
to determine if the contents of the loop (i.e
statement or statement_group) should be
executed. If the loop condition expression is
true, the loop is entered. The final expression
(step) is evaluated at the end of the loop.
begin: accumulate
Syntax
forever
disable group_name;
begin
It discontinues execution of a named group of @(posedge clk);
statements.
a = a + 1;
if (a == 2’b0111) disable accumulate;
end
end
3. FUNCTION and TASK
Verilog provides Functions and Tasks to allow the behavioral description of a module to be
broken into more manageable parts allowing better readability and manageability. Functions and
Tasks are useful for several reasons which are, they allow often used behavioral sequences to be
written once and called when needed, they allow for a cleaner writing style and finally they
allow data to be hidden from other parts of the design.
3.1 FUNCTION
Functions are defined in the module in which they are used. It is possible to define function in
separate file and use compile directive 'include to include the function in the file which
instantiates the function. Function can call other functions, but cannot call task.
The following are some of the general rules for functions:
 Functions must contain at least one input argument and cannot drive more than one
output.
 Functions cannot contain an inout or output declaration.
 Functions cannot contain time controlled statements (#, @, wait), i.e., it cannot contain
delays .
 Function cannot include timing delays, like posedge, negedge, # delay. Which means that
function should be executed in "zero" time delay.
 Functions must contain a statement that assigns the return value to the implicit function
name register.
 The variables declared within the function are local to that function. The order of
declaration within the function defines how the variables passed to the function by the
caller are used.
61
Syntax
 Function begins with keyword function and ends with keyword endfunction.
 Inputs are declared after the keyword function.
function [msb:lsb] function_name;
input [msb:lsb] input_arguments;
reg [msb:lsb] reg_variable_list;
parameter [msb:lsb] parameter_list;
integer [msb:lsb] integer_list;
... statements ...
endfunction
Example
function [7:0] my_func; // function return 8-bit value
input [7:0] i;
reg [3:0] temp;
integer n;
temp= i[7:4] | ( i[3:0]);
my_func = {temp, i[3:0]};
endfunction
Example: Simple Function
function myfunction;
input a, b, c, d;
begin
myfunction = ((a+b) + (c-d));
end
endfunction
Calling a Function
Let’s assume that function in above example is stored in a file called myfunction.v. Advantage of
coding function in separate file is that, it can be used in multiple module's.
module func_test(a, b, c, d, e, f);
input a, b, c, d, e ;
output f;
wire f;
`include "myfunction.v"
assign f = (myfunction (a,b,c,d)) ? e :0;
endmodule
62
3.2 TASK
Tasks are used in all programming languages, generally known as procedures or sub routines. A
task is similar to a function, but unlike a function it has any number input and output ports.
Therefore tasks do not return values. Included in the main body of code tasks can be called many
times, reducing code repetition. Tasks are defined in the module in which they are used. It is
possible to define task in separate file and use compile directive 'include to include the task in the
file which instantiates the task. Task can include timing delays, like posedge, negedge, # delay.
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. Task can call another task
or function.
Syntax
 Task begins with keyword task and end's with keyword endtask
 Input and output are declared after the keyword task.
 Local variables are declared after input and output declaration.
task task_name;
input [msb:lsb] input_port_list;
output [msb:lsb] output_port_list;
reg [msb:lsb] reg_variable_list;
parameter [msb:lsb] parameter_list;
integer [msb:lsb] integer_list;
... statements ...
endtask
Example : Simple Task
task convert;
input [7:0] temp_in;
output [7:0] temp_out;
begin
temp_out = (9/5) *( temp_in + 32);
end
endtask
Example : Task using Global Variables
task convert;
begin
temp_out = (9/5) *( temp_in + 32);
end
endtask
63
Calling a Task
Let’s assume that task in example “Simple Task” is stored in a file called mytask.v. Advantage
of coding task in separate file is that, it can be used in multiple modules.
module temp_cal (temp_a, temp_b, temp_c, temp_d);
input [7:0] temp_a, temp_c;
output [7:0] temp_b, temp_d;
reg [7:0] temp_b, temp_d;
`include "mytask.v"
always @ (temp_a)
convert (temp_a, temp_b);
always @ (temp_c)
convert (temp_c, temp_d);
endmodule
4. MODELING FINITE STATE MACHINE (FSM)
State machine or FSM are the heart of any digital design. There are two types of state machines
classified by the types of outputs generated from each. The first is the Moore State Machine
where the outputs are only a function of the present state, the second is the Mealy State Machine
where one or more of the outputs are a function of the present state and one or more of the
inputs.
Mealy Model
Moore Model
64
Modeling State machines.
One thing that needs to be kept in mind when coding FSM is that, combinational logic and
sequence logic should be in two different always blocks. In the above two figures, next state
logic is always the combinational logic. State Registers and Output logic are sequential logic. It
is very important that any asynchronous signal to the next state logic should be synchronized
before feeding to FSM. Always try to keep FSM in separate Verilog file.
Using constants declaration like parameter or `define to define states of the FSM, this makes
code more readable and easy to manage.
State Diagram.
Verilog Code
FSM code should have three sections,
 Encoding style.
 Combinational part.
 Sequential part.
Encoding Style
One Hot Encoding
parameter [1:0] IDLE = 3'b001,
GNT0 = 3'b010,
GNT1 = 3'b100;
Binary Encoding
parameter [1:0] IDLE = 2'b00,
GNT0 = 2'b01,
GNT1 = 2'b10;
65
Combinational Section
This section can be modeled using function, assign statement or using always block with case
statement. For time being let’s see always block version.
next_state = 3'b000
case(state)
IDLE : if (req_0 == 1'b1)
next_state = GNT0;
else if (req_1 == 1'b1)
next_state= GNT1;
else
next_state = IDLE;
GNT0 : if (req_0 == 1'b1)
next_state = GNT0;
else
next_state = IDLE;
GNT1 : if (req_1 == 1'b1) begin
next_state = GNT1;
else
next_state =1 IDLE;
default : next_state = IDLE
endcase
end
Sequential Section
This section has be modeled using only edge sensitive logic such as always block with posedge
or negedge of clock.
always @ (posedge clock)
begin : OUTPUT_LOGIC
if (reset == 1'b1)
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b0;
state <= #1 IDLE;
end
else
begin
state <= #1 next_state;
case(state)
66
IDLE :
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b0;
end
GNT0 :
begin
gnt_0 <= #1 1'b1;
gnt_1 <= #1 1'b0;
end
GNT1 :
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b1;
end
default :
begin
gnt_0 <= #1 1'b0;
gnt_1 <= #1 1'b0;
end
endcase
end
end
5. WRITING TESTBENCHES
Writing a testbench is as complex as writing the RTL code itself. These days ASICs are getting
more and more complex and thus verifying these complex ASIC has become a challenge.
Typically 60-70% of time needed for any ASIC is spent on verification/validation/testing. For
writing testbench it is important to have the design specification of "design under test" or simply
DUT. Specs need to be understood clearly and test plan is made, which basically documents the
test bench architecture and the test scenarios ( test cases) in detail.
Example : Counter
Let’s assume that we have to verify a simple 4-bit up counter, which increments its count when
ever enable is high and resets to zero, when reset is asserted high. Reset is synchronous to clock.
Code for Counter
module counter (clk, reset, enable, count);
input clk, reset, enable;
output [3:0] count;
67
reg [3:0] count;
always @ (posedge clk)
if (reset == 1'b1)
count <= 0;
else if ( enable == 1'b1)
count <= count + 1;
endmodule
Test Plan
We will write self checking test bench, but we will do this in steps to help you understand the
concept of writing automated test benches. Our testbench environment will look something like
shown in below figure.
DUT is instantiated in testbench, and testbench will contain a clock generator, reset generator,
enable logic generator, compare logic, which basically calculate the expected count value of
counter and compare the output of counter with calculated value.
Test Cases
 Reset Test : We can start with reset deasserted, followed by asserting reset for few clock
ticks and deasserting the reset, See if counter sets its output to zero.
 Enable Test : Assert/deassert enable after reset is applied.
 Random Assert/deassert of enable and reset.
Writing TestBench
First step of any testbench creation is to create a dummy template which basically declares inputs
to DUT as reg and outputs from DUT as wire, instantiate the DUT as shown in code below.
Note: there is no port list for the test bench.
Test Bench
module counter_tb;
reg clk, reset, enable;
wire [3:0] count;
68
counter U0 (
.clk (clk),
.reset (reset),
.enable (enable),
.count (count)
);
endmodule
Next step would be to add clock generator logic. Before we add clock generator we need to drive
all the inputs to DUT to some known state as shown in code below.
Test Bench with Clock gen
module counter_tb;
reg clk, reset, enable;
wire [3:0] count;
counter U0 (
.clk (clk),
.reset (reset),
.enable (enable),
.count (count)
);
initial
begin
clk = 0;
reset = 0;
enable = 0;
end
always
#5 clk = !clk;
endmodule
Initial block in verilog is executed only once, thus simulator sets the value of clk, reset and
enable to 0, which by looking at the counter code (of course you will be refering to the the DUT
specs) could be found that driving 0 makes all this signals disabled. There are many ways to
generate clock, one could use forever loop inside an initial block as an alternate to above code.
At this point, you would like test if the testbench is generating the clock correctly. You will see
that simulator does not come out, or print anything on screen or does it dump any waveform.
Thus we need to add support for all the above as shown in code below.
69
Test Bench continues...
module counter_tb;
reg clk, reset, enable;
wire [3:0] count;
counter U0 (
.clk (clk),
.reset (reset),
.enable (enable),
.count (count)
);
initial
begin
clk = 0;
reset = 0;
enable = 0;
end
always
#5 clk = !clk;
initial
begin
$dumpfile ("counter.vcd");
$dumpvars;
end
initial
begin
$display("\t\ttime,\tclk,\treset,\tenable,\tcount");
$monitor("%d,\t%b,\t%b,\t%b,\t%d",$time, clk, reset, enable, count);
end
initial
#100 $finish;
//Rest of testbench code after this line
endmodule
$dumpfile is used for specifying the file that simulator will use to store the waveform, that can be
used later to view using waveform viewer. $dumpvars basically instructs the Verilog compiler to
start dumping all the signals to "counter.vcd".
$display is used for printing text or variables to stdout (screen), \t is for inserting tab. Syntax is
same as printf. Second line $monitor is bit different, $monitor keeps track of changes to the
variables that are in the list (clk, reset, enable, count). Whenever anyone of them changes, it
prints their value, in the respective radix specified.
70
$finish is used for terminating simulation after #100 time units (note, all the initial, always
blocks start execution at time 0).
Adding Reset Logic
Once we have the basic logic to allow us to see what our testbench is doing, we can next add the
reset logic, If we look at the testcases, we see that we had added a constraint that it should be
possible to activate reset anytime during simulation. To achieve this we have many approaches,
but we will follow something that will go long way. There is something called 'events' in
Verilog, events can be triggered, and also monitored to see, if an event has occurred. Lets code
our reset logic in such a way that it waits for the trigger event "reset_trigger" to happen, when
this event happens, reset logic asserts reset at negative edge of clock and de-asserts on next
negative edge as shown in code below. Also after de-asserting the reset, reset logic triggers
another event called "reset_done_trigger". This trigger event can then be used at somewhere else
in test bench to sync up.
Code of reset logic
event reset_trigger;
event reset_done_trigger;
initial begin
forever begin
@ (reset_trigger);
@ (negedge clk);
reset = 1;
@ (negedge clk);
reset = 0;
-> reset_done_trigger;
end
end
Adding test case logic
Moving forward, let’s add logic to generate the test cases, ok we have three testcases as in the
first part of this tutorial. Lets list them again.
 Reset Test : We can start with reset deasserted, followed by asserting reset for few clock
ticks and deasserting the reset, See if counter sets its output to zero.
 Enable Test : Assert/deassert enable after reset is applied.
 Random Assert/deassert of enable and reset.
Test Case # 1 : Asserting/ Deasserting reset
In this test case, we will just trigger the event reset_trigger after 10 simulation units.
initial
begin: TEST_CASE
71
#10 -> reset_trigger;
end
Test Case # 2 : Asserting/ Deasserting enable after reset is applied.
In this test case, we will trigger the reset logic and wait for the reset logic tocomplete its
operation, before we start driving enable signal to logic 1.
initial
begin: TEST_CASE
#10 -> reset_trigger;
@ (reset_done_trigger);
@ (negedge clk);
enable = 1;
repeat (10) begin
@ (negedge clk);
end
enable = 0;
end
Test Case # 3 : Asserting/Deasserting enable and reset randomly.
In this testcase we assert the reset, and then randomly drive values on to enable and reset signal.
initial
begin : TEST_CASE
#10 -> reset_trigger;
@ (reset_done_trigger);
fork begin
repeat (10) begin
@ (negedge clk);
enable = $random;
repeat (10) begin
@ (negedge clk);
reset = $random;
end
end
end
Well you might ask, are all this three test case exist in same file, well the answer is no. If we try
to have all three test cases on one file, then we end up having race condition due to three initial
blocks driving reset and enable signal. So normally, once test bench coding is done, test cases are
coded separately and included in testbench as `include directive as shown below. If you look
closely all the three test cases, you will find that, even through test case execution is not
complete, simulation terminates. To have better control, what we can do is, add a event like
72
"terminate_sim" and execute $finish only when this event is triggered. We can trigger this event
at the end of test case execution. The code for $finish now could look as below.
event terminate_sim;
initial begin
@ (terminate_sim);
#5 $finish;
End
and the modified test case #2 would like.
initial
begin: TEST_CASE
#10 -> reset_trigger;
@ (reset_done_trigger);
@ (negedge clk);
enable = 1;
repeat (10) begin
@ (negedge clk);
end
enable = 0;
#5 -> terminate_sim;
end
73
BASICS OF FPGA
A Hard-Coded World – ASIC
ASIC stands for Application Specific Integrated Circuit. ASIC design flow takes huge amount of
time and hence costly. It is an IC which is designed only for a particular application; i.e., one
time programmability. For Example Pentium chips (P I, P II) are ASIC chips. ASIC involves
huge rework cost. Aim should be for right output from first time even in re-work cycle.
A Re-programmability World
 Rehearse your act as many times till perfection.
 Very less turnaround time involved.
 Can afford to commit initial mistakes.
 Proto-type complex systems before going to a hard coded world.
Emulation platform can be developed using FPGA’s to validate designs at system level. Designs
can be downloaded into an FPGA based system to validate the functionality. If any defects are
encountered during the validation process the design can be fixed and verified using the same
platform.
What Re-programmability means to H/W
 Same piece of hardware.
 Different types of functionality.
 Matter of loading a new bitmap.
 Easy design changes.
 Faster development cycle.
 Suitable for low volume production.
The same piece of hardware can be used multiple times to validate either new designs or
modified (bug fixes, feature enhancement) designs. The development cycle is faster as this
involves only the design and verification cycle followed by synthesis and FPGA P&R instead of
the entire backend flow and fabrication of the hardware.
NOTE: ASIC is cost effective for mass production & FPGA is effective for low volume
production.
So What’s an FPGA?
 Field Programmable Gate Array.
 Gate Array – a custum VLSI circuit consisting of huge number of unconnected gates.
 Circuit function determined at the field by the user.
 Re-programmable.
 Pre tested for manufacturing defects.
 It is:
74
 An ASIC for FPGA manufacturer.
 An FPGA for the user.
 Comes as chip ready to operate.
Field Programmable means that the FPGA’s function is defined by a user’s program rather than
by the manufacturer of the device. A typical integrated circuit performs a particular function
defined at the time of manufacturer. In contrast, the FPGA’s function is defined by a program
written by someone other than the device manufacturer. This user programmability gives the user
access to complex integrated designs without the high engineering costs associated with
application specific integrated circuit.
The FPGA is an integrated circuit that contains many identical logic cells that can be viewed as
standard components. The individual cells are interconnected by a matrix of wires and
programmable switches. A user’s design is implemented by specifying the simple logic function
for each cell and selectively closing the switches in the interconnected matrix. The array of logic
cells and interconnects from a fabric of basic building blocks for logic circuits. Complex designs
are created by combining these basic blocks to create the desired circuit.
FPGA Devices


Xilinx Devices
 Vertex Series
o Vertex-5 family, Vertex-4 family, Vertex II family, Vertex family
o High cost, Highly efficient
 Spartan Series
o Spartan-3A, 3E, 3 family, Spartan IIE, II family, Spartan family
o Low cost, Low efficient
Altera Devices
 Stratix Series
o Stratix III family, Stratix II (GX) family, Stratix (GX) family
 Cyclone Series
o Cyclone III family, Cyclone II family, Cyclone family
75
Download