This chapter is designed to give concise and useful summary information on important language constructs and usage in Verilog, hopefully helpful and easy to use, but not necessarily comprehensive. The information is intended for the reader to understand the key concepts in this book. This chapter will introduce the key concepts in Verilog and the important syntax required for most Verilog designs, particularly with reference to FPGAs.
Verilog has been the primary hardware description language (HDL) for digital design worldwide for probably more than 30 years, but it is only relatively recently that it has begun to extend beyond its original focus of IC design into the FPGA arena outside the USA.
Verilog as an HDL does have several advantages over other HDLs such as VHDL, as it is both C-like and also very compact. This makes writing models in Verilog very straightforward for digital designers with some software background, and fast.
The reduced scale of the syntax (i.e., its compact nature) also makes it less prone to typing errors simply due to the fewer number of characters often required compared to other languages.
This chapter will provide a primer for the basics of Verilog, and as for the VHDL primer, the reader is referred to a large number of textbooks and references (also many online sources) for more detailed language descriptions and examples. The purpose of this chapter is as a “quick start” and to provide an overview of the key language features rather than as a full-blown reference.
Verilog is a language that defines the functional blocks using a “module” concept. The basic principle is that a module will carry out some function (rather like a procedure in C) but the ultimate goal is that the module will eventually be synthesized into hardware.
A module is defined by the keyword module and endmodule with the general framework as shown:
The top level module that will be simulated will not necessarily have any connections or parameters.
Once we have a basic module definition (with a name) the next step is to connect it up. Consider the example of an 8-bit counter that has a clock (clk) and reset (rst) input, with the output word defined as dout (7 down to 0) The module definition needs to be modified to include the names of the ports in the header as shown here:
The module can also be written with the port names spread over several lines such as:
The obvious question is “why would we do that?” However, this way makes it simple to add descriptive comments next to each port name definition, which can be extremely helpful in debugging the model.
Comments in Verilog use the // notation (the same as in C++) and so we could write the module header with some additional comments as shown below. As you can see, we can put helpful comments such as the active clock edge (rising or falling), whether the reset signal is active high or low, and finally the width of the dout variable.
The final step at this point of model creation is to define the type of the ports, in terms of direction. For example, are they inputs, outputs or both? Also, what is the width of the bus if they are composite (multiple value) ports? In this simple example, the clk and rst ports are both inputs, and the dout port is an output, of width 8 (tagged with indices 7 down to 0 in classical digital designer format).
This definition is done using the keywords input, output or inout and the expanded port definitions are given here.
Once a module has been declared and its ports defined, it is then necessary to make those available internally to the module for use as connections. The most basic connection type is called a wire and this is exactly as its name suggests, simply a direct connection.
If we take our counter example, in order to make each port available for internal coding, we need to setup a wire for each input. These are defined using the wire keyword and the extended module is given as follows:
But what about the counter output? Why did we not simply assign a wire type to the dout variable? The answer is that wire is essentially combinatorial (i.e., a direct connection) whereas the output is synchronous and needs to be held in a register. In Verilog we denote this using the reg keyword rather than the simple wire and so the dout variable needs to be defined as shown below. Notice that the declaration of the register also needs to have the bus defined in an identical manner to the port declaration.
When we define the module behavior there are several ways in which this can be done. Verilog is in nature a “bottom up” style language and therefore we need to think in terms of assigning signals directly, or acting in terms of hardware “blocks.” The two types of behavioral block that we will consider first are the always and initial blocks.
The always block is a way of defining a section of code that will always be activated—in other words the same as a VHDL process, it is a block of hardware that is always active. The initial block, in contrast, is activated on startup, and used to initialize conditions, then never used again.
In order to illustrate how this works in practice we can consider our simple counter. The basic behavior of this module is to count on a rising clock edge, and increment the counter by one, unless the reset is high, in which case the output value will be set to zero.
The basic outline of the always block is as shown here:
If we look at this code, we can see that the always keyword is used to define the block and that it is activated by the function posedge—in other words the rising edge of the clk clock signal. The code is contained between a begin and end, with the name after the begin defining a block name—which is useful in complex designs for the identification of specific signals within blocks.
The final step in the development of our counter is to therefore define the behavior, and implement the check for reset active high and the counter itself.
The module parameters are defined using the keyword parameter with an example given below, where the parameter buswidth is defined as a parameter with a default value of 8, and a second parameter n is defined as 4.
These parameters (buswidth and n) can then be assigned when the module is instantiated using the following approach:
If a parameter local to the module is required then we can use the keyword localparam to define a parameter that will just be available inside the module.
As we have seen with the section on wires and registers, wires are simply connection points, and registers are used to store variables. Therefore, as we have seen previously, registers can be used to store data for the interface section of the model, but also for internal variables.
The basic data type of the register (reg) is a simple digital logic type with four values (0,1,Z,X), with the default value of X. The register data type can then be used as a building block to create more complex types such as buffers of a specific size. For example, to create a register of width 4 bits, with the name reg4 use the following syntax:
The most basic decision-making element is the if statement, and this can be used in a very simple manner as shown in the basic example below, where the variable y is checked and depending on its value, the register dout will be assigned the value of a or b.
If the decision making requires more than a single statement per choice, then use the keywords begin and end to wrap up several statements such as:
Finally, if statements can also be nested to provide multiple options, as can be seen in the following example, where if y=1, then the second nested if checks for the value of the variable x.
There are two ways of looping in Verilog, for and repeat. For loops are very similar to C, setting up the start, end and increment values. For example, to set up a loop to carry out a function on each bit of a 32 bit bus, a for loop could be used in this way:
The repeat function could be done in a similar way using the repeat function, where the loop is handled incrementing the defined number of times, and then it is up to the designer to increment the value (in this case i).
This chapter is a very brief introduction to basic Verilog. For a more comprehensive overview there are many useful references online, such as http://www.asic-world.com/verilog/, or the excellent text by Zwolinski [4]. It should be noted that in this book we are purely considering Verilog, but there is a modern variant called “SystemVerilog” which has more high level and abstract functionality than basic Verilog. However, not all the FPGA tools support SystemVerilog yet, but Verilog is fairly universally usable for any FPGA tool flow.
18.217.139.162