Use of Verilog in CS/ECE 552
In this class, you are going to be using the Verilog
language to represent large portions of the design of your projects.
Verilog is a powerful language. It is primarily a
programming language, originally designed to write simulations of
hardware. One can write in Verilog at many levels. At one extreme,
you can describe individual transistors, or represent the delay of a
signal passing along a single wire. At the other extreme, you could
write a function that mathematically calculates a cosine, without any
clue how cosine-calculating hardware would be designed.
We are not intending to teach all facets of Verilog.
For this class, you only need to do one thing: represent the hardware
that you are designing. For this, you only need a subset of the
language. This document will describe how to write a module using
this subset, and give examples to follow.
A requirement of the
assignments in this class is that you only
use the subset of Verilog described here. You will lose points if you
use disallowed statement types. If in doubt, ask the TA.
There are a couple of reasons for this restriction.
First, it will avoid some pitfalls in Verilog which can lead to
hard-to-find bugs. (I have seen Verilog designs which simulate
correctly or incorrectly, basically at random, depending on what
order certain assignments happen to execute in.) Secondly, designing
with this style will tend to produce good designs which synthesize
well; for this reason you may be required to follow some rules such
as this when you have a job in industry. Thirdly, these rules
encourage you to "think like hardware" -- there are no
"for" loops in hardware, just logic and connections and
modules. As soon as you are tempted to put in a "for" loop,
you are trying to write software!
You may be surprised, then, to see "for"
loops and "if" statements in some of the modules you are
supplied with for this class. These statements have a place in the
simulation infrastructure that we use to test a design. For example,
the memory module has a loop to print out its contents to a file
after the simulation is done. This is software! We will supply
you with a basic infrastructure so that your design can be free of
this sort of thing.
This document is not a substitute for a Verilog
reference. You may want to refer to on-line resources such as: ASIC
World Verilog Quick Reference Another
reference
Logic and State The most fundamental thing to keep
in mind is that your hardware design will be composed of two things:
combinational logic, and state. For any moderately complex logic
design, state must be handled in an organized way. An R-S flipflop
would get you laughed out the door of a computer design company! In
our designs, state will all be held in D-flipflops that are all
clocked on the rising edge of a common system clock. You are provided
here with a module for a single
D-flipflop with synchronous reset. Instantiate copies of this module
to build larger registers. Do not create your own flipflops in some
other way. Combinational logic will be specified primarily with
"assign" statements. The statement "assign xyz = a &
b;" specifies an AND gate. An assign statements can get long and
complex, and can specify hundreds of gates, but it always represents
some set of flow-through logic that generates the value for a wire
(or bundle of wires). Your design will be hierarchical. That
means you will write Verilog modules for all the small building
blocks of your design, and you will instantiate these modules within
larger modules. For example, if you want to increment a number, do
not create new increment logic each time; write increment modules of
whatever sizes are needed and call them from your other modules. Most
of your design should consist of calls to other modules.
The modules you write should have a structure like
this:
module my_module_name
(param, param,
... param); input param; output
param; wire
[msb:lsb]
signalName; assign
signalName =
expression; modulename
instance (param, param...); //
call some submodule endmodule
First, we name the
module and list all its input and output parameters (in order).
Second, we list all the input and output parameters all over again,
giving their sizes, e.g. input
[7:0] firstByte; Put the input and
output statements in the same order that the parameters appear in the
module statement. Verilog does not require it, but good practice (and
this class) require that you do so. The assign statements specify
all logic that is to be done in this module. Care should be taken to
format it so that it is readable; use liberal whitespace and consider
lining up similar logic into columns. Note that assign statements
can operate on an entire "vector" of bits at one time.
Sometimes this involves making multiple copies of a 1-bit wire in
order to interact with the vectors. For example:
wire [15:0] a, b, ss, ans; assign ss =
{16{s}}; // use 16 copies of "s" assign ans = (ss ^ a)
| b;
The assign statements
should only contain boolean logic. Do not use expressions that
require creativity on the part of the compiler! Specifically, you
are not permitted to use:
addition,
subtraction, multiplication, division, or modulo
You don't know what
you'll get. Will your adder be ripple-carry or carry-lookahead? And
who knows what sort of divider circuit you'd end up with!
In this class, you are responsible for knowing
how to design such things; leaving their design unspecified is
equivalent to not completing the assignment.
You are allowed to
use the following in assign statements:
reduction (wide AND
and OR gates; e.g. &(a[31:0]) Most of your logic will done
in assign statements (either in the module in question or in some
submodule or sub-submodule...). There is one exception: the "case"
statement. Although a case statement is a fairly high-level
construct, it is an extremely useful way of representing muxes,
next-state evaluation, and other common types of logic. The case
statement will be the only situation in this class where you will
use statements such as begin, end, reg, always. Look at this
example: wire [1:0] s; reg
out; always @* case (s) 2'b00 : out = i0; 2'b01 : out =
i1; 2'b10 : out = i2; 2'b11 : out = i3; endcase For
this class, you must follow these rules:
Completely specify
all possible combinations of the select inputs. Always use a
"default:" clause. It must specify an error output. Even
though the default clause should never be hit, this is the guarantee
that if you've missed something, it will assert an error wire. OR
together all your error wires, and at the top level of the design
run them into the "err" input of the clock/reset module.
This will stop the simulation if you hit any error case.
wire inputA, inputB, goBack; wire [2:0]
currentState; reg [2:0] newState; reg out, err; always
@(goBack or currentState or inputA or inputB) casex ({goBack,
currentState, inputA, inputB}) 6'b1_???_?_? : begin out = 0;
newState = 3'b000; err=0; end 6'b0_000_0_? : begin out = 0;
newState = 3'b000; err=0; end 6'b0_000_1_? : begin out = 0;
newState = 3'b001; err=0; end 6'b0_001_1_? : begin out = 0;
newState = 3'b001; err=0; end 6'b0_001_0_0 : begin out = 0;
newState = 3'b010; err=0; end 6'b0_001_0_1 : begin out = 0;
newState = 3'b011; err=0; end 6'b0_010_?_0 : begin out = 0;
newState = 3'b010; err=0; end 6'b0_010_?_1 : begin out = 0;
newState = 3'b011; err=0; end 6'b0_011_?_1 : begin out = 0;
newState = 3'b011; err=0; end 6'b0_011_?_0 : begin out = 0;
newState = 3'b100; err=0; end 6'b0_100_?_? : begin out = 1;
newState = 3'b000; err=0; end 6'b0_101_?_? : begin out = 0;
newState = 3'b000; err=1; end 6'b0_110_?_? : begin out = 0;
newState = 3'b000; err=1; end 6'b0_111_?_? : begin out = 0;
newState = 3'b000; err=1; end default: begin out = 0; newState =
3'b000; err=1; end endcase
Other things you may
use:
parameter - use this for constants that
change from instance to instance of a module. Everything
that has not been mentioned above should not be necessary for
specifying a computer design. (If you feel that something has been
missed and should be included, contact me.) Some features of the
language, such as functions, are not necessarily bad, but are
unnecessary for completing the assignment and so should not be used.
The following are not
allowed in assignments and will result in points off:
attribute buf bufif0 bufif1 casez cmos
deassign disable edge else endattribute endfunction endprimitive
endspecify endtable endtask event for forever fork function highz0
highz1 if ifnone initial inout integer join medium large macromodule
negedge nmos notif0 notif1 pmos posedge primitive pull0 pull1
pulldown pullup rcmos real realtime release repeat rnmos rpmos rtran
rtranif0 rtranif1 scalared signed small specify specparam strength
strong0 strong1 supply0 supply1 table task time tran tranif0 tranif1
tri tri0 tri1 triand trior trireg unsigned vectored wait wand weak0
weak1 while wor === !== Also not
allowed: Any notion of time or delays; any real numbers. Force
statements are OK for debugging but must not appear in a finished
assignment.
A Java program Vcheck will be
used to scan your design for some of the common illegal constructs.
It is fairly simple, and can be easily fooled into either allowing
things or complaining incorrectly. But it is a useful tool if you are
unclear as to whether or not your design meets the requirements set
forth here. You will be required to run it on your Verilog designs
and hand in the output; since Vcheck is new this will also help to
test it more thoroughly. To run it, copy the two class files
"Vcheck.class" and "VerFile.class" into a
directory, and from that directory type "java
Vcheck <myfile.v>"
|