Verilog LifeSaver

As we have a glimpse of how SystemVerilog/Verilog looks like by looking at how they are constructed by using the digital building blocks, like combinational logic, registers, etc. Now, we shall also introduce the programming side of this Verilog.

Wire

Unlike physical wires, wires (and other signals) in Verilog are directional. This means information flows in only one direction, from (usually one) source to the sinks (The source is also often called a driver that drives a value onto a wire).

The ports on a module also have a direction (usually input or output). An input port is driven by something from outside the module, while an output port drives something outside. When viewed from inside the module, an input port is a driver or source, while an output port is a sink.

Vector

Vectors are used to group related signals using one name to make it more convenient to manipulate. For example, wire [7:0] w; declares an 8-bit vector named w that is functionally equivalent to having 8 separate wires.

Classic Examples

1

Vector Basic

2

Part Select: Endianness Swap

Example 1: A 32-bit vector can be viewed as containing 4 bytes (bits [31:24], [23:16], etc.). Build a circuit that will reverse the byte ordering of the 4-byte word.

Part-select can be used on both the left side and right side of an assignment. Thus, the Verilog code should look as follows,


Example 2: Given an 8-bit input vector [7:0], reverse its bit ordering.

Code Explanation

3

Bitwise and Logical Operators in Verilog

The table here summarises the bitwise and logical operators in verilog well.

4

Concatenating Vectors

The technique used here has been introduced in this chapter. The concatenating can be done both in the L.H.S and R.H.S of the continuous assignment statements.


Example: Given several input vectors, concatenate them together then split them up into several output vectors. There are six 5-bit input vectors: a, b, c, d, e, and f, for a total of 30 bits of input. There are four 8-bit output vectors: w, x, y, and z, for 32 bits of output. The output should be a concatenation of the input vectors followed by two 1 bits:

Modules

Classic Examples

1

Adder-subtractor

An adder-subtractor can be built from an adder by optionally negating one of the inputs, which is equivalent to inverting the input then adding 1. The net result is a circuit that can do two operations: (a + b + 0) and (a + ~b + 1).

You are provided with a 16-bit adder module, which you need to instantiate twice:

Build the adder-subtractor below.

Hint

  1. An XOR gate can also be viewed as a programmable inverter, where one input controls whether the other should be inverted. The following two circuits are both XOR gates:

  1. Use a 32-bit wide XOR gate to invert the b input whenever sub is 1. This can also be viewed as b[31:0] XORed with sub replicated 32 times.

Procedures

Procedures include always, initial, task, and function blocks. Procedures allow sequential statements (which cannot be used outside of a procedure) to be used to describe the behaviour of a circuit.

Combinational always block

Since digital circuits are composed of logic gates connected with wires, any circuit can be expressed as some combination of modules and assign statements. However, sometimes this is not the most convenient way to describe the circuit. Procedures (of which always blocks are one example) provide an alternative syntax for describing circuits.

For synthesizing hardware, two types of always blocks are relevant:

  • Combinational: always @(*)

  • Clocked: always @(posedge clk)

Combinational always blocks are equivalent to assign statements, thus there is always a way to express a combinational circuit both ways. The choice between which to use is mainly an issue of which syntax is more convenient. The syntax for code inside a procedural block is different from code that is outside. Procedural blocks have a richer set of statements (e.g., if-else, case), cannot contain continuous assignments, but also introduces many new non-intuitive ways of making errors.

reg vs. wire

As we have seen the rule of thumb explained here, we have further explanation below

The left-hand-side of an continuous assign statement must be a net type (e.g., wire), while the left-hand-side of a procedural assignment (in an always block) must be a variable type (e.g., reg). These types (wire vs. reg) have nothing to do with what hardware is synthesized, and is just syntax left over from Verilog's use as a hardware simulation language.

This rule applies to every procedural block — always, initial, task, and function. In short, in procedural blocks, the L.H.S can only be

  • reg, or

  • integer.

Clocked always block

Clocked always blocks create a blob of combinational logic just like combinational always blocks, but also creates a set of flip-flops (or "registers") at the output of the blob of combinational logic. Instead of the outputs of the blob of logic being visible immediately, the outputs are visible only immediately after the next (posedge clk).

Blocking vs. Non-blocking Assignments

There are three types of assignments in Verilog:

  • Continuous assignments (assign x = y;). Can only be used when not inside a procedure ("always block").

  • Procedural blocking assignment: (x = y;). Can only be used inside a procedure.

  • Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure.

We have already seen the rule of thumb for blocking and non-blocking statements here! Feel free to go back and review the rules again! A short summary is

In a combinational always block, use blocking assignments. In a clocked always block, use non-blocking assignments.

initial block

The initial block is primarily used to initialize variables and drive design ports with specific values. It is not synthesizable and hence cannot be converted into a hardware schematic with digital elements. Thus, we have stated above that the initial block is usually used in testbench. However, we can still use them in our RTL code to initialize our variables, but note that this initialization is just for testbench!

Function

A function can reduce the appearance of repeated code in Verilog. For now, please visit here for more examples.

Task

A function is meant to do some processing on the input and return a single value, whereas a task is more general and can calculate multiple result values and return them using output and inout type arguments. Tasks can contain simulation time consuming elements such as @, posedge and others.

To write a task, a good example is introduced in NUS CG3207 Lab 1's testbench.


Credits

Last updated