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.

Feature Verilog C/Java
Purpose Describe hardware circuits Write software programs
Execution Synthesized into silicon or configured in FPGA Compiled and executed on CPU
Concurrency Naturally parallel (all blocks run simultaneously) Sequential (one instruction at a time)
Time Concept Built-in time delays (#10, @(posedge clk)) No inherent time concept
Data Types wire, reg, logic (represent physical connections) int, float, objects (abstract memory)

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.

Numerical Literals

In Verilog, numerical literals are unsigned 32‐bit numbers by default, but you might want to declare the width of each numerical literal as this results in less guesswork when, for example, when concatenating a wire and a numerical literal together.

Some examples:

wire [2:0] a = 3'b111;
// 3 bit binary
wire [4:0] b = 5'd31;
// 5 bit decimal
wire [31:0] c = 32'hdeadbeef; // 32 bit hexadecimal

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.

Constants

We can use `define to define global constants in our code (like the #define preprocessor directive in C). Note that unlike C, when referencing the constant, we need to append a backtick to the front of the constant: e.g., in our case we had to use `VAR0 instead of VAR. Also, do not append a semicolon to the `define statement.

Some examples:


        

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.

module data_selector (A, B, SEL, O) (
  input A,
  input B,
  input SEL,
  output Q
);
endmodule

or just (alternatively):

module data_selector (
  input A,
  input B,
  input SEL,
  output Q
);
endmodule

When you create a project with more than one module, you will always have a top-level module. This is the module that brings all the submodules together as well as the module that will have a UCF associated with it to map the IO pins of the device to the signals in the design.

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.

By the way, we might have dropped the names I4, I5, I6, and I7. After all, they are not going to be called by further code.

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

Dot Instantiation Syntax

The foregoing style of instantiation depends on the ordering of the ports in the comparator module. Still, there is an alternate syntax for instantiating modules which does not depend on port ordering, and is thus usually vastly preferred. The syntax is:

module_name [instance_name] (.port_name(ioname), ...);

Thus, the preceding module mux could be instantiated stating its input parameters before its output parameter (given wires a0, b0, s0 and o0):

mux mux0 (.SEL(s0).A(a0), .B(b0), .OUT(o0));

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)

Parameters

We often want to create some generic module that can be customized by a few parameters when the module is instantiated. This is where the parameter statement comes in.

The following is an example of a parameterized ALU, which defaults to 32‐bits if no parameter is given during module instantiation:

`define ADD 3'd0
`define LESS 3'd1
`define EQ 3'd2
`define OR 3'd3
`define AND 3'd4
`define NOT 3'd5

module ALU(opcode, op_a, op_b, result);
  parameter N = 32;
  input [2:0] opcode;
  input [N-1:0] op_a, op_b;
  output [N-1:0] result;

  // result used in LHS of always block -> must be reg
  reg [N-1:0] result;
  always @* begin
    case (opcode)
      `ADD:  result = op_a + op_b;
      `LESS: result = op_a < op_b;
      `EQ:   result = op_a == op_b;
      `OR:   result = op_a | op_b;
      `AND:  result = op_a & op_b;
      `NOT:  result = ~op_a;
      default: result = 0;
    endcase
  end
endmodule

Then, to instantiate this ALU within another module, use the #(width) symbols within the instantiation line. For example, this line instantiates a 16‐bit ALU:

ALU #(16) alu1(...)

Testbenches

Up until now, we may have written purely synthesizable Verilog—Verilog that will be synthesized, translated, and mapped to actual hardware (in our case, the FPGA). But before we do that costly step, we need to be extremely confident that our modules function correctly. This is where testbenches and simulation software come into play.

A Verilog testbench is a special file that instantiates the module (or modules) that we need to test. This testbench is not synthesized into hardware. Rather, it provides input stimuli into the instantiated modules so that we can run the testbench in a software simulator of our projected hardware design. Because they do not have to be synthesized, testbenches can be written in a different style than normal synthesizable Verilog.

Simulation proceeds in hardware across discrete time units. To make actions in simulation go in a defined order, we often need to present the input stimuli at different time periods, rather than all at the same time. We do this by using delay statements.

Initial Blocks

Initial and forever blocks are like always blocks in that the statements within an initial block execute in order when triggered. Also, only registers are allowed on the left hand side of an initial block. However, while an always blocks executes every time a condition changes, initial blocks are executed once—at the beginning of the program.

The following code sets opcode, op_a, and op_b to 0, 10, and 20 respectively at t=0 in the simulation, and then changes those values to 2, 10, and 20 respectively at t=5 in the simulation:

reg [2:0] opcode;
reg [4:0] op_a, op_b;

initial begin
  opcode = 3’b000;
  op_a = 5’d10;
  op_b = 5’d20;

  #5 opcode = 3’b010;
  op_a = 5’d10;
  op_b = 5’d20;
end

... to be included, say, in the definition of an ALU (Arithmetic-Logic Unit).

Display Statement

The $display statement can be used to display the value of a variable using printf‐like syntax. Also, it automatically inserts a newline at the end of the printing.

wire [3:0] ten = 4’d10;
$display("10 in hex: %h, dec: %d, bin: %b", ten, ten, ten);

Sample Testbench

Using the preceding statements, we can make a very crude testbench of our ALU module.

module ALU_test;

/* Declare as regs since we'll be changing these
   values in always blocks */
reg [2:0] opcode;
reg [15:0] op_a, op_b;
wire [15:0] result; // just connected to module

// Instantiate the ALU module
ALU #(16) alu(.opcode(opcode), .op_a(op_a),
              .op_b(op_b), .result(result));
initial begin
  opcode = `ADD;
  {op_a, op_b} = {16'd32, 16'd5};
  // Wait 1 time unit for result to settle
  #1 $display("%b + %b = %b", op_a, op_b, result);
  #5;

  opcode = `OR;
  {op_a, op_b} = {16'd8, 16'd7};
  #1 $display("%b | %b = %b", op_a, op_b, result);
  // etc.
end

endmodule

/* Output of simulator:
# 0000000000100000 + 0000000000000101 = 0000000000100101
# 0000000000001000 | 0000000000000111 = 0000000000001111 */

This testbench is far from complete. We tested only one set of inputs for only two of the functions of the unit. We should test more cases, especially the corner cases. Also, an automated test is better than a manual test such as this. But it gives an idea of how to start programming testbenches.

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.

Variables in Verilog

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.

Wires

You can think of wires as modeling physical wires—you can connect them either to another wire, an input or output port on another module, or to a constant logical value. To declare a wire, we use the wire statement:

wire
a_wire;
wire [1:0] two_bit_wire;
wire [4:0] five_bit_wire;

We then use the assign statement to connect them to something else. Assuming that we are in a module that takes a two bit input named two_bit_input, we could do the following:

assign two_bit_wire = two_bit_input;
// Connect a_wire to the lowest bit of two_bit_wire
assign a_wire = two_bit_wire[0];
/* {} is concatenation – 3 MSB will be 101, 2 LSB will be
connected to two_bit_wire */
assign five_bit_wire = {3'b101, two_bit_wire};
// This is an error! You cannot assign a wire twice!
// assign a_wire = 1’b1;

Note that these are continuous assignments. That means that in the previous example, whenever the input two_bit_input changes, so do the values of two_bit_wire, a_wire, and five_bit_wire. There is no “order” by which they change—the changes occur at the same time. This is also why you cannot assign the same wire twice in the same module—a wire cannot be driven by two different signals at the same time. This is what we mean when saying Verilog is “naturally concurrent.”

Finally, there is a shortcut that is sometimes used to declare and assign a wire at the same time:

// Declares gnd, and assigns it to 0 :
wire gnd = 1'b0;

Registers

Another common data type is register. Despite the name, registers do not imply memory. They are simply a language construct denoting variables that are on the left hand side of an always block (and in simulation code, initial and forever blocks).

You declare registers, like wires, at the top level of a module, but, again, you use them within always blocks. You cannot assign registers values at the top level of a module, and you cannot assign wires while inside an always block.

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-input and 4-input 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. The SR latch itself may be built like this:

module sr_latch(q, qbar, s, r);
  output q, qbar;
  input  s, r;
    
  nor (q,    r, qbar);
  nor (qbar, s, q   );
endmodule

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 and is based on a sensitivity list.

Whenever a signal in the sensitivity list changes values, the statements in the always block will be run sequentially in the simulator. (In terms of actual hardware, the synthesis tool will synthesize circuits that are logically equivalent to the statements within the always block.)

In the degenerate case, a register in an always statement acts like a wire data type, as in this simple module:

module bitwise_not(a_in, a_out);
  input [1:0] a_in;
  output [1:0] a_out;

  /* Declare the 2-bit output a_out as a register, since it
  is used on the LHS of an always block */
  reg [1:0] a_out;

  // better to use always @* – see next example
  always @(a_in) begin
    a_out = ~a_in; // out = bitwise not of in
  end
endmodule

So whenever the input a_in changes, the code within the always block is evaluated—a_out takes the inverse value of a_in. It is as if we declared a_out to be a wire, and assigned it to be ~a_in.


Alternatively, an always block starts again when it finishes. This can create an infinite loop. Some infinite loops are useful.

If and Case Statements

More interestingly, we can place case and if statements into always blocks. We can usually think of these case and if statements as being synthesized into some sort of multiplexer. Often enough it is encouraged to use only case statements, but some other times you may see if‐else being used more.


Here is a simple circuit which utilizes a case statement within an if statement as well as some other concepts discussed elsewhere:

module alarm_clock(day_i, hour_o, minute_o);
  input [2:0] day_i;
  output [4:0] hour_o;
  output [5:0] minute_o;

  wire [2:0] day_i;

  /* Declare hour_o and minute_o to be regs since
  * they are on LHS of an always block */
  reg [4:0] hour_o;
  reg [5:0] minute_o;

  // have is_weekday take the value of a comparator
  wire is_weekday;
  assign is_weekday = (day_i <= `FRI);

  always @* begin
    if (is_weekday) begin
      hour_o = 5'd8;
      minute_o = 6'd30;
    end
    else begin
      case (day_i)
        `SAT: {hour_o, minute_o} = {5'd11, 6'd15};
        `SUN: {hour_o, minute_o} = {5'd12, 6'd45};
        default: {hour_o, minute_o} = 11'd0;
      endcase
    end
  end
endmodule

In particular, note:

  • case and if statements must be placed within an always block.
  • The use of always@*. This is a new Verilog‐2001 construct that automatically populates the sensitivity list with all variables listed in the right hand side of the always block. Unless otherwise noted, you had better always use always @* for your always block sensitivity lists, as it will save you hours of debugging.
  • The use of begin... end to delineate multi‐line blocks (instead of the usual {} found in other C‐like languages). You may omit the begin…end if the assignments in your case or if statements are only a single line long, as in the case statement above.
  • The fact that every case statement has a matching default statement and every if statement has a matching else. Do not forget this, or you will generate latches!

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!");
// General syntax: #<n> -- delay for n time units

#5;   // delay this block for 5 time units
#100; // delay for 100 time units

// Can be compounded next to another statement to delay
// that statement

#10 $display("Hi Verilog!"); // wait 10 time units, then display "Hi 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.