Verilog overview & references


Modeling Digital Hardware

This document provides a brief overview of how a digital system can be modeled with Verilog.


Verilog's view of Digital Hardware

Verilog provides the capability to design a digital system in a modular fashion. Entire systems can be viewed as being composed of numerous individual modules. A Verilog module is a system block with well defined

1. input signals, say x1,x2,...,xn
2. output signals, say
y1,y2,...,ym
and
3. internal structure and connections or behavior.

Combinational circuits (CCs) produce output signals based on the value of their current input signals. A CC can have any number of input variables (input signals). We consider a CC as a graph of logic gates interconnected according to the logic function they compute. CCs route and transform the input values to the desired output values. In this graph the vertices correspond to the logic gates and the edges to the wires that connect them together. This graph is a Directed Acyclic Graph (DAG) as it does not include backward-edges or "loops". We focus next on the structural description of modules.


Structural Hardware Modeling

Here we model circuits by specifying the internal structure of the block. It is common to directly use the most primitive building-blocks available, such as, logic gates, or larger blocks which have already been defined elsewhere. Verilog provides NOT, OR, XOR, NAND, XNOR, NOR gates, among others. Verilog allows each logic gate to have any valid number of inputs. For instance, a single NOR gate can have four inputs, computing

 

In Verilog we can request an XNOR function to be applied to its inputs, as if we were "calling a function",

xnor xnor1(z,x1,x2,x3,x4);

Usually the output signals in the predefined gates appear first in the argument list of the gate and the input signals appear afterwards. The identifier xnor1 is associated with this particular instance of an XNOR gate. Identifiers are not mandatory for gate instances. Any other gate can be similarly requested by mentioning its name and then listing the identifiers that correspond to its output and input signals.

For instance the following

or    or1(z,x1,x2,x3,x4);
and   and1(x1,a,b);
and   and2(x2,a,c);
not   not1(na,a);
not   not2(ne,e);
and   and3(x3,na,d);
and   and4(x4,ne,d);

represents in "structural" form the logic network which computes

                                    ...(2)

The variable names acting as arguments in the logic gate functions, implicitly show the internal connections among the various gates themselves. These variables are of the "wire" type. It is straightforward to describe in structural terms any CC, since CCs are DAGs of primitive logic gates. The development of a logic circuit from scratch, starting from primitive gates is called structural or "gate-level" modeling.


Verilog modules, ports and wires

Gate-level circuit models, quickly become very unwieldy to manage. In Verilog, we can manage this complexity by grouping logic gates together into modules. A module is a subset of the circuit which can be used as a building block in the design of the entire circuit. Blocks usually carry out specific functions. A simple example is a half or a full adder block with the the well known input and output signals. Verilog declares modules by

module module-identifier

The description of the logic implementing a module is enclosed between the module and the endmodule keywords. We can declare a module for the circuit computing Eq. (2) of the example above as follows:

module Functionz(z,a,b,c,d,e);

    output z;         /* Output wire for Functionz */
    wire   z;
    input  a,b,c,d,e; /* Input ports */

    wire x1,x2,x3,x4,na,ne; /* Wires for internal connections */

    or   or1(z,x1,x2,x3,x4); /* Instantiation of OR gate `or1' */
    and  and1(x1,a,b); /* Instantiation of AND gate `and1' */
    and  and2(x2,a,c); /* etc. */
    not  not1(na,a);
    not  not2(ne,e);
    and  and3(x3,na,d);
    and  and4(x4,ne,d);

endmodule

Module components can be simple logic gates, as above, or they can be instances of other modules. Module components are interconnected by wires. Wires represent the actual conductors connecting the output of a gate to the input of another. In the example above we are using wire x1,x2,x3,x4,na,ne; for this purpose. A wire is by default a 1-bit signal line.

A Verilog structural model consists of a collection of interconnected modules. Each module defines a set of inputs and outputs connections, called ports. Verilog ports are connection points with other modules in the system. They are reminiscent of the formal parameters of a procedure in a high level language, in the sense that they allow the module to exchange signals with other modules. Ports can be of mode input, output or inout and by default are of 1-bit in width. A module may receive input signals from its input ports and generate signals to be sent out their output ports. inout ports can at times receive input atother times generate output, but cannot do both simultaneously.

As expected, after the logic for module Functionz has been defined we can use Functionz as a primitive circuit block of other modules. We discuss the modular system description in the next subsection.


Hierarchical Specification and Modeling of Hardware Systems

To make a module part of a larger hardware system, we must first "instantiate" it within a higher-level module of this system. By itself, a module definition is just the specification of a circuit, not a part of the system itself. This is similar to a type definition in high-level languages where no variable is allocated actual storage.

To actually add the circuit to the system, we need to create an instance of the associated module and attach it to other modules. Instantiating the module introduces a new physical entity and connects it to the remaining hardware in the system. Suppose that we want to use our module Functionz as part of a larger system. We will instantiate it from within another module as follows.

module LargerSystem (Z1, Z2, X1, X2, X3, Y1, Y2, Y3 );

output Z1, Z2;
wire Z1, Z2;
input X1, X2, X3, Y1, Y2, Y3; /* Input ``ports'' */
wire a1, b1, c1, d1, e1, z1, a2, b2, c2, d2, e2, z2;

Functionz fz1(z1, a1, b1, c1, d1, e1); /* 1st instantiation of Functionz */
Functionz fz2(z2, a2, b2, c2, d2, e2); /* 2nd instantiation of Functionz */

