Chapter 4

A Verilog Primer

The Essentials

Abstract

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.

Keywords

Introduction to Verilog

Key concepts

4.1 Introduction

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.

4.2 Modules

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:

1 module modulename (list of connections);

2

3 // contents of the model here

4

5 endmodule

The top level module that will be simulated will not necessarily have any connections or parameters.

4.3 Connections

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:

1 module counter (clk, rst, dout);

2

3 // contents of the model here

4

5 endmodule

The module can also be written with the port names spread over several lines such as:

1 module counter (

2 clk ,

3 rst ,

4 dout

5 );

6

7 // contents of the model here

8

9 endmodule

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.

1 module counter (

2 clk , // Clock Signal (Rising Edge)

3 rst,   // reset Signal (Active High)

4 dout   // Counter Output (7:0)

5 );

6

7 //contents of the model here

8

9 endmodule

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.

1 module counter (

2 clk ,   // Clock Signal (Rising Edge)

3 rst,   // reset Signal (Active High)

4 dout   // Counter Output (7:0)

5 );

6 // port declarations

7 input clk;

8 input rst;

9 output [7:0] dout;

10 

11 //contents of the model here

12 

13 endmodule

4.4 Wires and Registers

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:

1 module counter (

2 clk ,   // Clock Signal (Rising Edge)

3 rst,   // reset Signal (Active High)

4 dout   // Counter Output (7:0)

5 );

6 // port declarations

7 input clk;

8 input rst;

9 output [7:0] dout;

10 

11 // wire definitions

12 wire clk;

13 wire rst;

14 

15 //contents of the model here

16 

17 endmodule

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.

1 module counter (

2 clk ,  // Clock Signal (Rising Edge)

3 rst,   // reset Signal (Active High)

4 dout   // Counter Output (7:0)

5 );

6 // port declarations

7 input clk;

8 input rst;

9 output [7:0] dout;

10 

11 // wire definitions

12 wire clk;

13 wire rst;

14 

15 // Register definitions

16 reg [7:0] dout;

17 

18 //contents of the model here

19 

20 endmodule

4.5 Defining the Module Behavior

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:

1 always @ (posedge clk)  // Count on the Rising Edge of the Clock

2 begin: counter  // Start of the Count − block name count

3

4  // Count Code

5

6 end  // End of the Count − end of block counter

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.

1 module counter (

2 clk ,  // Clock Signal (Rising Edge)

3 rst,   // reset Signal (Active High)

4 dout   // Counter Output (7:0)

5 );

6 // port declarations

7 input clk;

8 input rst;

9 output [7:0] dout;

10

11  // wire definitions

12  wire clk;

13  wire rst;

14

15  // Register definitions

16  reg [7:0] dout;

17

18  always @ ( posedge clk )   // Count on the Rising Edge of the Clock

19  begin: COUNTER  // Start of the Counter − block name COUNTER

20

21 if (rst == 1’b1) begin

22 dout <= #1 8’b00000000;

23 end

24 else begin

25 dout <= #1 dout + 1;

26 end

27

28  end  // End of the Counter − end of block COUNTER

29

30  endmodule

4.6 Parameters

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.

1 module modulename (interface ports);

2 parameter buswidth = 8;

3 parameter n = 4;

4 … contents of the model here

5

6 endmodule

These parameters (buswidth and n) can then be assigned when the module is instantiated using the following approach:

1 moduleinstance #(8,4) 2modulename(list of ports);

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.

4.7 Variables

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.

4.8 Data Types

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:

1 reg [3:0] reg4 ;

4.9 Decision Making

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.

1 if (y == 1)

2   dout = a ;

3 else

4   dout = 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:

1 if (y == 1)

2 begin

3 dout = a ;

4 cout = d ;

5 end

6 else

7 begin

8  dout = b ;

9  cout = e ;

10 end

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.

1 if (y == 1)

2 begin

3   if (x == 1)

4   dout = a ;

5   else

6   cout = d ;

7 end

8 else

9 begin

10 dout = b ;

11 cout = e ;

12 end

4.10 Loops

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:

1 for (i = 0; i <32; i = i + 1)

2 // Complete the function using the loop variable i

3 end

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

1 repeat (32)

2 // Complete the function using the loop variable i

3 i = i + 1;

4 end

4.11 Summary

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.

References

[4] Zwolinski M. Digital Systems Design with SystemVerilog. England: Pearson Education; 2009 ISBN 978-0137045792.


2606 “To view the full reference list for the book, click here

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.139.162