Академический Документы
Профессиональный Документы
Культура Документы
This document uses short examples to demonstrate the basic Verilog syntax, time delays, and
concurrent execution features. We have tried to condense all the interesting and hard parts of
Verilog into 15 pages and skip all of the boring stuff like the what is the difference between real
and integer data types. Studying this document will enable you to model circuits using simple
structural and behavioral Verilog code and provide a solid framework for learning all the details
of the language.
If Verilog is a new language for you we recommend getting a copy of "Verilog HDL" by Samir
Plantikar. It is a well organized Verilog textbook that is filled with lots of examples.
Most Verilog and VHDL books begin with several chapters describing the language's history and
advantages. But the arguments boil down to these:
HDL simulators are better then gate level simulators for 2 reasons: portable
model development, and the ability to design complicated test benches that react
to outputs from the model under test. Finding a model for a unique component for
your particular gate level simulator can be a frustrating task, with an HDL
language you can always write your own model. Also most gate level simulators
are limited to simple waveform based test benches which complicates the testing
of bus and microprocessor interface circuits.
Verilog is a great low level language. Structural models are easy to design and
Behavioral RTL code is pretty good. The syntax is regular and easy to
remember. It is the fastest HDL language to learn and use. However Verilog
lacks user defined data types and lacks the interface-object separation of the
VHDL's entity-architecture model.
VHDL is good for designing behavioral models and incorporates some of the
modern object oriented techniques. It's syntax is strange and irregular, and the
language is difficult to use. Structural models require a lot of code that interferes
with the readability of the model.
C++as an hardware modeling language is excellent choice for high-level
behavioral analysis of a system (like evaluating different data flow architectures
in a microprocessor). However C++ lacks the basic hardware concepts like
knowledge of strengths, connections, and concurrent execution which
complicates model generation for lower level simulations.
Choosing Verilog, VHDL, or C++ will be based on availability of tools, models, and in-house
expertise. If you are just learning your first HDL language we recommend Verilog because you
will be able to quickly become comfortable with the syntax and timing issues of the language:
Chapter 2: Verilog Structure
This chapter gives a quick overview of the structural and behavioral elements of
Verilog and discusses the time flow and time control statements. Verilog differs from
regular programming languages (C, Pascal, ...) in 3 main aspects: (1) simulation time
concept, (2) multiple threads, and (3) some basic circuit concepts like network
connections and primitive gates. If you know how to program in C and you
understand basic digital design then learning Verilog will be easy.
If you have VeriLogger Pro installed on your computer you can copy and paste the
following code segments into a text file and simulate the examples.
2.1 Modules
In Verilog, circuit components are designed inside a module. Modules can contain
both structural and behavioral statements. Structural statements represent circuit
components like logic gates, counters, and microprocessors. Behavioral level
statements are programming statements that have no direct mapping to circuit
components like loops, if-then statements, and stimulus vectors which are used to
exercise a circuit.
Figure 1 shows an example of a circuit and a test bench module. A module starts with
the keyword module followed by an optional module name and an optional port list.
The key word endmodule ends a module.
`timescale 1ns / 1ps
//create a NAND gate out of an AND and an Invertor
module some_logic_component (c, a, b);
// declare port signals
output c;
input a, b;
// declare internal wire
wire d;
//instantiate structural logic gates
and a1(d, a, b); //d is output, a and b are inputs
not n1(c, d); //c is output, d is input
endmodule
2.2 Structural Design with Gate Primitives and the Delay operator
Verilog defines some basic logic gates as part of the language. In Figure 1, module
some_logic_component instantiates two gate primitives: the not gate and the and gate.
The output of the gate is the first parameter, and the inputs are the rest of the
parameters. These primitives are scaleable so you can get multiple input gates just by
adding inputs into the parameter list. For example:
nand a1(out1, in1, in2); //2-input NAND gate
nand a2(out1, in1, in2, in3, in4, in5); //5-input NAND gate
By default the timing delay for the gate primitives is zero time. You can define the
rising delay, falling delay using the #(rise, fall) delay operator. And for tri-state gates
you can also define the turn-off delay (transition to high impedance state Z) by using
the #(rise, fall, off) delay operator. For example
notif0 #(10,11,27) inv2(c,d,control) //rise=10, fall=11, off=27(not if
control=0)
nor #(10,11) nor1(c,a,b); //rise=10, fall=11 (nor gate)
xnor #(10) xnor1(i,g,h); //rise=10, fall=10 (xnor gate)
Also each of the 3 delays can be defined to have minimum, typical, and a maximum
value using the a colon to separate the values like 8:10:12 instead of 10 in the above
examples. At run time, the Verilog simulator looks for to see if
the +mindelay, +typdelay, or +maxdelay option has been defined so that it will
know which of the 3 time values to use. In VeriLogger these options are set using
the Project > Project Preferences menu. If none of the options are specified then the
typical value is used.
// min:typ:max values defined for the (rise, fall) delays
or #(8:10:12, 10:11:13) or1(c,a,b);
The delay operator has one subtle side effect: it swallows narrow input pulses.
Normally, the delay operator causes the output response of a gate to be delayed a
certain amount of time. However if the input pulse width is shorter then the overall
delay of the gate then the change will not be shown on the output.
If you have a lot of random logic, the gate primitives of the previous section are
tedious to use because all the internal wires must be declared and hooked up correctly.
Sometimes it is easier to just describe a circuit using a single Boolean equation. In
Verilog, Boolean equations which have similar timing properties as the gate
primitives are defined using a continuous assignment statement.
Assignments can also be made during the declaration of a wire. In this case the assign
keyword is implicitly assumed to be there for example:
wire d;
assign d = a || b; //continuous assignment
wire d = a || b; //implicit continuous assignment
By default the timing delay for assignment statements is zero time. You can define a
propagation delay using the #delay operator just like we did for the gate primitives.
The following examples have the exact same timing.
wire c;
assign #5 c = a && b; //delay in the continuous assignment
To demonstrate the pulse swallowing effect of the delays operator, consider the
following senario. In the above examples, if input a changed value at time 10 (and
held its value for at least 5 time units), then the output c would change values at time
15. If input a had a value pulse that was shorter then the propagation delay of the
assignment then the value on a would not be passed to the output.
The delay operator can also use the full rise, fall, and off delays and each delay can
have a minimum:typical: maximum value. The following is a valid line of code.
and #(8:10:12, 10:11:13, 26:27:29) a1(c,a,b); //min:typ:max of (rise,fall,off)
Appendix A defines all of the operators that can be used in an assignment statement.
By default the timing inside a module is controlled by the module itself. However,
modules can be defined to have parameterized delays similar to the #(4,5) delay
operator used with gate primitives. In the module definition, use the parameter
keyword to create delay variables. Parameters can also be used to change other scalar
values in the module. When the module is instantiated then you can choose to
override the delay values using the #(parameter) notation. For example:
module some_logic_component (c, a, b);
... //some code
parameter andDelay = 2; //default delays
parameter invDelay = 2;
and #andDelay a1(d, a, b); //using parameter delays
not #invDelay n1(c, d);
endmodule
Modules also support a special kind of timing called specify blocks which can be used
in conjunction with SDF analyzers. Specify blocks also support continuous setup and
hold checking.
Behavioral code is used to describe circuits at a more abstract level then the structural
level statements we have studied. All Behavioral code occurs within either an initial
block or in an always block. A module can contain several initial and always blocks.
These behavioral blocks contain statements that control simulation time, data flow
statements (like if-then and case statements), and blocking and non-blocking
statements.
An initial block executes once during a simulation. Initial blocks are usually used to
initialize variables and to describe stimulus waveforms which exercise which drive
the simulation.
During a simulation each always and each initial block begin to execute at time zero.
Each block executes concurrently with each structural statement and all the other
behavioral blocks. The following example shows a behavioral SRAM model. The
initial block sets the memory cells to zero at startup. The always block executes each
time there is a change on the write control line, the chip select line, or the address bus.
As an exercise, copy and paste this code into a verilog file and write a test bench to
exercise the model. If you are using VeriLogger Pro then you can draw a test bench.
//SRAM Model
module sram(CSB,WRB,ABUS,DATABUS);
input CSB; // active low chip select
input WRB; // active low write control
input [11:0] ABUS; // 12-bit address bus
inout [7:0] DATABUS; // 8-bit data bus
//** internal signals
reg [7:0] DATABUS_driver;
wire [7:0] DATABUS = DATABUS_driver;
reg [7:0] ram[0:4095]; // memory cells
integer i;
Verilog supports structural data types called nets which model hardware connections
between circuit components. The two most common structural data types
arewire and reg. The wire nets act like real wires in circuits. The reg type hold their
values until another value is put on them, just like a register hardware component. The
declarations for wire and reg signals are inside a module but outside any initial or
always block. The initial state of a reg is x unknown, and the initial state of awire is z.
Ports:Modules communicate with each other through ports, the signals listed in the
parameter list at the top of the module. Ports can be of type in, out, and inout.
Here are 3 simplistic rules for matching the structural data type to the type of port:
1. Use reg as the outputs of Behavioral blocks. If you us a wire then the value will
never be seen by other blocks.
2. Use wire for all inputs, inouts, and most outputs of Structural elements.
3. If you need a special strength type operation use special net keyword wand, wor,
tir, triand, trior, trireg.
The data type time can hold a special simulator value called simulation time which is
extracted from the system function $time. The time information can be used to help
you debug your simulations.
The size is always specified as a decimal number. If no is specified then the default
size is at least 32bits and may be larger depending on the machine. Valid base formats
are 'b , 'B , 'h , 'H 'd , 'D , 'o , 'O for binary, hexadecimal, decimal, and octal.
Numbers consist of strings of digits (0-9, A-F, a-f, x, X, z, Z). The X's mean
unknown, and the Z's mean high impedance If no base format is specified the number
is assumed to be a decimal number. Some examples of valid numbers are:
2'b10 // 2 bit binary number
'b10 // at least a 32-bit binary number
3 // at least a 32-bit decimal number
8'hAf // 8-bit hexadecimal
-16'd47 // negative decimal number
There are 2 kinds of assignment statements: blocking using the = operator, and non-
blocking using the <= operator. Blocking assignments act like sequential code
statements and execute when they are called. Non-blocking schedule events to happen
at some time in the future. This can be confusing because lines that appear after a non-
blocking statement execute at the same time as the non-blocking statement. Here are
some examples:
#5 x = 1'b0; // blocks for 5 time units, applies value to x, then next line
goes
y = 1'b1; // blocks, sets y to 1 now, then next statement goes
y <= #3 1'b0; // evaluates now, schedules apply y=0 in 3 time units, and next
line goes
#5 x <= y; // waits for 5 time units, evaluates,
// schedules apply at end of current time, and next line
goes
Verilog supports three similar data structures called Arrays, Vectors, and Memories.
Arrays are used to hold several objects of the same type. Vectors are used to represent
multi-bit busses. And Memories are arrays of vectors which are accessed similar to
hardware memories. Read the following examples to determine how to reference and
use the different data structures.
//*** Arrays for integer, time, reg, and vectors of reg ***************
integer i[3:0]; //integer array with a length of 4
time x[20:1]; //time array with length of 19
reg r[7:0]; //scalar reg array with length of 8
3.7 Operators
Here is a small selection of the Verilog Operators which look similar but have
different effects. Logical Operators evaluate to TRUE or FALSE. Bitwise operators
act on each bit of the operands to produce a multi-bit result. Unary Reduction
operators perform the operation on all bits of the operand to produce a single bit
result.
Step 1: Create RTL Design Models and Behavioral Test Bench Code
To program the FPGA, you’ll need to write RTL-level modules that describe the logic for
processing image data from the scanner and model the ports that can be read and written to by
the PC via the PCI bus. At the end of this process, let’s assume you have generated 3 Verilog
files that model your FPGA design:
pci_interface.v (models ports and communication across PCI bus)
ocr_processor.v (communicates with scanner and performs character recognition)
fpga.v (top-level module of fpga design that instantiates modules defined in pci_interface.v and
ocr_processor.v)
To test your design code, you’ll need to write a behavioral model that generates data in the
format provided by the scanner (e.g. models IO from scanner). And you’ll need to write a bus-
functional model (behavioral model that models the bus level IO) of the PC that to test the PCI
interface of your board. Let’s assume you create the following testbench files:
pci_stimulator.v (bus-functional IO model of the PC that reads/writes FPGA ports)
scanner.v (generates scanner input to feed the FPGA)
testbench.v (top level test bench that instantiates the pci_stimulator, the scanner, and the FPGA
top level module, plus any glue logic we’ve included in the design)