Verilog

What is Verilog? In 1985, Automated Integrated Design Systems (renamed Gateway Design Automation in 1986) introduced a product named Verilog. It was the first logic simulator to seamlessly incorporate both a higher-level language and gate-level simulation. Before Verilog, there were many gate-level simulators and several higher-level language simulators, but there was no way to make them work together easily.

The Verilog language has been updated with the IEEE standardization in 1995, and now the update to the standard in 2001.

Verilog's Levels of Description

As regards levels of description, Verilog can definitely be used from the system level down to the switches level.

Verilog Format

Verilog Identifiers

Identifiers are the names Verilog uses for the objects in a design. Identifiers are the names you give your wires, gates, functions, and anything else you design or use. The basic rules for identifiers are as follows:

  • May contain letters (a-z, A-Z), digits (0-9), underscores (_), and dollar signs ($).
  • Must start with a letter or underscore.
  • Are case sensitive (unless made case insensitive by a tool option).
  • May be up to 1024 characters long.
  • Other printable ASCII characters may be used in an escaped identifier. These obey two rules: (1) they must start with a backslash (\), and (2) they must end with white space.

Comments in Verilog

These are the same as those in C++. Single line comments begin with a double backslash, whereas multiline comments are enclosed between \* and */ strings.

Numbers

