You Could Download File D - FF.V
You Could Download File D - FF.V
You Could Download File D - FF.V
1 // D flip-flop Code
2 module d_ff ( d, clk, q, q_bar);
3 input d ,clk;
4 output q, q_bar;
5 wire d ,clk;
6 reg q, q_bar;
7
8 always @ (posedge clk)
9 begin
10 q <= d;
11 q_bar <= ! d;
12 end
13
14 endmodule
You could download file d_ff.v here
One can describe a simple Flip flop as that in the above figure, as well as a complicated
design having 1 million gates. Verilog is one of the HDL languages available in the
industry for hardware designing. It allows us to design a Digital design at Behavior
Level, Register Transfer Level (RTL), Gate level and at switch level. Verilog allows
hardware designers to express their designs with behavioral constructs, deferring the
details of implementation to a later stage in the final design.
Many engineers who want to learn this language, very often ask this question, how much
time will it take to learn Verilog? Well my answer to them is "It may take no more than
one week, if you happen to know at least one programming language".
Design Styles
Verilog, like any other hardware description language, permits a design in either Bottom-
up or Top-down methodology.
Bottom-Up Design
The traditional method of electronic design is bottom-up. Each design is performed at the
gate-level using the standard gates (refer to the Digital Section for more details). With the
increasing complexity of new designs this approach is nearly impossible to maintain.
New systems consist of ASIC or microprocessors with a complexity of thousands of
transistors. These traditional bottom-up designs have to give way to new structural,
hierarchical design methods. Without these new practices it would be impossible to
handle the new complexity.
Top-Down Design
The desired design-style of all designers is the top-down one. A real top-down design
allows early testing, easy change of different technologies, a structured system design and
offers many other advantages. But it is very difficult to follow a pure top-down design.
Due to this fact most designs are a mix of both methods, implementing some key
elements of both design styles.
Figure shows a Top-Down design approach.
Verilog supports designing at many different levels of abstraction. Three of them are very
important:
• Behavioral level
• Register-Transfer Level
• Gate Level
Behavioral level
Register-Transfer Level
Gate Level
Within the logic level the characteristics of a system are described by logical links and
their timing properties. All signals are discrete signals. They can only have definite
logical values (`0', `1', `X', `Z`). The usable operations are predefined logic primitives
(AND, OR, NOT etc gates). Using gate level modeling might not be a good idea for any
level of logic design. Gate level code is generated by tools like synthesis tools and this
netlist is used for gate level simulation and for backend.
Introduction
Every new learner's dream is to understand Verilog in one day, at least enough to use it.
The next few pages are my attempt to make this dream a reality. There will be some
theory and examples followed by some exercises. This tutorial will not teach you how to
program; it is designed for those with some programming experience. Even though
Verilog executes different code blocks concurrently as opposed to the sequential
execution of most programming languages, there are still many parallels. Some
background in digital design is also helpful.
Life before Verilog was a life full of schematics. Every design, regardless of complexity,
was designed through schematics. They were difficult to verify and error-prone, resulting
in long, tedious development cycles of design, verification... design, verification... design,
verification...
When Verilog arrived, we suddenly had a different way of thinking about logic circuits.
The Verilog design cycle is more like a traditional programming one, and it is what this
tutorial will walk you through. Here's how it goes:
• Specifications (specs)
• High level design
• Low level (micro) design
• RTL coding
• Verification
• Synthesis.
First on the list is specifications - what are the restrictions and requirements we will
place on our design? What are we trying to build? For this tutorial, we'll be building a
two agent arbiter: a device that selects among two agents competing for mastership. Here
are some specs we might write up.
• Two agent arbiter.
• Active high asynchronous reset.
• Fixed priority, with agent 0 having priority over agent 1
• Grant will be asserted as long as request is asserted.
Once we have the specs, we can draw the block diagram, which is basically an
abstraction of the data flow through a system (what goes into or comes out of the black
boxes?). Since the example that we have taken is a simple one, we can have a block
diagram as shown below. We don't worry about what's inside the magical black boxes
just yet.
Now, if we were designing this machine without Verilog, the standard procedure would
dictate that we draw a state machine. From there, we'd make a truth table with state
transitions for each flip-flop. And after that we'd draw Karnaugh maps, and from K-maps
we could get the optimized circuit. This method works just fine for small designs, but
with large designs this flow becomes complicated and error prone. This is where Verilog
comes in and shows us another way.
Low level design
To see how Verilog helps us design our arbiter, let's go on to our state machine - now
we're getting into the low-level design and peeling away the cover of the previous
diagram's black box to see how our inputs affect the machine.
Each of the circles represents a state that the machine can be in. Each state corresponds
to an output. The arrows between the states are state transitions, labeled by the event that
causes the transition. For instance, the leftmost orange arrow means that if the machine is
in state GNT0 (outputting the signal that corresponds to GNT0) and receives an input of !
req_0, the machine moves to state IDLE and outputs the signal that corresponds to that.
This state machine describes all the logic of the system that you'll need. The next step is
to put it all in Verilog.
Modules
We'll need to backtrack a bit to do this. If you look at the arbiter block in the first picture,
we can see that it has got a name ("arbiter") and input/output ports (req_0, req_1, gnt_0,
and gnt_1).
Since Verilog is a HDL (hardware description language - one used for the conceptual
design of integrated circuits), it also needs to have these things. In Verilog, we call our
"black boxes" module. This is a reserved word within the program used to refer to things
with inputs, outputs, and internal logic workings; they're the rough equivalents of
functions with returns in other programming languages.
If you look closely at the arbiter block we see that there are arrow marks, (incoming for
inputs and outgoing for outputs). In Verilog, after we have declared the module name and
port names, we can define the direction of each port. (version note: In Verilog 2001 we
can define ports and port directions at the same time) The code for this is shown below.
1 module arbiter (
2 // Two slashes make a comment line.
3 clock , // clock
4 reset , // Active high, syn reset
5 req_0 , // Request 0
6 req_1 , // Request 1
7 gnt_0 , // Grant 0
8 gnt_1 // Grant 1
9 );
10 //-------------Input Ports-----------------------------
11 // Note : all commands are semicolon-delimited
12 input clock ;
13 input reset ;
14 input req_0 ;
15 input req_1 ;
16 //-------------Output Ports----------------------------
17 output gnt_0 ;
18 output gnt_1 ;
You could download file one_day1.v here
Here we have only two types of ports, input and output. In real life, we can have bi-
directional ports as well. Verilog allows us to define bi-directional ports as "inout."
Bi-Directional Ports Example -
How do you define vector signals (signals composed of sequences of more than one bit)?
Verilog provides a simple way to define these as well.
Note the [7:0] means we're using the little-endian convention - you start with 0 at the
rightmost bit to begin the vector, then move to the left. If we had done [0:7], we would be
using the big-endian convention and moving from left to right. Endianness is a purely
arbitrary way of deciding which way your data will "read," but does differ between
systems, so using the right endianness consistently is important. As an analogy, think of
some languages (English) that are written left-to-right (big-endian) versus others (Arabic)
written right-to-left (little-endian). Knowing which way the language flows is crucial to
being able to read it, but the direction of flow itself was arbitrarily set years back.
Summary
• We learnt how a block/module is defined in Verilog.
• We learnt how to define ports and port directions.
• We learnt how to declare vector/scalar ports.
Data Type
What do data types have to do with hardware? Nothing, actually. People just wanted to
write one more language that had data types in it. It's completely gratuitous; there's no
point.
A driver is a data type which can drive a load. Basically, in a physical circuit, a driver
would be anything that electrons can move through/into.
The first type of driver is called a reg in Verilog (short for "register"). The second data
type is called a wire (for... well, "wire"). You can refer to tidbits section to understand it
better.
There are lots of other data types - for instance, registers can be signed, unsigned, floating
point... as a newbie, don't worry about them right now.
Examples :
Summary
Operators
Operators, thankfully, are the same things here as they are in other programming
languages. They take two values and compare (or otherwise operate on) them to yield a
third result - common examples are addition, equals, logical-and... To make life easier for
us, nearly all operators (at least the ones in the list below) are exactly the same as their
counterparts in the C programming language.
Operator Type Operator Symbol Operation Performed
Arithmetic * Multiply
/ Division
+ Add
- Subtract
% Modulus
+ Unary plus
- Unary minus
Logical ! Logical negation
&& Logical and
|| Logical or
Relational > Greater than
< Less than
>= Greater than or equal
<= Less than or equal
Equality == Equality
!= inequality
Reduction ~ Bitwise negation
~& nand
| or
~| nor
^ xor
^~ xnor
~^ xnor
Shift >> Right shift
<< Left shift
Concatenation {} Concatenation
Conditional ? conditional
Example -
Summary
• Let's attend the C language training again, they're (almost) just like the C ones.
History Of Verilog
Verilog simulator was first used beginning in 1985 and was extended substantially
through 1987. The implementation was the Verilog simulator sold by Gateway. The first
major extension was Verilog-XL, which added a few features and implemented the
infamous "XL algorithm" which was a very efficient method for doing gate-level
simulation.
The time was late 1990. Cadence Design System, whose primary product at that time
included Thin film process simulator, decided to acquire Gateway Automation System.
Along with other Gateway products, Cadence now became the owner of the Verilog
language, and continued to market Verilog as both a language and a simulator. At the
same time, Synopsys was marketing the top-down design methodology, using Verilog.
This was a powerful combination.
In 1990, Cadence recognized that if Verilog remained a closed language, the pressures of
standardization would eventually cause the industry to shift to VHDL. Consequently,
Cadence organized the Open Verilog International (OVI), and in 1991 gave it the
documentation for the Verilog Hardware Description Language. This was the event
which "opened" the language.
OVI did a considerable amount of work to improve the Language Reference Manual
(LRM), clarifying things and making the language specification as vendor-independent as
possible.
Soon it was realized that if there were too many companies in the market for Verilog,
potentially everybody would like to do what Gateway had done so far - changing the
language for their own benefit. This would defeat the main purpose of releasing the
language to public domain. As a result in 1994, the IEEE 1364 working group was
formed to turn the OVI LRM into an IEEE standard. This effort was concluded with a
successful ballot in 1995, and Verilog became an IEEE standard in December 1995.
When Cadence gave OVI the LRM, several companies began working on Verilog
simulators. In 1992, the first of these were announced, and by 1993 there were several
Verilog simulators available from companies other than Cadence. The most successful of
these was VCS, the Verilog Compiled Simulator, from Chronologic Simulation. This was
a true compiler as opposed to an interpreter, which is what Verilog-XL was. As a result,
compile time was substantial, but simulation execution speed was much faster.
In the meantime, the popularity of Verilog and PLI was rising exponentially. Verilog as a
HDL found more admirers than well-formed and federally funded VHDL. It was only a
matter of time before people in OVI realized the need of a more universally accepted
standard. Accordingly, the board of directors of OVI requested IEEE to form a working
committee for establishing Verilog as an IEEE standard. The working committee 1364
was formed in mid 1993 and on October 14, 1993, it had its first meeting.
The standard, which combined both the Verilog language syntax and the PLI in a single
volume, was passed in May 1995 and now known as IEEE Std. 1364-1995.
After many years, new features have been added to Verilog, and the new version is called
Verilog 2001. This version seems to have fixed a lot of problems that Verilog 1995 had.
This version is called 1364-2001.
Introduction
Being new to Verilog you might want to try some examples and try designing something
new. I have listed the tool flow that could be used to achieve this. I have personally tried
this flow and found this to be working just fine for me. Here I have taken only the front
end design part and bits of FPGA design of the tool flow, that can be done without any
fat money spent on tools.
Specification
This is the stage at which we define what are the important parameters of the
system/design that you are planning to design. A simple example would be: I want to
design a counter; it should be 4 bit wide, should have synchronous reset, with active high
enable; when reset is active, counter output should go to "0".
High Level Design
This is the stage at which you define various blocks in the design and how they
communicate. Let's assume that we need to design a microprocessor: high level design
means splitting the design into blocks based on their function; in our case the blocks are
registers, ALU, Instruction Decode, Memory Interface, etc.
Low level design or Micro design is the phase in which the designer describes how each
block is implemented. It contains details of State machines, counters, Mux, decoders,
internal registers. It is always a good idea to draw waveforms at various interfaces. This
is the phase where one spends lot of time.
Figure : Sample Low level design
RTL Coding
In RTL coding, Micro design is converted into Verilog/VHDL code, using synthesizable
constructs of the language. Normally we like to lint the code, before starting verification
or synthesis.
1 module addbit (
2 a , // first input
3 b , // Second input
4 ci , // Carry input
5 sum , // sum output
6 co // carry output
7 );
8 //Input declaration
9 input a;
10 input b;
11 input ci;
12 //Ouput declaration
13 output sum;
14 output co;
15 //Port Data types
16 wire a;
17 wire b;
18 wire ci;
19 wire sum;
20 wire co;
21 //Code starts here
22 assign {co,sum} = a + b + ci;
23
24 endmodule // End of Module addbit
You could download file addbit.v here
Simulation
Simulation is the process of verifying the functional characteristics of models at any level
of abstraction. We use simulators to simulate the Hardware models. To test if the RTL
code meets the functional requirements of the specification, we must see if all the RTL
blocks are functionally correct. To achieve this we need to write a testbench, which
generates clk, reset and the required test vectors. A sample testbench for a counter is
shown below. Normally we spend 60-70% of time in design verification.
We use the waveform output from the simulator to see if the DUT (Device Under Test) is
functionally correct. Most of the simulators come with a waveform viewer. As design
becomes complex, we write self checking testbench, where testbench applies the test
vector, then compares the output of DUT with expected values.
There is another kind of simulation, called timing simulation, which is done after
synthesis or after P&R (Place and Route). Here we include the gate delays and wire
delays and see if DUT works at rated clock speed. This is also called as SDF simulation
or gate level simulation.
Synthesis
Synthesis is the process in which synthesis tools like design compiler or Synplify take
RTL in Verilog or VHDL, target technology, and constrains as input and maps the RTL
to target technology primitives. Synthesis tool, after mapping the RTL to gates, also do
the minimal amount of timing analysis to see if the mapped design is meeting the timing
requirements. (Important thing to note is, synthesis tools are not aware of wire delays,
they only know of gate delays). After the synthesis there are a couple of things that are
normally done before passing the netlist to backend (Place and Route)
The gatelevel netlist from the synthesis tool is taken and imported into place and route
tool in Verilog netlist format. All the gates and flip-flops are placed; clock tree synthesis
and reset is routed. After this each block is routed. The P&R tool output is a GDS file,
used by foundry for fabricating the ASIC. Backend team normally dumps out SPEF
(standard parasitic exchange format) /RSPF (reduced parasitic exchange format)/DSPF
(detailed parasitic exchange format) from layout tools like ASTRO to the frontend team,
who then use the read_parasitic command in tools like Prime Time to write out SDF
(standard delay format) for gate level simulation purposes.
Figure : Sample micro-processor placement
Once the chip (silicon) is back from fab, it needs to be put in a real environment and
tested before it can be released into Market. Since the simulation speed (number of clocks
per second) with RTL is very slow, there is always the possibility to find a bug in Post
silicon validation.
Introduction
If you refer to any book on programming languages, it starts with an "Hello World"
program; once you have written it, you can be sure that you can do something in that
language .
Well I am also going to show how to write a "hello world" program, followed by a
"counter" design, in Verilog.
1 //-----------------------------------------------------
2 // This is my first Verilog Program
3 // Design Name : hello_world
4 // File Name : hello_world.v
5 // Function : This program will print 'hello world'
6 // Coder : Deepak
7 //-----------------------------------------------------
8 module hello_world ;
9
10 initial begin
11 $display ("Hello World by Deepak");
12 #10 $finish;
13 end
14
15 endmodule // End of Module hello_world
You could download file hello_world.v here
Words in green are comments, blue are reserved words. Any program in Verilog starts
with reserved word 'module' <module_name>. In the above example line 8 contains
module hello_world. (Note: We can have compiler pre-processor statements like
`include', `define' before module declaration)
Line 10 contains the initial block: this block gets executed only once after the simulation
starts, at time=0 (0ns). This block contains two statements which are enclosed within
begin, at line 10, and end, at line 13. In Verilog, if you have multiple lines within a block,
you need to use begin and end. Module ends with 'endmodule' reserved word, in this case
at line 15.
Counter Design
1 //-----------------------------------------------------
2 // This is my second Verilog Design
3 // Design Name : first_counter
4 // File Name : first_counter.v
5 // Function : This is a 4 bit up-counter with
6 // Synchronous active high reset and
7 // with active high enable signal
8 //-----------------------------------------------------
9 module first_counter (
10 clock , // Clock input of the design
11 reset , // active high, synchronous Reset input
12 enable , // Active high enable signal for counter
13 counter_out // 4 bit vector output of the counter
14 ); // End of port list
15 //-------------Input Ports-----------------------------
16 input clock ;
17 input reset ;
18 input enable ;
19 //-------------Output Ports----------------------------
20 output [3:0] counter_out ;
21 //-------------Input ports Data Type-------------------
22 // By rule all the input ports should be wires
23 wire clock ;
24 wire reset ;
25 wire enable ;
26 //-------------Output Ports Data Type------------------
27 // Output port can be a storage element (reg) or a wire
28 reg [3:0] counter_out ;
29
30 //------------Code Starts Here-------------------------
31 // Since this counter is a positive edge trigged one,
32 // We trigger the below block with respect to positive
33 // edge of the clock.
34 always @ (posedge clock)
35 begin : COUNTER // Block Name
36 // At every rising edge of clock we check if reset is active
37 // If active, we load the counter output with 4'b0000
38 if (reset == 1'b1) begin
39 counter_out <= #1 4'b0000;
40 end
41 // If enable is active, then we increment the counter
42 else if (enable == 1'b1) begin
43 counter_out <= #1 counter_out + 1;
44 end
45 end // End of Block COUNTER
46
47 endmodule // End of Module counter
You could download file first_counter.v here
The counter testbench consists of clock generator, reset control, enable control and
monitor/checker logic. Below is the simple code of testbench without the
monitor/checker logic.
1 `include "first_counter.v"
2 module first_counter_tb();
3 // Declare inputs as regs and outputs as wires
4 reg clock, reset, enable;
5 wire [3:0] counter_out;
6
7 // Initialize all variables
8 initial begin
9 $display ("time\t clk reset enable counter");
10 $monitor ("%g\t %b %b %b %b",
11 $time, clock, reset, enable, counter_out);
12 clock = 1; // initial value of clock
13 reset = 0; // initial value of reset
14 enable = 0; // initial value of enable
15 #5 reset = 1; // Assert the reset
16 #10 reset = 0; // De-assert the reset
17 #10 enable = 1; // Assert enable
18 #100 enable = 0; // De-assert enable
19 #5 $finish; // Terminate simulation
20 end
21
22 // Clock generator
23 always begin
24 #5 clock = ~clock; // Toggle clock every 5 ticks
25 end
26
27 // Connect DUT to test bench
28 first_counter U_counter (
29 clock,
30 reset,
31 enable,
32 counter_out
33 );
34
35 endmodule
You could download file first_counter_tb.v here
Counter Waveform
Lexical Conventions
The basic lexical conventions used by Verilog HDL are similar to those in the C
programming language. Verilog HDL is a case-sensitive language. All keywords are in
lowercase.
White Space
White space can contain the characters for blanks, tabs, newlines, and form feeds. These
characters are ignored except when they serve to separate other tokens. However, blanks
and tabs are significant in strings.
White space characters are :
• Blank spaces
• Tabs
• Carriage returns
• New-line
• Form-feeds
1 module addbit(a,b,ci,sum,co);
2 input a,b,ci;output sum co;
3 wire a,b,ci,sum,co;endmodule
You could download file bad_code.v here
1 module addbit (
2 a,
3 b,
4 ci,
5 sum,
6 co);
7 input a;
8 input b;
9 input ci;
10 output sum;
11 output co;
12 wire a;
13 wire b;
14 wire ci;
15 wire sum;
16 wire co;
17
18 endmodule
You could download file good_code.v here
Comments
• Single line comments begin with the token // and end with a carriage return
• Multi line comments begin with the token /* and end with the token */
Examples of Comments
1 /* This is a
2 Multi line comment
3 example */
4 module addbit (
5 a,
6 b,
7 ci,
8 sum,
9 co);
10
11 // Input Ports Single line comment
12 input a;
13 input b;
14 input ci;
15 // Output ports
16 output sum;
17 output co;
18 // Data Types
19 wire a;
20 wire b;
21 wire ci;
22 wire sum;
23 wire co;
24
25 endmodule
You could download file comment.v here
Case Sensitivity
NOTE : Never use Verilog keywords as unique names, even if the case is different.
Identifiers
Identifiers are names used to give an object, such as a register or a function or a module,
a name so that it can be referenced from other places in a description.
• Identifiers must begin with an alphabetic character or the underscore character (a-
z A-Z _ )
• Identifiers may contain alphabetic characters, numeric characters, the underscore,
and the dollar sign (a-z A-Z 0-9 _ $ )
• Identifiers can be up to 1024 characters long.
data_input mu
clk_input my$clk
i386 A
Escaped Identifiers
Verilog HDL allows any character to be used in an identifier by escaping the identifier.
Escaped identifiers provide a means of including any of the printable ASCII characters in
an identifier (the decimal values 33 through 126, or 21 through 7E in hexadecimal).
Numbers in Verilog
You can specify constant numbers in decimal, hexadecimal, octal, or binary format.
Negative numbers are represented in 2's complement form. When used in a number, the
question mark (?) character is the Verilog alternative for the z character. The underscore
character (_) is legal anywhere in a number except as the first character, where it is
ignored.
Integer Numbers
Syntax: <size>'<radix><value>;
Integer Stored as
1 00000000000000000000000000000001
8'hAA 10101010
6'b10_0011 100011
'hF 00000000000000000000000000001111
Verilog expands <value> filling the specified <size> by working from right-to-left
• When <size> is smaller than <value>, then leftmost bits of <value> are truncated
• When <size> is larger than <value>, then leftmost bits are filled, based on the
value of the leftmost bit in <value>.
o Leftmost '0' or '1' are filled with '0'
o Leftmost 'Z' are filled with 'Z'
o Leftmost 'X' are filled with 'X'
Note : X Stands for unknown and Z stands for high impedance, 1 for logic high or 1 and
0 for logic low or 0.
Integer Stored as
6'hCA 001010
6'hA 001010
16'bZ ZZZZZZZZZZZZZZZZ
8'bx xxxxxxxx
Real Numbers
Verilog Supports both types of numbers, but with certain restrictions. Like 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 negative sign prefix is a positive number. Or indirect way
would be "Unsigned".
Negative numbers can be specified by putting a minus sign before the size for a constant
number, thus they become signed numbers. Verilog internally represents negative
numbers in 2's complement format. An optional signed specifier can be added for signed
arithmetic.
Examples
Number Description
32'hDEAD_BEEF Unsigned or signed positive number
-14'h1234 Signed negative number
The example file below shows how Verilog treats signed and unsigned numbers.
1 module signed_number;
2
3 reg [31:0] a;
4
5 initial begin
6 a = 14'h1234;
7 $display ("Current Value of a = %h", a);
8 a = -14'h1234;
9 $display ("Current Value of a = %h", a);
10 a = 32'hDEAD_BEEF;
11 $display ("Current Value of a = %h", a);
12 a = -32'hDEAD_BEEF;
13 $display ("Current Value of a = %h", a);
14 #10 $finish;
15 end
16
17 endmodule
You could download file signed_number.v here
Current Value of a = 00001234
Current Value of a = ffffedcc
Current Value of a = deadbeef
Current Value of a = 21524111
Introduction
Verilog has built in primitives like gates, transmission gates, and switches. These are
rarely used in design (RTL Coding), but are used in post synthesis world for modeling the
ASIC/FPGA cells; these cells are then used for gate level simulation, or what is called as
SDF simulation. Also the output netlist format from the synthesis tool, which is imported
into the place and route tool, is also in Verilog gate level primitives.
Note : RTL engineers still may use gate level primitivies or ASIC library cells in RTL
when using IO CELLS, Cross domain synch cells.
Gate Primitives
The gates have one scalar output and multiple scalar inputs. The 1st terminal in the list of
gate terminals is an output and the other terminals are inputs.
Gate Description
and N-input AND gate
nand N-input NAND gate
or N-input OR gate
nor N-input NOR gate
xor N-input XOR gate
xnor N-input XNOR gate
Examples
1 module gates();
2
3 wire out0;
4 wire out1;
5 wire out2;
6 reg in1,in2,in3,in4;
7
8 not U1(out0,in1);
9 and U2(out1,in1,in2,in3,in4);
10 xor U3(out2,in1,in2,in3);
11
12 initial begin
13 $monitor(
14 "in1=%b in2=%b in3=%b in4=%b out0=%b out1=%b out2=%b",
15 in1,in2,in3,in4,out0,out1,out2);
16 in1 = 0;
17 in2 = 0;
18 in3 = 0;
19 in4 = 0;
20 #1 in1 = 1;
21 #1 in2 = 1;
22 #1 in3 = 1;
23 #1 in4 = 1;
24 #1 $finish;
25 end
26
27 endmodule
You could download file gates.v here
Gate Description
not N-output inverter
buf N-output buffer.
bufif0 Tri-state buffer, Active low en.
bufif1 Tri-state buffer, Active high en.
notif0 Tristate inverter, Low en.
notif1 Tristate inverter, High en.
Transmission gates tran and rtran are permanently on and do not have a control line. Tran
can be used to interface two wires with seperate drives, and rtran can be used to weaken
signals.
Examples
1 module transmission_gates();
2
3 reg data_enable_low, in;
4 wire data_bus, out1, out2;
5
6 bufif0 U1(data_bus,in, data_enable_low);
7 buf U2(out1,in);
8 not U3(out2,in);
9
10 initial begin
11 $monitor(
12 "@%g in=%b data_enable_low=%b out1=%b out2= b data_bus=%b",
13 $time, in, data_enable_low, out1, out2, data_bus);
14 data_enable_low = 0;
15 in = 0;
16 #4 data_enable_low = 1;
17 #8 $finish;
18 end
19
20 always #2 in = ~in;
21
22 endmodule
You could download file transmission_gates.v here
Switch Primitives
There are six different switch primitives (transistor models) used in Verilog, nmos, pmos
and cmos and the corresponding three resistive versions rnmos, rpmos and rcmos. The
cmos type of switches have two gates and so have two control signals.
Syntax: keyword unique_name (drain. source, gate)
Gate Description
1. pmos Uni-directional PMOS switch
1. rpmos Resistive PMOS switch
2. nmos Uni-directional NMOS switch
2. rnmos Resistive NMOS switch
3. cmos Uni-directional CMOS switch
3. rcmos Resistive CMOS switch
4. tranif1 Bi-directional transistor (High)
4. tranif0 Bi-directional transistor (Low)
5. rtranif1 Resistive Transistor (High)
5. rtranif0 Resistive Transistor (Low)
6. tran Bi-directional pass transistor
6. rtran Resistive pass transistor
7. pullup Pull up resistor
8. pulldown Pull down resistor
Transmission gates are bi-directional and can be resistive or non-resistive. Resistive
devices reduce the signal strength which appears on the output by one level. All the
switches only pass signals from source to drain, incorrect wiring of the devices will result
in high impedance outputs.
Examples
1 module switch_primitives();
2
3 wire net1, net2, net3;
4 wire net4, net5, net6;
5
6 tranif0 my_gate1 (net1, net2, net3);
7 rtranif1 my_gate2 (net4, net5, net6);
8
9 endmodule
You could download file switch_primitives.v here
Transmission gates tran and rtran are permanently on and do not have a control line. Tran
can be used to interface two wires with separate drives, and rtran can be used to weaken
signals. Resistive devices reduce the signal strength which appears on the output by one
level. All the switches only pass signals from source to drain, incorrect wiring of the
devices will result in high impedance outputs.
A : Pull 1
B : Supply 0
A : Supply 1
B : Large 1
Introduction
Verilog has built-in primitives like gates, transmission gates, and switches. This is a
rather small number of primitives; if we need more complex primitives, then Verilog
provides UDP, or simply User Defined Primitives. Using UDP we can model
• Combinational Logic
• Sequential Logic
We can include timing information along with these UDP to model complete ASIC
library models.
Syntax
UDP begins with reserve word primitive and ends with endprimitive. Ports/terminals of
primitive should follow. This is similar to what we do for module definition. UDPs
should be defined outside module and endmodule
In the above code, udp_syntax is the primitive name, it contains ports a, b,c,d.
<UDP>
::= primitive <name_of_UDP> ( <output_terminal_name>,
<input_terminal_name> <,<input_terminal_name>>* ) ;
<UDP_declaration>+
<UDP_initial_statement>?
<table_definition>
endprimitive
<name_of_UDP>
::= <IDENTIFIER>
<UDP_declaration>
::= <UDP_output_declaration>
||= <reg_declaration>
||= <UDP_input_declaration>
<UDP_output_declaration>
::= output <output_terminal _name>;
<reg_declaration>
::= reg <output_terminal_name> ;
<UDP_input_declaration>
::= input <input_terminal_name> <,<input_terminal_name>>* ;
<UDP_initial_statement>
::= initial <output_terminal_name> = <init_val> ;
<init_val>
::= 1'b0
||= 1'b1
||= 1'bx
||= 1
||= 0
<table_definition>
::= table
<table_entries>
endtable
<table_entries>
::= <combinational_entry>+
||= <sequential_entry>+
<combinational_entry>
::= <level_input_list> : <OUTPUT_SYMBOL> ;
<sequential_entry>
::= <input_list> : <state> : <next_state> ;
<input_list>
::= <level_input_list>
||= <edge_input_list>
<level_input_list>
::= <LEVEL_SYMBOL>+
<edge_input_list>
::= <LEVEL_SYMBOL>* <edge> <LEVEL_SYMBOL>*
<edge>
::= ( <LEVEL_SYMBOL> <LEVEL_SYMBOL> )
||= <EDGE_SYMBOL>
<state>
::= <LEVEL_SYMBOL>
<next_state>
::= <OUTPUT_SYMBOL>
||= -
Body
1 `include "udp_body.v"
2 module udp_body_tb();
3
4 reg b,c;
5 wire a;
6
7 udp_body udp (a,b,c);
8
9 initial begin
10 $monitor(" B = %b C = %b A = %b",b,c,a);
11 b = 0;
12 c = 0;
13 #1 b = 1;
14 #1 b = 0;
15 #1 c = 1;
16 #1 b = 1'bx;
17 #1 c = 0;
18 #1 b = 1;
19 #1 c = 1'bx;
20 #1 b = 0;
21 #1 $finish;
22 end
23
24 endmodule
You could download file udp_body_tb.v here
Simulator Output
B = 0 C = 0 A = 0
B = 1 C = 0 A = 1
B = 0 C = 0 A = 0
B = 0 C = 1 A = 1
B = x C = 1 A = 1
B = x C = 0 A = x
B = 1 C = 0 A = 1
B = 1 C = x A = 1
B = 0 C = x A = x
Table
Table is used for describing the function of UDP. Verilog reserved word table marks the
start of table and reserved word endtable marks the end of table.
Each line inside a table is one condition; when an input changes, the input condition is
matched and the output is evaluated to reflect the new change in input.
Initial
Initial statement is used for initialization of sequential UDPs. This statement begins with
the keyword 'initial'. The statement that follows must be an assignment statement that
assigns a single bit literal value to the output terminal reg.
Symbols
UDP uses special symbols to describe functions like rising edge, don't care and so on.
The table below shows the symbols that are used in UDPs:
Arithmetic Operators
Example
1 module arithmetic_operators();
2
3 initial begin
4 $display (" 5 + 10 = %d", 5 + 10);
5 $display (" 5 - 10 = %d", 5 - 10);
6 $display (" 10 - 5 = %d", 10 - 5);
7 $display (" 10 * 5 = %d", 10 * 5);
8 $display (" 10 / 5 = %d", 10 / 5);
9 $display (" 10 / -5 = %d", 10 / -5);
10 $display (" 10 %s 3 = %d","%", 10 % 3);
11 $display (" +5 = %d", +5);
12 $display (" -5 = %d", -5);
13 #10 $finish;
14 end
15
16 endmodule
You could download file arithmetic_operators.v here
5 + 10 = 15
5 - 10 = -5
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2
10 / -5 = -2
10 % 3 = 1
+5 = 5
-5 = -5
Relational Operators
Operator Description
a<b a less than b
a>b a greater than b
a <= b a less than or equal to b
a >= b a greater than or equal to b
Note: If any operand is x or z, then the result of that test is treated as false (0)
Example
1 module relational_operators();
2
3 initial begin
4 $display (" 5 <= 10 = %b", (5 <= 10));
5 $display (" 5 >= 10 = %b", (5 >= 10));
6 $display (" 1'bx <= 10 = %b", (1'bx <= 10));
7 $display (" 1'bz <= 10 = %b", (1'bz <= 10));
8 #10 $finish;
9 end
10
11 endmodule
You could download file relational_operators.v here
5 <= 10 = 1
5 >= 10 = 0
1'bx <= 10 = x
1'bz <= 10 = x
Equality Operators
There are two types of Equality operators. Case Equality and Logical Equality.
Operator Description
a === b a equal to b, including x and z (Case equality)
a !== b a not equal to b, including x and z (Case inequality)
a == b a equal to b, result may be unknown (logical equality)
a != b a not equal to b, result may be unknown (logical equality)
• Operands are compared bit by bit, with zero filling if the two operands do not
have the same length
• Result is 0 (false) or 1 (true)
• For the == and != operators, the result is x, if either operand contains an x or a z
• For the === and !== operators, bits with x and z are included in the comparison
and must match for the result to be true
Example
1 module equality_operators();
2
3 initial begin
4 // Case Equality
5 $display (" 4'bx001 === 4'bx001 = %b", (4'bx001 === 4'bx001));
6 $display (" 4'bx0x1 === 4'bx001 = %b", (4'bx0x1 === 4'bx001));
7 $display (" 4'bz0x1 === 4'bz0x1 = %b", (4'bz0x1 === 4'bz0x1));
8 $display (" 4'bz0x1 === 4'bz001 = %b", (4'bz0x1 === 4'bz001));
9 // Case Inequality
10 $display (" 4'bx0x1 !== 4'bx001 = %b", (4'bx0x1 ! == 4'bx001));
11 $display (" 4'bz0x1 !== 4'bz001 = %b", (4'bz0x1 ! ==
4'bz001));
12 // Logical Equality
13 $display (" 5 == 10 = %b", (5 == 10));
14 $display (" 5 == 5 = %b", (5 == 5));
15 // Logical Inequality
16 $display (" 5 != 5 = %b", (5 ! = 5));
17 $display (" 5 != 6 = %b", (5 ! = 6));
18 #10 $finish;
19 end
20
21 endmodule
You could download file equality_operators.v here
Logical Operators
Operator Description
! logic negation
&& logical and
|| logical or
Example
1 module logical_operators();
2
3 initial begin
4 // Logical AND
5 $display ("1'b1 && 1'b1 = %b", (1'b1 && 1'b1));
6 $display ("1'b1 && 1'b0 = %b", (1'b1 && 1'b0));
7 $display ("1'b1 && 1'bx = %b", (1'b1 && 1'bx));
8 // Logical OR
9 $display ("1'b1 || 1'b0 = %b", (1'b1 || 1'b0));
10 $display ("1'b0 || 1'b0 = %b", (1'b0 || 1'b0));
11 $display ("1'b0 || 1'bx = %b", (1'b0 || 1'bx));
12 // Logical Negation
13 $display ("! 1'b1 = %b", ( ! 1'b1));
14 $display ("! 1'b0 = %b", ( ! 1'b0));
15 #10 $finish;
16 end
17
18 endmodule
You could download file logical_operators.v here
Bit-wise Operators
Bitwise operators perform a bit wise 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 extended on the left side with zeroes to
match the length of the longer operand.
Operator Description
~ negation
& and
| inclusive or
^ exclusive or
^~ or ~^ exclusive nor (equivalence)
Example
1 module bitwise_operators();
2
3 initial begin
4 // Bit Wise Negation
5 $display (" ~4'b0001 = %b", (~4'b0001));
6 $display (" ~4'bx001 = %b", (~4'bx001));
7 $display (" ~4'bz001 = %b", (~4'bz001));
8 // Bit Wise AND
9 $display (" 4'b0001 & 4'b1001 = %b", (4'b0001 & 4'b1001));
10 $display (" 4'b1001 & 4'bx001 = %b", (4'b1001 & 4'bx001));
11 $display (" 4'b1001 & 4'bz001 = %b", (4'b1001 & 4'bz001));
12 // Bit Wise OR
13 $display (" 4'b0001 | 4'b1001 = %b", (4'b0001 | 4'b1001));
14 $display (" 4'b0001 | 4'bx001 = %b", (4'b0001 | 4'bx001));
15 $display (" 4'b0001 | 4'bz001 = %b", (4'b0001 | 4'bz001));
16 // Bit Wise XOR
17 $display (" 4'b0001 ^ 4'b1001 = %b", (4'b0001 ^ 4'b1001));
18 $display (" 4'b0001 ^ 4'bx001 = %b", (4'b0001 ^ 4'bx001));
19 $display (" 4'b0001 ^ 4'bz001 = %b", (4'b0001 ^ 4'bz001));
20 // Bit Wise XNOR
21 $display (" 4'b0001 ~^ 4'b1001 = %b", (4'b0001 ~^ 4'b1001));
22 $display (" 4'b0001 ~^ 4'bx001 = %b", (4'b0001 ~^ 4'bx001));
23 $display (" 4'b0001 ~^ 4'bz001 = %b", (4'b0001 ~^ 4'bz001));
24 #10 $finish;
25 end
26
27 endmodule
You could download file bitwise_operators.v here
~4'b0001 = 1110
~4'bx001 = x110
~4'bz001 = x110
4'b0001 & 4'b1001 = 0001
4'b1001 & 4'bx001 = x001
4'b1001 & 4'bz001 = x001
4'b0001 | 4'b1001 = 1001
4'b0001 | 4'bx001 = x001
4'b0001 | 4'bz001 = x001
4'b0001 ^ 4'b1001 = 1000
4'b0001 ^ 4'bx001 = x000
4'b0001 ^ 4'bz001 = x000
4'b0001 ~^ 4'b1001 = 0111
4'b0001 ~^ 4'bx001 = x111
4'b0001 ~^ 4'bz001 = x111
Procedural Blocks
Verilog behavioral code is inside procedure blocks, but there is an exception: some
behavioral code also exist outside procedure blocks. We can see this in detail as we make
progress.
There are two types of procedural blocks in Verilog:
• initial : initial blocks execute only once at time zero (start execution at time zero).
• always : always blocks loop to execute over and over again; in other words, as the
name suggests, it executes always.
Example - initial
1 module initial_example();
2 reg clk,reset,enable,data;
3
4 initial begin
5 clk = 0;
6 reset = 0;
7 enable = 0;
8 data = 0;
9 end
10
11 endmodule
You could download file initial_example.v here
In the above example, the initial block execution and always block execution starts at
time 0. Always block waits for the event, here positive edge of clock, whereas initial
block just executed all the statements within begin and end statement, without waiting.
Example - always
1 module always_example();
2 reg clk,reset,enable,q_in,data;
3
4 always @ (posedge clk)
5 if (reset) begin
6 data <= 0;
7 end else if (enable) begin
8 data <= q_in;
9 end
10
11 endmodule
You could download file always_example.v here
In an always block, when the trigger event occurs, the code inside begin and end is
executed; then once again the always block waits for next event triggering. This process
of waiting and executing on event is repeated till simulation stops.
1 module initial_bad();
2 reg clk,reset;
3 wire enable,data;
4
5 initial begin
6 clk = 0;
7 reset = 0;
8 enable = 0;
9 data = 0;
10 end
11
12 endmodule
You could download file initial_bad.v here
1 module initial_good();
2 reg clk,reset,enable,data;
3
4 initial begin
5 clk = 0;
6 reset = 0;
7 enable = 0;
8 data = 0;
9 end
10
11 endmodule
You could download file initial_good.v here
If a procedure block contains more than one statement, those statements must be enclosed
within
When using begin-end, we can give name to that group. This is called named blocks.
Example - "begin-end"
1 module initial_begin_end();
2 reg clk,reset,enable,data;
3
4 initial begin
5 $monitor(
6 "%g clk=%b reset=%b enable=%b data=%b",
7 $time, clk, reset, enable, data);
8 #1 clk = 0;
9 #10 reset = 0;
10 #5 enable = 0;
11 #3 data = 0;
12 #1 $finish;
13 end
14
15 endmodule
You could download file initial_begin_end.v here
Begin : clk gets 0 after 1 time unit, reset gets 0 after 11 time units, enable after 16 time
units, data after 19 units. All the statements are executed sequentially.
Simulator Output
Example - "fork-join"
1 module initial_fork_join();
2 reg clk,reset,enable,data;
3
4 initial begin
5 $monitor("%g clk=%b reset=%b enable=%b data=%b",
6 $time, clk, reset, enable, data);
7 fork
8 #1 clk = 0;
9 #10 reset = 0;
10 #5 enable = 0;
11 #3 data = 0;
12 join
13 #1 $display ("%g Terminating simulation", $time);
14 $finish;
15 end
16
17 endmodule
You could download file initial_fork_join.v here
Fork : clk gets its value after 1 time unit, reset after 10 time units, enable after 5 time
units, data after 3 time units. All the statements are executed in parallel.
Simulator Output
Example - sequential
1 module sequential();
2
3 reg a;
4
5 initial begin
6 $monitor ("%g a = %b", $time, a);
7 #10 a = 0;
8 #11 a = 1;
9 #12 a = 0;
10 #13 a = 1;
11 #14 $finish;
12 end
13
14 endmodule
You could download file sequential.v here
Simulator Output
0 a = x
10 a = 0
21 a = 1
33 a = 0
46 a = 1
Example - Parallel
1 module parallel();
2
3 reg a;
4
5 initial
6 fork
7 $monitor ("%g a = %b", $time, a);
8 #10 a = 0;
9 #11 a = 1;
10 #12 a = 0;
11 #13 a = 1;
12 #14 $finish;
13 join
14
15 endmodule
You could download file parallel.v here
Simulator Output
0 a = x
10 a = 0
11 a = 1
12 a = 0
13 a = 1
Example - Mixing "begin-end" and "fork - join"
1 module fork_join();
2
3 reg clk,reset,enable,data;
4
5 initial begin
6 $display ("Starting simulation");
7 $monitor("%g clk=%b reset=%b enable=%b data=%b",
8 $time, clk, reset, enable, data);
9 fork : FORK_VAL
10 #1 clk = 0;
11 #5 reset = 0;
12 #5 enable = 0;
13 #2 data = 0;
14 join
15 #10 $display ("%g Terminating simulation", $time);
16 $finish;
17 end
18
19 endmodule
You could download file fork_join.v here
Simulator Output
Blocking assignments are executed in the order they are coded, hence they are sequential.
Since they block the execution of next statment, till the current statement is executed,
they are called blocking assignments. Assignment are made with "=" symbol. Example a
= b;
Nonblocking assignments are executed in parallel. Since the execution of next statement
is not blocked due to execution of current statement, they are called nonblocking
statement. Assignments are made with "<=" symbol. Example a <= b;
1 module blocking_nonblocking();
2
3 reg a,b,c,d;
4 // Blocking Assignment
5 initial begin
6 #10 a = 0;
7 #11 a = 1;
8 #12 a = 0;
9 #13 a = 1;
10 end
11
12 initial begin
13 #10 b <= 0;
14 #11 b <= 1;
15 #12 b <= 0;
16 #13 b <= 1;
17 end
18
19 initial begin
20 c = #10 0;
21 c = #11 1;
22 c = #12 0;
23 c = #13 1;
24 end
25
26 initial begin
27 d <= #10 0;
28 d <= #11 1;
29 d <= #12 0;
30 d <= #13 1;
31 end
32
33 initial begin
34 $monitor("TIME = %g A = %b B = %b C = %b D = %b",$time, a, b, c,
d);
35 #50 $finish;
36 end
37
38 endmodule
You could download file blocking_nonblocking.v here
Simulator Output
TIME = 0 A = x B = x C = x D = x
TIME = 10 A = 0 B = 0 C = 0 D = 0
TIME = 11 A = 0 B = 0 C = 0 D = 1
TIME = 12 A = 0 B = 0 C = 0 D = 0
TIME = 13 A = 0 B = 0 C = 0 D = 1
TIME = 21 A = 1 B = 1 C = 1 D = 1
TIME = 33 A = 0 B = 0 C = 0 D = 1
TIME = 46 A = 1 B = 1 C = 1 D = 1
Waveform
The assign and deassign procedural assignment statements allow continuous assignments
to be placed onto registers for controlled periods of time. The assign procedural statement
overrides procedural assignments to a register. The deassign procedural statement ends a
continuous assignment to a register.
Simulator Output
Another form of procedural continuous assignment is provided by the force and release
procedural statements. These statements have a similar effect on the assign-deassign pair,
but a force can be applied to nets as well as to registers.
One can use force and release while doing gate level simulation to work around reset
connectivity problems. Also can be used insert single and double bit errors on data read
from memory.
Simulator Output
• Delay controls.
• Edge-Sensitive Event controls.
• Level-Sensitive Event controls-Wait statements.
• Named Events.
Delay Controls
Simulation Output
Delays execution of the next statement until the specified transition on a signal.
syntax : @ (< posedge >|< negedge > signal) < statement >;
1 module edge_wait_example();
2
3 reg enable, clk, trigger;
4
5 always @ (posedge enable)
6 begin
7 trigger = 0;
8 // Wait for 5 clock cycles
9 repeat (5) begin
10 @ (posedge clk) ;
11 end
12 trigger = 1;
13 end
14
15 //Testbench code here
16 initial begin
17 $monitor ("TIME : %g CLK : %b ENABLE : %b TRIGGER : %b",
18 $time, clk,enable,trigger);
19 clk = 0;
20 enable = 0;
21 #5 enable = 1;
22 #1 enable = 0;
23 #10 enable = 1;
24 #1 enable = 0;
25 #10 $finish;
26 end
27
28 always
29 #1 clk = ~clk;
30
31 endmodule
You could download file edge_wait_example.v here
Simulator Output
Delays execution of the next statement until < expression > evaluates to true
1 module wait_example();
2
3 reg mem_read, data_ready;
4 reg [7:0] data_bus, data;
5
6 always @ (mem_read or data_bus or data_ready)
7 begin
8 data = 0;
9 while (mem_read == 1'b1) begin
10 // #1 is very important to avoid infinite loop
11 wait (data_ready == 1) #1 data = data_bus;
12 end
13 end
14
15 // Testbench Code here
16 initial begin
17 $monitor ("TIME = %g READ = %b READY = %b DATA = %b",
18 $time, mem_read, data_ready, data);
19 data_bus = 0;
20 mem_read = 0;
21 data_ready = 0;
22 #10 data_bus = 8'hDE;
23 #10 mem_read = 1;
24 #20 data_ready = 1;
25 #1 mem_read = 1;
26 #1 data_ready = 0;
27 #10 data_bus = 8'hAD;
28 #10 mem_read = 1;
29 #20 data_ready = 1;
30 #1 mem_read = 1;
31 #1 data_ready = 0;
32 #10 $finish;
33 end
34
35 endmodule
You could download file wait_example.v here
Simulator Output
Intra-assignment controls always evaluate the right side expression immediately and
assign the result after the delay or event control.
In non-intra-assignment controls (delay or event control on the left side), the right side
expression is evaluated after the delay or event control.
Example - Intra-Assignment
1 module intra_assign();
2
3 reg a, b;
4
5 initial begin
6 $monitor("TIME = %g A = %b B = %b",$time, a , b);
7 a = 1;
8 b = 0;
9 a = #10 0;
10 b = a;
11 #20 $display("TIME = %g A = %b B = %b",$time, a , b);
12 $finish;
13 end
14
15 endmodule
You could download file intra_assign.v here
Simulation Output
TIME = 0 A = 1 B = 0
TIME = 10 A = 0 B = 0
TIME = 30 A = 0 B = 0
Waveform
1 module tri_buf_using_assign();
2 reg data_in, enable;
3 wire pad;
4
5 assign pad = (enable) ? data_in : 1'bz;
6
7 initial begin
8 $monitor ("TIME = %g ENABLE = %b DATA : %b PAD %b",
9 $time, enable, data_in, pad);
10 #1 enable = 0;
11 #1 data_in = 1;
12 #1 enable = 1;
13 #1 data_in = 0;
14 #1 enable = 0;
15 #1 $finish;
16 end
17
18 endmodule
You could download file tri_buf_using_assign.v here
Simulation Output
Waveform
Example - Mux
1 module mux_using_assign();
2 reg data_in_0, data_in_1;
3 wire data_out;
4 reg sel;
5
6 assign data_out = (sel) ? data_in_1 : data_in_0;
7
8 // Testbench code here
9 initial begin
10 $monitor("TIME = %g SEL = %b DATA0 = %b DATA1 = %b OUT = %b",
11 $time,sel,data_in_0,data_in_1,data_out);
12 data_in_0 = 0;
13 data_in_1 = 0;
14 sel = 0;
15 #10 sel = 1;
16 #10 $finish;
17 end
18
19 // Toggel data_in_0 at #1
20 always
21 #1 data_in_0 = ~data_in_0;
22
23 // Toggel data_in_1 at #2
24 always
25 #2 data_in_1 = ~data_in_1;
26
27 endmodule
You could download file mux_using_assign.v here
Simulation Output
Waveform
Task
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 module simple_task();
2
3 task convert;
4 input [7:0] temp_in;
5 output [7:0] temp_out;
6 begin
7 temp_out = (9/5) *( temp_in + 32)
8 end
9 endtask
10
11 endmodule
You could download file simple_task.v here
Example - Task using Global Variables
1 module task_global();
2
3 reg [7:0] temp_out;
4 reg [7:0] temp_in;
5
6 task convert;
7 begin
8 temp_out = (9/5) *( temp_in + 32);
9 end
10 endtask
11
12 endmodule
You could download file task_global.v here
Calling a Task
Let's assume that the task in example 1 is stored in a file called mytask.v. Advantage of
coding a task in a separate file, is that it can be used in multiple modules.
Below is the waveform used for writing into memory and reading from memory. We
make the assumption that there is a need to use this interface from multiple agents. So we
write the read/write as tasks.
1 module bus_wr_rd_task();
2
3 reg clk,rd,wr,ce;
4 reg [7:0] addr,data_wr,data_rd;
5 reg [7:0] read_data;
6
7 initial begin
8 clk = 0;
9 read_data = 0;
10 rd = 0;
11 wr = 0;
12 ce = 0;
13 addr = 0;
14 data_wr = 0;
15 data_rd = 0;
16 // Call the write and read tasks here
17 #1 cpu_write(8'h11,8'hAA);
18 #1 cpu_read(8'h11,read_data);
19 #1 cpu_write(8'h12,8'hAB);
20 #1 cpu_read(8'h12,read_data);
21 #1 cpu_write(8'h13,8'h0A);
22 #1 cpu_read(8'h13,read_data);
23 #100 $finish;
24 end
25 // Clock Generator
26 always
27 #1 clk = ~clk;
28 // CPU Read Task
29 task cpu_read;
30 input [7:0] address;
31 output [7:0] data;
32 begin
33 $display ("%g CPU Read task with address : %h", $time,
address);
34 $display ("%g -> Driving CE, RD and ADDRESS on to bus", $time);
35 @ (posedge clk);
36 addr = address;
37 ce = 1;
38 rd = 1;
39 @ (negedge clk);
40 data = data_rd;
41 @ (posedge clk);
42 addr = 0;
43 ce = 0;
44 rd = 0;
45 $display ("%g CPU Read data : %h", $time, data);
46 $display ("======================");
47 end
48 endtask
49 // CU Write Task
50 task cpu_write;
51 input [7:0] address;
52 input [7:0] data;
53 begin
54 $display ("%g CPU Write task with address : %h Data : %h",
55 $time, address,data);
56 $display ("%g -> Driving CE, WR, WR data and ADDRESS on to
bus",
57 $time);
58 @ (posedge clk);
59 addr = address;
60 ce = 1;
61 wr = 1;
62 data_wr = data;
63 @ (posedge clk);
64 addr = 0;
65 ce = 0;
66 wr = 0;
67 $display ("======================");
68 end
69 endtask
70
71 // Memory model for checking tasks
72 reg [7:0] mem [0:255];
73
74 always @ (addr or ce or rd or wr or data_wr)
75 if (ce) begin
76 if (wr) begin
77 mem[addr] = data_wr;
78 end
79 if (rd) begin
80 data_rd = mem[addr];
81 end
82 end
83
84 endmodule
You could download file bus_wr_rd_task.v here
Simulation Output
Function
A Verilog HDL function is the same as a task, with very little differences, like function
cannot drive more than one output, can not contain delays.
• functions are defined in the module in which they are used. It is possible to define
functions in separate files and use compile directive 'include to include the
function in the file which instantiates the task.
• functions can not include timing delays, like posedge, negedge, # delay, which
means that functions should be executed in "zero" time delay.
• functions can have any number of inputs but only one output.
• 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.
• functions can take, drive, and source global variables, when no local variables are
used. When local variables are used, basically output is assigned only at the end
of function execution.
• functions can be used for modeling combinational logic.
• functions can call other functions, but can not call tasks.
Syntax
• A function begins with keyword function and ends with keyword endfunction
• inputs are declared after the keyword function.
1 module simple_function();
2
3 function myfunction;
4 input a, b, c, d;
5 begin
6 myfunction = ((a+b) + (c-d));
7 end
8 endfunction
9
10 endmodule
You could download file simple_function.v here
Introduction
There are tasks and functions that are used to generate input and output during
simulation. Their names begin with a dollar sign ($). The synthesis tools parse and ignore
system functions, and hence can be included even in synthesizable models.
These commands have the same syntax, and display text on the screen during simulation.
They are much less convenient than waveform display tools like GTKWave. or Undertow
or Debussy. $display and $strobe display once every time they are executed, whereas
$monitor displays every time one of its parameters changes. The difference between
$display and $strobe is that $strobe displays the parameters at the very end of the current
simulation time unit rather than exactly when it is executed. The format string is like that
in C/C++, and may contain format characters. Format characters include %d (decimal),
%h (hexadecimal), %b (binary), %c (character), %s (string) and %t (time), %m
(hierarchy level). %5d, %5b etc. would give exactly 5 spaces for the number instead of
the space needed. Append b, h, o to the task name to change default format to binary,
octal or hexadecimal.
Syntax
These return the current simulation time as a 64-bit integer, a 32-bit integer, and a real
number, respectively.
$reset resets the simulation back to time 0; $stop halts the simulator and puts it in
interactive mode where the user can enter commands; $finish exits the simulator back to
the operating system.
$scope, $showscope
$random
$random generates a random integer every time it is called. If the sequence is to be
repeatable, the first time one invokes random giving it a numerical argument (a seed).
Otherwise the seed is derived from the computer clock.
These can dump variable changes to a simulation viewer like Debussy. The dump files
are capable of dumping all the variables in a simulation. This is convenient for
debugging, but can be very slow.
Syntax
• $dumpfile("filename.vcd")
• $dumpvar dumps all variables in the design.
• $dumpvar(1, top) dumps all the variables in module top and below, but not
modules instantiated in top.
• $dumpvar(2, top) dumps all the variables in module top and 1 level below.
• $dumpvar(n, top) dumps all the variables in module top and n-1 levels below.
• $dumpvar(0, top) dumps all the variables in module top and all level below.
• $dumpon initiates the dump.
• $dumpoff stop dumping.
• $fopen opens an output file and gives the open file a handle for use by the other
commands.
• $fclose closes the file and lets other programs access it.
• $fdisplay and $fwrite write formatted data to a file whenever they are executed.
They are the same except $fdisplay inserts a new line after every execution and
$write does not.
• $strobe also writes to a file when executed, but it waits until all other operations
in the time step are complete before writing. Thus initial #1 a=1; b=0;
$fstrobe(hand1, a,b); b=1; will write write 1 1 for a and b.
• $monitor writes to a file whenever any of its arguments changes.
Syntax
• handle1=$fopen("filenam1.suffix")
• handle2=$fopen("filenam2.suffix")
• $fstrobe(handle1, format, variable list) //strobe data into filenam1.suffix
• $fdisplay(handle2, format, variable list) //write data into filenam2.suffix
• $fwrite(handle2, format, variable list) //write data into filenam2.suffix all on one
line. Put in the format string where a new line is desired.
Introduction
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. Even though the above facts are well known to most ASIC
engineers, still engineers think that there is no glory in verification.
I have picked up some examples from the VLSI classes that I used to teach during 1999-
2001, when I was in Chennai. Please feel free to give your feedback on how to improve
the tutorial below.
Before you Start
For writing testbenches it is important to have the design specification of "design under
test" or simply DUT. Specs need to be understood clearly and a test plan, which basically
documents the test bench architecture and the test scenarios (test cases) in detail, needs to
be made.
Example - Counter
Let's assume that we have to verify a simple 4-bit up counter, which increments its count
whenever enable is high, and resets to zero when reset is asserted high. Reset is
synchronous to clock.
1 //-----------------------------------------------------
2 // Design Name : counter
3 // File Name : counter.v
4 // Function : 4 bit up counter
5 // Coder : Deepak
6 //-----------------------------------------------------
7 module counter (clk, reset, enable, count);
8 input clk, reset, enable;
9 output [3:0] count;
10 reg [3:0] count;
11
12 always @ (posedge clk)
13 if (reset == 1'b1) begin
14 count <= 0;
15 end else if ( enable == 1'b1) begin
16 count <= count + 1;
17 end
18
19 endmodule
You could download file counter.v here
Test Plan
We will write a 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 the figure below.
DUT is instantiated in the testbench, and the testbench will contain a clock generator,
reset generator, enable logic generator and compare logic, which basically calculates the
expected count value of counter and compares it with the output of counter.
Test Cases
• Reset Test : We can start with reset de-asserted, 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.
We can add some more test cases; but we are not here to test the counter, rather to learn
how to write test benches.
Memory Modeling
To help modeling of memory, Verilog provides support for two dimensions arrays.
Behavioral models of memories are modeled by declaring an array of register variables;
any word in the array may be accessed using an index into the array. A temporary
variable is required to access a discrete bit within the array.
Syntax
Examples
Declaration
Here [7:0] is the memory width and [0:255] is the memory depth with the following
parameters:
my_memory[address] = data_in;
Reading Values
data_out = my_memory[address];
Bit Read
Sometimes there may be need to read just one bit. Unfortunately Verilog does not allow
to read or write only one bit: the workaround for such a problem is as shown below.
data_out = my_memory[address];
data_out_it_0 = data_out[0];
Initializing Memories
A memory array may be initialized by reading memory pattern file from disk and storing
it on the memory array. To do this, we use system tasks $readmemb and $readmemh.
$readmemb is used for binary representation of memory content and $readmemh for hex
representation.
Syntax
$readmemh("file_name",mem_array,start_addr,stop_addr);
1 module memory();
2 reg [7:0] my_memory [0:255];
3
4 initial begin
5 $readmemh("memory.list", my_memory);
6 end
7 endmodule
You could download file memory.v here
$readmemh system task can also be used for reading testbench vectors. I will cover this
in detail in the test bench section ... when I find time.
Refer to the examples section for more details on different types of memories.
Introduction
Let's assume that we have a design which requires us to have counters of various width,
but with the same functionality. Maybe we can assume that we have a design which
requires lots of instants of different depth and width of RAMs of similar functionality.
Normally what we do is creating counters of different widths and then use them. The
same rule applies to the RAM we talked about.
But Verilog provides a powerful way to overcome this problem: it provides us with
something called parameter; these parameters are like constants local to that particular
module.
We can override the default values, either using defparam or by passing a new set of
parameters during instantiation. We call this parameter overriding.
Parameters
1 module secret_number;
2 parameter my_secret = 0;
3
4 initial begin
5 $display("My secret number is %d", my_secret);
6 end
7
8 endmodule
9
10 module defparam_example();
11
12 defparam U0.my_secret = 11;
13 defparam U1.my_secret = 22;
14
15 secret_number U0();
16 secret_number U1();
17
18 endmodule
You could download file defparam_example.v here
1 module secret_number;
2 parameter my_secret = 0;
3
4 initial begin
5 $display("My secret number in module is %d", my_secret);
6 end
7
8 endmodule
9
10 module param_overide_instance_example();
11
12 secret_number #(11) U0();
13 secret_number #(22) U1();
14
15 endmodule
You could download file param_overide_instance_example.v here
1 module ram_sp_sr_sw (
2 clk , // Clock Input
3 address , // Address Input
4 data , // Data bi-directional
5 cs , // Chip Select
6 we , // Write Enable/Read Enable
7 oe // Output Enable
8 );
9
10 parameter DATA_WIDTH = 8 ;
11 parameter ADDR_WIDTH = 8 ;
12 parameter RAM_DEPTH = 1 << ADDR_WIDTH;
13 // Actual code of RAM here
14
15 endmodule
You could download file param_more_then_one.v here
When instantiating more than the one parameter, parameter values should be passed in
the order they are declared in the sub module.
In Verilog 2001, the code above will work, but the new feature makes the code more
readable and error free.
As you must have experienced in college, everything (all the digital circuits) is designed
manually. Draw K-maps, optimize the logic, draw the schematic. This is how engineers
used to design digital logic circuits in early days. Well this works fine as long as the
design is a few hundred gates.
High-level design is less prone to human error because designs are described at a higher
level of abstraction. High-level design is done without significant concern about design
constraints. Conversion from high-level design to gates is done by synthesis tools, using
various algorithms to optimize the design as a whole. This removes the problem with
varied designer styles for the different blocks in the design and suboptimal designs. Logic
synthesis tools allow technology independent design. Design reuse is possible for
technology-independent descriptions.
What do we discuss here ?
When it comes to Verilog, the synthesis flow is the same as for the rest of the languages.
What we try to look in next few pages is how particular code gets translated to gates. As
you must have wondered while reading earlier chapters, how could this be represented in
Hardware ? An example would be "delays". There is no way we could synthesize delays,
but of course we can add delay to particular signals by adding buffers. But then this
becomes too dependent on synthesis target technology. (More on this in the VLSI
section).
First we will look at the constructs that are not supported by synthesis tools; the table
below shows the constructs that are supported by the synthesis tool.
Introduction
The function invoked in Verilog code is called a system call. An example of a built-in
system call is $display, $stop, $random. PLI allows the user to create custom system
calls, something that Verilog syntax does not allow us to do. Some of these are:-
• Power analysis.
• Code coverage tools.
• Modifying the Verilog simulation data structure - more accurate delays.
• Custom output displays.
• Co-simulation.
• Design debug utilities.
• Simulation analysis.
• C-model interface to accelerate simulation.
• Testbench modeling.
To achieve these applications of PLI, C code should have access to the internal data
structure of Verilog simulator. To facilitate this Verilog PLI provides something called
acc routines or simply access routines.
There is a second set of routines, which are called tf routines, or simply task and function
routines. The tf and acc are PLI 1.0 routines and are very vast and very old. The next set
of routines, which was introduced with the latest release of Verilog 2001 is called vpi
routines. These are small and crystal clear PLI routines and thus the new version PLI 2.0.
You can get Verilog 2001 LRM or PLI 1.0 IEEE document for details of each and every
function provided. Verilog IEEE LRMs are written in such a way that anyone with
hardware background can understand. If you are unable to get hold of the above IEEE
docs, then you can buy the PLI books listed in books section.
How it Works
We will define a function hello, which when called will print "Hello Deepak". This
example does not use any of the PLI standard functions (ACC, TF and VPI). For exact
linking details, please refer to simulator manuals. Each simulator implements its own way
for linking C/C++ functions to simulator.
C Code
1 #include <stdio.h>
2
3 void hello () {
4 printf ("\nHello Deepak\n");
5 }
You could download file hello.c here
Verilog Code
Once linking is done, simulation is run as a normal simulation as we saw earlier, with
slight modification to the command line options: we need to tell the simulator that we are
using PLI (Modelsim needs to know which shared objects to load in the command line).
Introduction
Well most of the changes in Verilog 2001 are picked from other languages, like generate,
configuration, file operation were from VHDL. I am just adding a list of the most
commonly used Verilog 2001 changes. You may need a simulator with Verilog 2001
support for testing the examples listed below.
In earlier version of Verilog ,we used to use 'or' to specify more than one sensitivity list
element. In the case of Verilog 2001, we use comma as shown in the example below.
1 module comma_example();
2
3 reg a, b, c, d, e;
4 reg [2:0] sum, sum95;
5
6 // Verilog 2k example for usage of comma
7 always @ (a, b, c, d, e)
8 begin : SUM_V2K
9 sum = a + b + c + d + e;
10 end
11
12 // Verilog 95 example for above code
13 always @ (a or b or c or d or e)
14 begin : SUM_V95
15 sum95 = a + b + c + d + e;
16 end
17
18 initial begin
19 $monitor ("%g a=%b b=%b c=%b d=%b e=%b sum=%b sum95=%b",
20 $time, a, b, c, d, e, sum, sum95);
21 #1 a = 1;
22 #1 b = 1;
23 #1 c = 1;
24 #1 d = 1;
25 #1 e = 1;
26 #1 $finish;
27 end
28
29 endmodule
You could download file comma_example.v here
We can use the same for edge sensitive code also, as shown in the code below.
1 module comma_edge_example();
2
3 reg clk, reset, d;
4 reg q, q95;
5
6 // Verilog 2k example for usage of comma
7 always @ (posedge clk, posedge reset)
8 begin : V2K
9 if (reset) q <= 0;
10 else q <= d;
11 end
12
13 // Verilog 95 example for above code
14 always @ (posedge clk or posedge reset)
15 begin : V95
16 if (reset) q95 <= 0;
17 else q95 <= d;
18 end
19
20 initial begin
21 $monitor ("%g clk=%b reset=%b d=%b q=%b q95=%b",
22 $time, clk, reset, d, q, q95);
23 clk = 0;
24 reset = 0;
25 d = 0;
26 #4 reset = 1;
27 #4 reset = 0;
28 #1 d = 1;
29 #10 $finish;
30 end
31
32 initial #1 forever clk = #1 ~clk;
33
34 endmodule
You could download file comma_edge_example.v here
Verilog 2001 allows us to use star in sensitive list instead of listing all the variables in
RHS. This removes typo mistakes and thus avoids simulation and synthesis mismatches.
1 module star_example();
2
3 reg a, b, c, d, e;
4 reg [2:0] sum, sum95;
5
6 // Verilog 2k example for usage of star for combo logic
7 always @ (*)
8 begin : SUM_V2K
9 sum = a + b + c + d + e;
10 end
11
12 // Verilog 95 example for above code
13 always @ (a or b or c or d or e)
14 begin : SUM_V95
15 sum95 = a + b + c + d + e;
16 end
17
18 initial begin
19 $monitor ("%g a=%b b=%b c=%b d=%b e=%b sum=%b sum95=%b",
20 $time, a, b, c, d, e, sum, sum95);
21 #1 a = 1;
22 #1 b = 1;
23 #1 c = 1;
24 #1 d = 1;
25 #1 e = 1;
26 #1 $finish;
27 end
28
29 endmodule
You could download file star_example.v here
In Verilog 1995, default data type is net and its width is always 1 bit. In Verilog 2001 the
width is adjusted automatically.
In Verilog 2001, we can disable default data type by `default net_type none, This
basically helps in catching the undeclared wires.
Register data type is called variable, as it created a lot of confusion for beginners. Also it
is possible to specify an initial value for the register/variable data type. Reg data type can
also be declared as signed.
1 module v2k_reg();
2
3 // v2k allows to init variables
4 reg a = 0;
5 // Here only last variable is set to 0, i.e d = 0
6 // Rest b, c are set to x
7 reg b, c, d = 0;
8 // reg data type can be signed in v2k
9 // We can assign with signed constants
10 reg signed [7:0] data = 8'shF0;
11
12 // Function can return signed values
13 // Its ports can contain signed ports
14 function signed [7:0] adder;
15 input a_in;
16 input b_in;
17 input c_in;
18 input signed [7:0] data_in;
19 begin
20 adder = a_in + b_in + c_in + data_in;
21 end
22 endfunction
23
24 endmodule
You could download file v2k_reg.v here
New operators
Verilog 2001 introduced two new operator that are of interest to designers. They are
1 module signed_shift();
2
3 reg [7:0] unsigned_shift;
4 reg [7:0] signed_shift;
5 reg signed [7:0] data = 8'hAA;
6
7 initial begin
8 unsigned_shift = data >> 4;
9 $display ("unsigned shift = %b", unsigned_shift);
10 signed_shift = data >>> 4;
11 $display ("signed shift = %b", signed_shift);
12 unsigned_shift = data << 1;
13 $display ("unsigned shift = %b", unsigned_shift);
14 signed_shift = data <<< 1;
15 $display ("signed shift = %b", signed_shift);
16 $finish;
17 end
18
19 endmodule
You could download file signed_shift.v here
Power operator
1 module power_operator();
2
3 reg [3:0] base = 2;
4 reg [5:0] exponent = 1;
5 reg [31:0] result = 0;
6
7 initial begin
8 $monitor ("base = %d exponent = %d result = %d", base, exponent,
result);
9 repeat (10) begin
10 #1 exponent = exponent + 1;
11 end
12 #1 $finish;
13 end
14
15 always @ (*)
16 begin
17 result = base ** exponent;
18 end
19
20 endmodule
You could download file power_operator.v here
Port Declaration
Verilog 2001 allows port direction and data type in the port list of modules as shown in
the example below.
1 module ansiport_example();
2
3 reg read,write = 0;
4 reg [7:0] data_in = 0;
5 reg [3:0] addr = 0;
6 wire [7:0] data_v95, data_notype, data_ansi;
7
8 initial begin
9 $monitor (
10 "%g rd=%b wr=%b addr=%b data_in=%h data_v95=%h data_notype=%h
data_ansi=%h"
11 , $time, read, write, addr, data_in, data_v95, data_notype,
data_ansi);
12 #1 read = 0; // why only for read ?
13 #3 repeat (16) begin
14 data_in = $random;
15 write = 1;
16 #1 addr = addr + 1;
17 end
18 write = 0;
19 addr = 0;
20 #3 repeat (16) begin
21 read = 1;
22 #1 addr = addr + 1;
23 end
24 read = 0;
25 #1 $finish;
26 end
27
28 memory_v95 U (read, write, data_in, addr, data_v95);
29 memory_ansi_notype W (read, write, data_in, addr, data_notype);
30 memory_ansi V (read, write, data_in, addr, data_ansi);
31
32 endmodule
33 // Verilog 95 code
34 module memory_v95 ( read, write, data_in, addr, data_out);
35 input read;
36 input write;
37 input [7:0] data_in;
38 input [3:0] addr;
39 output [7:0] data_out;
40
41 reg [7:0] data_out;
42 reg [7:0] mem [0:15];
43
44 always @ (*)
45 begin
46 if (write) mem[addr] = data_in;
47 end
48
49 always @ (read, addr)
50 begin
51 if (read) data_out = mem[addr];
52 end
53
54 endmodule
55
56 // Verilog 2k with notype in port list
57 module memory_ansi_notype (
58 input read,
59 input write,
60 input [7:0] data_in,
61 input [3:0] addr,
62 output [7:0] data_out
63 );
64 reg [7:0] mem [0:15];
65
66 always @ (*)
67 begin
68 if (write) mem[addr] = data_in;
69 end
70
71 assign data_out = (read) ? mem[addr] : 0;
72
73 endmodule
74
75 // Verilog 2k with width and data type listed
76 module memory_ansi (
77 input wire read,
78 input wire write,
79 input wire [7:0] data_in,
80 input wire [3:0] addr,
81 output reg [7:0] data_out
82 );
83
84 reg [7:0] mem [0:15];
85
86 always @ (*)
87 begin
88 if (write) mem[addr] = data_in;
89 end
90
91 always @ (read, addr)
92 begin
93 if (read) data_out = mem[addr];
94 end
95
96 endmodule
Verification with assertions refers to the use of an assertion language to specify expected
behavior in a design, and of tools that evaluate the assertions relative to the design under
verification.
Assertion-based verification is mostly useful to design and verification engineers who are
responsible for the RTL design of digital blocks and systems. ABV lets design engineers
capture verification information during design. It also enables internal state, datapath, and
error precondition coverage analysis.
Simple example of assertion could be a FIFO: whenever a FIFO is full and a write
happens, it is illegal. So a FIFO designer can write an assertion which checks for this
condition and asserts failure.
Assertions Languages
Currently there are multiple ways available for writing assertions as shown below.
Most assertions can be written in HDL, but HDL assertions can be lengthy and
complicated. This defeats the purpose of assertions, which is to ensure the correctness of
the design. Lengthy, complex HDL assertions can be hard to create and subject to bugs
themselves.
In this tutorial we will be seeing verilog based assertion (OVL) and PSL (sugar).
• Testing internal points of the design, thus increasing observability of the design.
• Simplifying the diagnosis and detection of bugs by constraining the occurrence of
a bug to the assertion monitor being checked.
• Allowing designers to use the same assertions for both simulation and formal
verification.
Assertion monitors address design verification concerns and can be used as follows to
increase design confidence.
• Combine assertion monitors to increase the coverage of the design (for example,
in interface circuits and corner cases).
• Include assertion monitors when a module has an external interface. In this case,
assumptions on the correct input and output behavior should be guarded and
verified.
• Include assertion monitors when interfacing with third party modules, since the
designer may not be familiar with the module description (as in the case of IP
cores), or may not completely understand the module. In these cases, guarding the
module with assertion monitors may prevent incorrect use of the module.
Normally assertions are implemented by the designers to safeguard their design, so they
code the assertions into their RTL. A simple example of an assertion would be: writing
into FIFO, when it is full. Traditionally verification engineers have been using assertions
in their verification environments without knowing that they are assertions. For
verification a simple application of assertions would be checking protocols. Example:
expecting the grant of an arbiter to be asserted after one clock cycle and before two
cycles after the assertion of request.
In the next few pages we will see simple examples on usage of assertions using Open
Verification Library and PSL assertions.
For using Open Verification Library examples you need Open Verification Library from
Accellera. For running PSL examples you need a simulator that can support PSL.
Then you need a bit of patience to go through the manuals to learn in details Assertions
and try out more examples.
space.gif
../images/main/bullet_green_ball.gif Verilog Quick Reference
This is still in very early stage, need time to add more on this.
space.gif
../images/main/bulllet_4dots_orange.gif MODULE
module MODID[({PORTID,})];
[{declaration}]
[{parallel_statement}]
[specify_block]
endmodule
space.gif
../images/main/bulllet_4dots_orange.gif DECLARATIONS
real {REALID,};
realtime {REALTIMID,};
event {EVTID,};
task TASKID;
[{declaration}]
begin
[{sequential_statement}]
end
endtask
[{declaration}]
begin
[{sequential_statement}]
end
endfunction
space.gif
../images/main/bulllet_4dots_orange.gif PARALLEL STATEMENTS
initial sequential_statement
always sequential_statement
([{expr,} | {.PORTID(expr),}]);
[INSTID] ({expr,});
space.gif
../images/main/bulllet_4dots_orange.gif GATE PRIMITIVES
pullup (out);
pulldown (out);
space.gif
../images/main/bulllet_4dots_orange.gif SEQUENTIAL STATEMENTS
begin[: BLKID
[{declaration}]]
[{sequential_statement}]
end
if (expr) sequential_statement
[else sequential_statement]
[{{expr,}: sequential_statement}]
[default: sequential_statement]
endcase
forever sequential_statement
repeat (expr) sequential_statement
sequential_statement
-> EVENTID;
fork[: BLKID
[{declaration}]]
[{sequential_statement}]
join
TASKID[({expr,})];
deassign lvalue;
lvalue ::=
space.gif
../images/main/bulllet_4dots_orange.gif SPECIFY BLOCK
{specify_statement}
endspecify
space.gif
../images/main/bulllet_4dots_orange.gif SPECIFY BLOCK STATEMENTS
[&&& scalar_expr]
path_delay ::=
expr | (expr, expr [, expr [, expr, expr, expr]])
space.gif
../images/main/bulllet_4dots_orange.gif EXPRESSIONS
primary
unop primary
primary ::=
space.gif
space.gif
../images/main/bulllet_4dots_orange.gif UNARY OPERATORS
+, - Positive, Negative
! Logical negation
~ Bitwise negation
space.gif
../images/main/bulllet_4dots_orange.gif BINARY OPERATORS
Increasing precedence:
?: if/else
|| Logical or
| Bitwise or
+, - Addition, Subtraction
unsized constant 32
op i +, -, ~ L(i)
i ? j : k max(L(j), L(k))
{i{j,...k}} i * (L(j)+...+L(k))
i = j L(i)
space.gif
../images/main/bullet_green_ball.gif SYSTEM TASKS
* indicates tasks not part of the IEEE standard
space.gif
../images/main/bulllet_4dots_orange.gif INPUT
space.gif
../images/main/bulllet_4dots_orange.gif OUTPUT
$display[defbase]([fmtstr,] {expr,});
fileno = $fopen("filename");
$fclose(fileno);
defbase ::= h | b | o
space.gif
../images/main/bulllet_4dots_orange.gif TIME
space.gif
../images/main/bulllet_4dots_orange.gif SIMULATION CONTROL
$stop Interrupt
$finish Terminate
$save("fn") Save current simulation
Variables to dump
space.gif
../images/main/bulllet_4dots_orange.gif MISCELLANEOUS
$random[(ID)]
space.gif
../images/main/bulllet_4dots_orange.gif ESCAPE SEQUENCES IN
FORMAT STRINGS
%% character "%"
space.gif
../images/main/bulllet_4dots_orange.gif LEXICAL ELEMENTS
base ::= b | o | d | h
comment ::= // comment newline
space.gif