or    or3  (Z1, z1, X1);
nand  nand1(Z2, z2, Y1);
or    or1  (a1, X1, X2);
or    or2  (b1, Y1, Y2);
and   and1 (c1, Y3, X3);
xnor  xnor1(d1, Y2, X2);
xor   xor1 (e1, Y1, Y2);
nor   nor1 (a2, X1, X2);
nor   nor2 (b2, Y1, Y2);
nand  nand2(c2, Y3, X3);
xor   xor2 (d2, Y2, X2);
xnor  xnor2(e2, Y1, Y2);

endmodule

We can see in the above that module LargerSystem includes as a part of its definition a module Functionz. Fig. 1 shows a block schematic of module LargerSystem with two instantiated instances of of module Functionz and other logic. Notice that LargerSystem has two output (Z1, Z2) and six input (X1, X2, X3, Y1, Y2, Y3) signals. Notice how the input for fz1 and fz2 is computed and passed to these two modules. Notice also how the output signals from fz1 and fz2 are connected to the appropriate parts of LargerSystem and then it is used to compute the outputs Z1, Z2 of LargerSystem itself. We use wires throughout to interconnect modules and ports together. In a similar fashion we can define systems consisting of blocks that are nested to an arbitrary depth.


Behavioral Modeling of Systems

Verilog Threads

Verilog specifies hardware parallelism using "threads of control." A new Verilog thread is created by adding behavioral program statements, enclosed within a begin ... end block in a module. Each of these Verilog threads progress in parallel in response to external events, such as, the changing value of input or a free running h/w clock. In turn new values computed within a thread triggering future modifications of system variables. Threads are behavioral constructs and they play the same role in Verilog as the processes in Discrete Simulation systems. Threads capture the "natural" concurrency of hardware systems. In Verilog we can add as many threads to a single model as necessary.


always and initial Verilog Threads

There are two types of threads: the initial and always. An initial thread is specified as an initial begin ... end block and it is executed once at $time = 0 (at the beginning of simulation). We use initial threads to initialize system variables and to drive the simulation procedure from the top-level module. initial threads complete as soon as the last (behavioral) statement in their body is executed. initial statements are not synthesizable by the backend of an integrated design automation system. This means that no actual hardware can be synthesized out of statements which are preceded by initial .

The always keyword, as in always begin ... end, introduces an "always" thread which continuously repeats its body in an infinite loop. As before the begin and end keywords define the thread's body. always specifies that the thread loops forever. When the last statement in the body is reached, thread execution starts over at the top of the body. always can (and in most cases should) be controlled by simulation time delays. These delays can be specified either as an explicit number of clock units the thread must wait or as events which will cause the thread to resume execution as soon as they take place.


Time Delays in Verilog

Any behavioral statement in Verilog can be preceded by the #i time delay expression, as follows

begin

...

#5 D = B; /* Time delay for 5 units */

...

end

which instructs Verilog to suspend the execution of the sequence of instructions for 5 simulation units and then carry out the assignment.


Event Lists in Verilog

In Verilog we can precede any behavioral statement by an event specification list as follows:

always @(A or B or C or negedge CLK or posedge Reset) begin

...

#5 D = B; /* Time delay for 5 units */

...

end

The expression @(...) above is called a sensitivity list and it is an event specifier. Statements preceded by a sensitivity list suspend execution until the event specifier's condition is satisfied. In @(A or B or C ...) of the example above, the event is "any change in the value of variables A or B or C." This is called a "\level-sensitive" specification and it will resume the execution of the always block as soon as any of the listed variables change signal value. In the @( ... ) line of the example above the sub-expression "negedge CLK or posedge Reset" specifies the event "the value of the CLK signal makes the high-to-low transition (falling edge) or the value of the Reset signal takes the low-to-high transition (rising edge)". This is called an "edge-triggered" specification and it is most useful in synchronous sequential circuits, where a clock signal triggers the modification of storage elements, such as, Flip-Flop circuits.


Simulation Time Units

During the simulation of a circuit by an HDL, it is sometimes necessary to simulate the actual delays of the signal propagation from the inputs to the output of the circuit. In Verilog we can specify delay in terms of basic time units using the symbol `#'. We can associate time units with physical time using a directive of the Verilog compiler, as follows:

`timescale 1ns/100ps

The first number specifies the unit of measurement for delays and the second, the precision for which delays are rounded off. We will discuss gate delays at a much deeper detail in subsequent laboratory assignments.


Four-valued Logic

Verilog uses a form of four-valued logic rather than the familiar two-valued Boolean logic. The four values are

x and z combine with 0 and 1 in interesting ways. For example, x AND 1 = x, but x AND 0 = 0.


Using Numeric Constants in Verilog

In Verilog constants are specified in the traditional form of a series of digits with or without a sign, but also in the following form <size> <base format> <number>

where <size> contains decimal digits that specify the size of the constant in the number of bits. The <size> is optional. The <base format> is the single character ' followed by one of the following characters b, d, o and h, which stand for binary, decimal, octal and hexadecimal, respectively. The <number> part contains digits which are legal for the <base format>. Some examples:

549 // decimal number

'h08FF // 16-bit hexadecimal number

4'b11 // 4-bit binary number 11 (0011)

3'b10x // 3-bit binary number with least significant bit unknown/don't care

12'o1345 // 12-bit octal number

5'd3 // 5-bit decimal number 3 (00011)

-4'b11 // 4-bit two's complement negation of binary number 11 (1101)

-4'd3 // 4-bit value decimal number -3 in two's complement (1101)

Note that the <number> part may not contain a sign. Any sign must go on the front. Verilog can use integer, real and even string variables with the corresponding meaning. Integer variables are used in the simulation process, for instance, by stepping through the various bit positions of a vector reg or wire variable. Most often it is useful to print out variables of various types for state reporting or debugging purposes.


External Links