You need to specify the base (or radix) and the number of bits used to represent your number. Further, you use a single quote (') to separate number of bits and radix.

<number_of_bits>'<radix><value>

Further, values may bear prefixes b (for binary), d (for decimal, the default), h (for hexadecimal), and o (for octal). These one-letter prefixes may be upper-case.

Text Macros

Verilog provides a text macro substitution facility. This is useful to define opcodes or other mnemonics you wish to use in your code. This is done with the grave accent key (backwards apostrophe) and the define keyword. Like this:

`define mycode 47

To be used like this:

 b = `mycode;

Semicolons

Each Verilog statement ends with a semicolon. The only lines that do not need semicolons are those lines with keywords that end a statement themselves, such as endmodule.

Verilog Modules

The main building block in Verilog is the module. You create modules using the keywords module and endmodule. You may specify a module name with in, out or inout variables, much like a C/C++ function declaration or prototype.

Ports in Modules

Modules may have ports or connections to the outside. In general, if you are modeling a self-contained system, you will not have ports. But if you are modeling something that needs to be connected to something else, you will need ports to make those connections.

Verilog supports three port directions: input, output, and inout (bidirectional). In Verilog, you must declare the ports in two places: First, as part of the port list in the module. Second, for the direction and size of all the module's ports using the input, output, and inout keywords.

The 2001 standard allows you to combine the portlist and direction into a single declaration.

Instances

An instance is a copy of a primitive or a module. When you use a built-in primitive, you make an instance or copy of the built-in gate and list its connections. When you make an instance of a built-in gate, you have the option to give it a unique name. When you make an instance of a module you are required to give it an instance name.

In the following listing, or, and, and not name primitives. Also, we might have dropped the names I4, I5, I6, and I7.

module mux(OUT, A, B, SEL);
  output OUT;
  input A,B,SEL;
  not I5 (sel_n, SEL);
  and I6 (sel_a, A, SEL);
  and I7 (sel_b, sel_n, B);
  or I4 (OUT, sel_a, sel_b);
endmodule

Module Hierarchies

While you cannot define a module inside another module, you can instantiate and connect modules inside other modules, creating a hierarchy. For example, if you want to have a 2-bit mux, you can create it by using two 1-bit muxes from the previous listing. Look:

module mux2(OUT, A, B, SEL);
  output [1:0] OUT;
  input [1:0] A,B;
  input SEL;
  mux hi (OUT[1], A[1], B[1], SEL);
  mux lo (OUT[0], A[0], B[0], SEL);
endmodule

Hierarchical Names

With hierarchical names, we can point at a copy out of several copies of one kind so as to uniquely identify each gate, port, and wire.

Hierarchical names have two forms: A downward path from the current module (relative), or a name that starts at a top-level module and provides a complete path (absolute). Verilog uses the dot (.) to separate the elements in the path of a hierarchical name.

Connecting Ports

In the foregoing example connect by order has been used since the port order must be known and matched. Verilog also supports a connect by name syntax, where the port order does not need to be known, but the port names must be known. The connect by name syntax uses the hierarchical name for the ports to make the connects.

Connect by name uses the following syntax:

.<portname>(<net_to_be_connected>)
Notice the leading dot

Top-Level Modules

When the Verilog simulator finishes compiling your modules, the first thing it reports is which module or modules are Highest-level modules. Highest-level or top modules are modules that no other module has made an instance of. These non-referenced modules are considered to be at the top of the hierarchy. Usually there is only one top-level module, the test bench for your circuit. The test bench module is used to provide inputs or stimulus to a design.

Each instantiated module has a unique instance name. Since a top-level module is not instantiated, it has no instance name. Verilog automatically assigns an instance name to top-level modules. The instance name of a top-level module is the name of the module itself.

Value Set

For logic simulation, we need more values than just zeroes and ones. You also need values to describe unknown values and high impedance (not driving). Verilog uses the values x to represent unknown and z to represent high impedance (not driving). Any bit in Verilog can have any of the values 0, 1, x, or z.

Numbers, Values, and Unknowns

Is x a number? How do you set a signal to the value unknown? x by itself is an identifier. If we want the value x we need to make it into a number. To make a number we need a number of bits, a radix, and a value. Therefore 1'bx is a number with the value x.

There are a few more rules about numbers and values. Verilog does not sign extend values. It extends all values with zero, except those with x or z in the most significant place. Numbers with x and z in the most significant place extend with x and z, respectively.

Strengths

Strengths are necessary in switch-level modeling. In Verilog, strengths are represented in a range from 0 (high impedance) to 7 (supply). There are four driver strengths: supply, strong, pull, and weak. There are three capacitive strengths: large, medium, and small. The capacitive strengths are used for storage nodes in switch-level circuits. The strengths and values combine internally in Verilog to create a set of 120 possible states for a signal in Verilog.

Gate-Level or Structural Design in Verilog

This consists of simply connecting devices. Even complex designs may comprise structural modules, as gate-, dataflow- and even behavioural-level descriptions may be mixed and matched.

Verilog has a set of twenty-six built-in gates and switches (primitives).

Their syntax is fairly consistent. The first variable is out, the remainder are in. The pullup and pulldown primitives have a single output and no inputs, and are used to pull up or pull down a net. MOS-level unidirectional and bidirectional switches are represented by the remaining primitives.

Ports in Primitives

The Verilog terminology for a connection or pin is port. All the built-in primitives (gates) have ports. The pullup and pulldown primitives have only one port. The first port of each of the built-in primitives (gates) is the output. This allows you, for example, to use the same and primitive to represent a 2-input or 4-input AND gate. The only built-in primitives that can have more than one output port are the buf and not primitives, which can have many outputs, with the last terminal being the input.

Here is some Verilog code for the 2-lnput and 4-lnput AND Gates:

and ( Y, A, B );
and ( Y, A, B, C, D );

A Gate-Level Example

This is the model of a simple multiplexer, with lower-case internal varibles: sel_n (negation of SEL), sel_a, and sel_b:

module mux(OUT, A, B, SEL);
  output OUT;
  input A,B,SEL;
  not I5 (sel_n, SEL);
  and I6 (sel_a, A, SEL);
  and I7 (sel_b, B, sel_n);
  or I4 (OUT, sel_a, sel_b);
endmodule

Building Memory Cells with Gates in Verilog

To model memory modules realistically delays must be specified, which have not yet been discussed although hopefully the notation is intuitive to follow.

We shall build our memory registers from the SR latch:

module sr_latch(q, qbar, s, r);
  output q, qbar;
  input  s, r;
  wire q2r, qbar2s;

  nand (q,    r, qbar2s);
  nand (qbar, s, q2r);
endmodule

You may insert a delay after nand: #(tdelay), or #(trise, tfall)

An edge flip flop:

module edge_ff(q, qbar, d, clk, clear);
  output q, qbar;
  input  d, clk, clear;
  wire   s, sbar, r, rbar, cbar;
  // input latches:
  assign cbar = ~clear;
  assign sbar = ~(rbar & s),
         s    = ~(sbar & cbar & clk),
         r    = ~(rbar & ~clk & s),
         rbar = ~(r    & cbar & d);
  // output latch:
  assign q    = ~(s & qbar),
         qbar = ~(q & r & cbar);
endmodule

A toggle flip-flop:

module t_ff(q, clk, clear);
  output q;
  input clk, clear;
  edge_ff ff1(q, ~q, clk, clear);
endmodule

DataFlow-Level or Procedural Design in Verilog

Procedural Verilog code is like programming in a computer language—with one large exception: Procedural Verilog code adds a concept of time. With a programming language, code is started at a particular location, for example, at the first line or main function. In Verilog, code starts running in one of two places: at the initial statement and at the always statement. You can have as many initial statements and always statements as you want in your simulation or module.

initial blocks schedule statements to execute at set times, whereas always blocks cause code to execute whenever in time a given condition is fullfilled.

When an initial or an always block contains several statements, they must be enclosed between keywords begin and end.

The initial Keyword

The initial keyword causes Verilog to execute its statements at time 0. It is used not only for initialization. Here is an example of an initial section:

initial $display("Hello Verilog");

The always Keyword

always blocks contain statements whose execution is triggered by some event. Alternatively, an always block starts again when it finishes. This can create an infinite loop. Some infinite loops are useful.

D Flip-Flop in Verilog's Data Flow

An edge-sensitive D flip-flop:

module edge_d_ff(q, qbar, d, clk, clear)
  output q, qbar;
  input  d, clk ,clear;
  wire s, sbar, r, rbar, cbar;
  // input latches:
  assign cbar = ~clear;
  assign sbar = ~(rbar & s),
         s    = ~(sbar & cbar & ~clk),
         r    = ~(rbar & ~s   % ~clk),
         rbar = ~(r    & cbar & d);
  // output latch:
  assign q    = ~(s&qbar),
         qbar = ~(q & r & cbar);
endmodule

Delays in Verilog

Every statement in Verilog may have a delay before it is run. If we have two (initial) statements scheduled at the same time, which will run first? Not only is there no way to tell, but if you run the statements on another simulator, it might run them in a different order. A race condition arises.

Delays are set by prepending #delay_in_time_units.

initial #1 $display("Hello Delays Verilog!");

fork-join Blocks

The fork-join block is similar to the begin-end block in that it is also used to group statements. In begin-end blocks, the statements are sequential, and the delays are additive. In fork-join blocks, the statements are concurrent, and the delays are independent, or absolute from the time the fork-join block starts.

Nesting begin-end and fork-join blocks

Note that fork-join and begin-end blocks are themselves single statements. You can nest fork-join and begin-end blocks. You can also nest begin-end blocks within begin-end blocks; fork-join blocks within fork-join blocks; and begin-end blocks within fork-join blocks.

Although there are four possible ways to nest these blocks, two of the combinations are generally impractical. Nesting begin-end blocks within begin-end blocks has no benefit because all the statements are sequential already. When begin-end blocks are nested, it is usually for control flow. And nesting a fork-join block in a fork-join block is impractical unless there is a delay outside the inner fork-join block.

System Tasks

A Verilog system task is a special built-in command for system functions such as printing messages or reading and writing files. They all begin with the $ symbol.

The $display(...) System Task

$display("Hello Verilog");
$display(a); // display variable 'a'

You may also use format specifiers, much as in C printf(...). The format specifiers are %b for binary, %d for decimal, %h for hexadecimal, %o for octal, and %s for string.

$display("The value of a is %b, The value of b is %b", a, b);

...

Other System Tasks to Print Results

The $display command has several relatives: $write, $strobe, and $monitor. $write and $strobe are very similar to $display, and $monitor is a special, more powerful command.

$write is similar to $display: They both print results when encountered. The only difference between the two is that $display automatically puts in a new line at the end of the results, whereas $write does not. If you need to print many results on a line and need to use more that a single $display statement, use $write statements for the first part(s) of the line and then a $display for the rest of the line. You could decide never to use $display, and just use $write and put a new line in manually.

$strobe

If you want to print out your results only after all values are finished changing at the current time unit, use $strobe. $strobe waits until just before time is going to advance, then it prints. With $strobe you always get the new value.

$monitor

If you want to print results as they change, use the $monitor system task. Unlike $display, which prints only once, $monitor automatically prints out whenever any of the signals it is printing changes, so you only need to call it once. Only one $monitor can be active at a time. If you want to change what is being printed, just execute another $monitor system task and the new $monitor becomes the active print-on-change system task.

Because $monitor can produce a lot of output, there are two more special system tasks for stopping and restarting $monitor. To stop the $monitor from printing, use the $monitoroff command. To restart the $monitor, use the $monitoron command. Remember that there can be only one $monitor active in your simulation at a time. The last one executed is the only one in effect.

Writing to Files

By default, Verilog puts all the output that goes to your screen into a log file called verilog.log. You can view the results of your simulation by looking at the log file.

Besides sending output to the screen and log file, Verilog can write up to thirty-one additional files at the same time. File output is accomplished by declaring an integer that is used to represent the file and then opening the file. Once the file is opened, output commands similar to the ones previously described may be used to write to the file. Here's an example:

integer f;
f = $fopen("myFile");
$fdisplay(f, "Hello Verilog File");

For each of the commands covered so far ($display, $write, $monitor, and $strobe), there is an 'f' prefixed version of the command for printing data to files. All the file output commands require the first argument to be the file integer. The other command arguments are just like those for $display.

The integers used to represent the files have exactly one bit set in them. A single $fdisplay command can write to more than one file. The trick is to use the vertical line symbol ('|')to connect the file numbers, as shown below. One other trick is the numbering of the files: 1 is reserved for the screen and log file, so the first file opened will be 2 and the next, 4.

integer file1, file2;
file1 = $fopen("file1");
file2 = $fopen("file2");

$display("The number used for file 1 is %0d", file1);
$display("The number used for file 2 is %0d", file2);

$fdisplay(file1, "Hello File 1");
$fdisplay(file2, "Hello File 2");
$fdisplay(file1 | file2, "Hello both files");
$fdisplay(file1 | file2 | 1, "Hello files and screen");

$fclose(file1);
$fclose(file2);

Advanced File IO Functions

The 2001 standard greatly enhances file input and output capabilities. The 2001 standard defines $ferror, $fflush, $fgetc, $fgets, $fread, $fscanf, $fseek, $fsscanf, $ftel, $rewind, $sformat, $swrite, $swriteb, $swriteh, $swriteo and $ungetc, as new system functions. These functions work on files opened with $fopen, when $fopen is called with a mode similar to the ANSI C fopen function call. Each of these new functions works similarly to the ANSI C functions by the same name.

Setting the Default Radix

All of the commands for formatting output can be used with or without format specifiers. When you use a format specifier, the radix for each value printed out is set individually. If you do not use a format specifier, the default radix is decimal. Often it is desirable to print out values without having to use a format specifier. When debugging, it is inconvenient to have to use a format specifier just to see a value in a different radix. Thus, Verilog provides four types of each of the output functions with a different default radix. These are named by appending 'b' for binary, 'h' for hexadecimal, and 'o' for octal to $display, $fdisplay, $write, $fwrite, $strobe, $fstrobe, $monitor, and $fmonitor.

Some Verilog Examples

A Hello World Example

This is a simple and easy to understand example of a Verilog program. It has been lifted from the iverilog documentation:

module main;
initial
  begin
    $display("Hello, World");
    $finish;
  end
endmodule

A Behavioral-Level Example

/* Abstract behavioral system describing a telephone */
module office_phone;
  parameter min_conversation=1, max_conversation=30, false=0, true=!false;
  event ring, incoming_call, answer, make_call, busy;
  reg off_hook;
  integer seed, missed_calls;
  initial
  begin
    seed=43; // seed for call duration
    missed_calls=0;
  end
  // someone tries to call us
  always @ incoming_call
    if (! off_hook) -> ring; // if not on the phone it rings
    else begin
    -> busy; // else they get a busy signal
    $display($time," A caller got a busy signal");
    missed_calls = missed_calls + 1;
  end
  // phone is ringing . . .
  always @ring
    begin
      $write($time," Ring Ring"); / / d o we want to answer it?
      if ($random & 'b110) begin // yes we will answer it
      -> answer;
      off_hook = true;
      $display(" answered");
    end
    else // no, we do not want to answer this phone call
    begin
      missed_calls = missed calls + 1;
      $display(" not answered missed calls =%d", missed_calls);
    end
  end
    always @make_call
      if (off_hook)
      $display($time," cannot make call phone in use");
      else
      begin
        $display($time," making call");
        off_hook = true;
      end
    always wait(off_hook == true) // we are on the phone
    begin
      // wait the call duration
      #($dist_uniform(seed, min_conversation,max_conversation))
      off_hook = false;
      $display($time," off phone");
    end // might wait about 2 hours between making calls
    always #($random & 255) -> make_call;
    // someone might call in within 4 hours
    always #($random & 511) -> incoming_call;
    // Simulate two days worth of calls
  initial #(60*24*2) $finish;
endmodule

iverilog, a Friendly Verilog compiler

Icarus Verilog is a Verilog compiler. It is suitable for use as a simulator, and, to some degree, synthesizer. Icarus Verilog runs under Linux and a variety of UNIX systems, as well as Windows as a command line tool, so the instructions are generally applicable to all environments.

Try compiling the file hello.vl with:

% iverilog -o hello hello.vl

then run hello like so to get...

% vvp hello
Hello, World

...

Simulation, Synthesis and Such

You choose between simulation and synthesis by choosing a target. Note that the default target selects simulation, to be performed by running a vvp file through vvp. Targets can be set with the switch:

-t<target>

Target switches are:

null
The null target causes no code to be generated. It is useful for checking the syntax of the Verilog source.
vvp
This is the default. The vvp target generates code for the vvp runtime. The output is a complete program that simulates the design but must be run by the vvp command.
fpga
This is a synthesis target that supports a variety of fpga devices, mostly by EDIF format output. The Icarus Verilog fpga code generator can generate complete designs or EDIF macros that can in turn be imported into larger designs by other tools. The fpga target implies the synthesis -S flag.
vhdl
This target produces a VHDL translation of the Verilog netlist. The output is a single file containing VHDL entities corresponding to the modules in the Verilog source code. Note that only a subset of the Verilog language is supported. See the wiki for more information.

Minimum, Typical or Maximum Values

-Tmin|typ|max

Use this switch to select min, typ or max times from min:typ:max expressions. Normally, the compiler will simply use the typ value from these expressions (printing a warning for the first ten it finds) but this switch will tell the compiler explicitly which value to use. This will suppress the warning that the compiler is making a choice.

Module Libraries in Verilog

The Icarus Verilog compiler supports module libraries as directories that contain Verilog source files. During elaboration, the compiler notices the instantiation of undefined module types. If the user specifies library search directories, the compiler will search the directory for files with the name of the missing module type. If it finds such a file, it loads it as a Verilog source file, then tries again to elaborate the module.

Library module files should contain only a single module, but this is not a requirement. Library modules may reference other modules in the library or in the main design.

Language Version

This is called generation and is set through switches: -g1995, -g2001, -g2001-noconfig or -g2005.