UW-Madison
Computer Sciences Dept.

CS/ECE 552 Introduction to Computer Architecture


Spring 2012 Section 1
Instructor David A. Wood and T. A. Ramkumar Ravikumar
URL: http://www.cs.wisc.edu/~david/courses/cs552/S12/

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

  • shifting by a non-constant shift count (i.e., assign sigX = sigY >> sigZ;)

  • testing for less-than or greater-than (which requires subtraction). The reason for these rules is twofold:

  • 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:

  • shift by a constant amount

  • a ? b : c [if a true, use b; else use c]

  • == and != (test for equality and inequality)

  • concatenation: {bit, bit}

  • 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:

  • Use a "reg" statement only to specify the outputs of a case statement. No other use of this keyword is allowed.

  • Use an "always" statement only to introduce a case statement. No other use of this keyword is allowed.

  • 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.

  • The value for the outputs of the case statement must be specified in every case. This is important: Failure to specify a value for some output bit will cause it to retain its previous state, causing a glitch-prone RS latch to form.

  • A large case statement (more than about 12 lines) should go in its own module.

  • You may use "casex" instead of "case" to specify don't-care bits in the select.
    Here is a slightly less trivial example. This case statement implements the logic for a state machine which looks for pulses on inputA and then inputB. Note that all outputs are set in each case. Note also that it is clear by inspection that all combinations of inputs have been considered.

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:

  • 'define - use this for constants that don't change.

  • 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>"

 
Computer Sciences | UW Home