Table of Contents

Structural Verilog

This is a quick introduction and reference for Structural Verilog. It is by no means a complete guide to Verilog, but does include everything you will need to know for the Structural Verilog portion of the class. More details are contained in the textbook. 1)

General Verilog Rules

Verilog is case-sensitive.
Module, wire, port, instance names, etc. cannot start with a number.
All lines of code must end in a semicolon ; except for the endmodule line.
Underscores _ can be used in naming but not dashes -, other punctuation, or white space.
Comment syntax:

/*  Multi
line comment */
// Single line comment

Verilog Execution

Verilog does not operate sequentially. Meaning, it does not run through and execute each line of code as it reaches that line. Instead, as each line of Verilog is processed, the needed hardware implementation is created, its inputs and outputs are hooked up, and it is placed in the implementation. Then the following lines are processed. Only after all the code has been processed does the hardware begin operating. This is because Verilog is a Hardware Description Language (HDL). All the hardware must be described and implemented before anything can execute.

Modules

Modules look like functions in C style languages (such as C++). They have internals that are kept hidden from other modules, but are interfaced with through what is called their port list. A port list is a list of all the inputs and outputs for a module.

Here is an example module using Structural Verilog:

module myModule(a, b, c);
  output a;
  input b, c;
  wire b_not;
 
  not(b_not, b);
  and(a, b_not, c);
endmodule

myModule is the name of the module. You will use this when you instantiate this module from elsewhere. Instantiating a module is like making a function call in C. In Verilog, it creates an instance of that hardware.
(a, b, c) is the port list. This lists the names of the inputs and outputs for the module. This is similar to a parameter list that you would make for a function in C.
output a; declares the port a as an output. All ports and variables must have a declared type.
input b, c; declares b and c as inputs. This uses a comma separated list to declare multiple variables as the same type. All type declarations may also contain a list like this.
wire b_not; declares a wire. A wire is a connection or variable that is internal to the module. Outside module don't need it and won't have access to it.

Using Modules

Using modules inside of other modules is very similar to making function calls in C but is instead called instantiation; you are making an instance of the module. Also unlike C, every instantiation requires an instance name.

For example, if we wanted to use the module from above named myModule inside of another module, it would look like this:

module anotherModule(a, b);
  input a, b;
  wire c;
 
  myModule foo(c, a, b);     // Module Instantiation
 
endmodule

foo is the name of that instance of myModule. (c, a, b) will be hooked up to that instance of myModule's port list, just like passing arguments into a function call in C.

Gates

In the myModule example in the Modules example, there are two gates being used. The following will explain how they work:

  not(b_not, b);
  and(a, b_not, c);

not(b_not, b); is an instance of a not gate, or an inverter. The first argument is what will be connected to its output, the second argument will be connected to its input. It would look like this: and(a, b_not, c); is like the not gate, but is an and gate with two inputs. This creates a gate that would look like this:

The general rule for instantiating gates is the following, where type is the name of the type of gate (not, and, etc.). For gates besides the not gate (which only have one input), you can have as many inputs to a gate as you want, all separated by a comma. These same rules apply to using the or gate.

  type(output, inputs);

If you're connecting multiple gates together, you'll have to make wire variables to connect to the output of a gate, and the input of the other gates. The variable type output should only be used for your module's outputs, not for simple gate outputs.

Variables and Ports

Within the context of this tutorial for Structural Verilog, you will only be using variables and ports of types input, output, and wire.

Ports are the inputs and outputs declared in the module declaration. For example, in myModule (shown again here), the ports are a, b, and c. This is determined by the first line, module myModule(a, b, c);. These ports are the only variables that should be declared are outputs or inputs. Everything should be wires.

module myModule(a, b, c);
  output a;
  input b, c;
  wire b_not;
 
  not(b_not, b);
  and(a, b_not, c);
endmodule

Number Constants

Numbers in Verilog will default to 32 bit decimal numbers if no radix (base) or bit length is given. To prevent unexpected behavior and keep your code clear, you should specify the bit length and radix of numbers that you use.

The general syntax for doing so is explained in the following.

  <bits>'<signed><radix>value

<bits> is the number of bits that the number will be. This is just a number like 1 or 4.
<signed> specifies whether the number is signed or unsigned. Use s for signed and leave blank for unsigned. (Signed numbers are in 2's complement.) For this class, you will never need to use signed numbers, so you can just forget about this part.
<radix> designates what radix or base the number is in. This is only one letter. The radix abbreviations are:

b binary
o octal
d decimal
h hexadecimal

You will never use octal in this class, unless you like making things unnecessarily difficult for yourself.

value should be replaced by the number you plan to use.

All the elements inside <> are optional.

Examples:

  5'sb01
  4'd5
  4'hf
  0

5'sb01 is a signed 5 bit binary number with the value 1.
4'd5 is a 4 bit unsigned decimal number with the value 5.
4'hf is a 4 bit unsigned hexadecimal number with the value 15 (F in hexidecimal).
0 is a 32 bit unsigned decimal number with the value 0. That's a lot of bits just to represent zero. It's better to always specify how many bits a number needs, this will keep the code and simulations more clear and concise. This can prevent bugs that are very hard to spot.

Buses

Ports that hold multiple bits, known as buses, are easy to create and use in Verilog. They are similar to arrays in most programming languages, though the syntax is slightly different. Wires, inputs, outputs, etc. can all be made into buses.

Declaring

To declare a bus, provide the width of the bus directly after the type declaration. However, instead of stating the size of the bus, give the most significant bit (MSB) index followed by the least significant bit (LSB) index. The following example demonstrates how this is done:

  input [3:0] myBus;

input [3:0] myBus; will create an input bus with the MSB being 3 and the LSB being 0. This has a total of 4 bits.

Accessing

You can access the bits in a bus individually or in a range. It uses similar syntax.

Individually:

  myBus[3]

This will access the third bit of myBus. This can be used to read the value in that bit or to store a value in that bit.

By range:

  myBus[3:0]

This will access all of the bits in myBus from bit 0 to bit 3 (4 bits total). This allows you to read or set all of those bits at once! It is very powerful and convenient!

When using a bus in a TCL file, you add values onto it in the following ways.

  add_force myBusName {0011 0}
  add_force myOtherBus[0] {0 0}

add_force myBusName {0011 0} adds the entire value 0011 onto myBusName all at once (at time 0).
add_force myOtherBus[0] {0 0} adds the value 0 onto bit 0 of myOtherBus (at time 0);.

1)
Appendix A