An Introduction to Verilog Data Types and Arrays

Share on facebook
Share on twitter
Share on linkedin

Table of Contents

In this post, we talk about the most commonly used data types in Verilog. This includes a discussion of data respresentation, net types, variables types, vectors types and arrays.

Although verilog is considered to be a loosely typed language, we must still declare a data type for every port or signal in our verilog design.

The type which we specify is used to define the characteristics of our data.

We can use types which interpret data purely as logical values, for example. We can also use types which interpret our data as if it were a numeric value.

When we assign data to a signal in verilog, the data is implicitly converted to the correct type in most cases. As a result, there is often no need necessary to explicitly perform type conversions in verilog.

Respresenting Data in Verilog

When we write verilog, we often need to represent digital data values in our code. We can express this data as either a binary, hexadecimal or octal values.

Unlike in other programming languages, we also need to define the number of bits we have in our data representation.

This is because we are describing hardware circuits when we use verilog. Therefore, we can create data busses which contain as many bits as we choose.

The code snippet below shows the general syntax for representing digital data in verilog.

<bits>'<representation><value>

We use the <bits> field to indicate the number of bits in the data that we are representing.

We use the <representation> field to indicate how our data is represented. This field can be set to b (for binary), h (for hex), o (for octal) or d (for decimal).

Finally, we use the <value> field to set the actual value of the data.

The code snippet below shows how we represent the decimal value of 8 using each of the different valid reprentations.

// Binary value of 8
4'b1000;
// Hex value of 8
4'h8;
// Octal value of 8
4'o10;
// Decimal value of 8
4'd8

Basic Data Types in Verilog

Broadly speaking, the basic data types in verilog can be split into two main groups – net types and variable types.

We use these two different groups to model different elements of our digital circuits.

We use the net types to model connections in our digital circuits. They are unable to store values on their own and must be driven with data.

We primarily use the variable types to model registers or flip flops in our design. These types can store data, meaning that their behaviour is similar to variables in other programming languages such as C.

Regardless of the exact type we are using, there are four valid values we can assign to individual bits in our data. These four different values are shown in the table below.

0Binary 0 value
1Binary 1 value
zHigh impedance value
xunknown value

We always use the same syntax to declare a variable in verilog, regardless of the type. The code snippet below shows this general syntax.

// General syntax to declare a variable in verilog
<type_name> <size> <variable_name> = <value>;

We use the <type_name> field in the above example to declare the type of variable we have. We simply replace this field with the name of the type.

As an example, the verilog code below declares an integer type variable and assigns it a value of 100.

integer example = 100;

Net Types in Verilog

We use the net data types in verilog to describe the physical connections between different components in our design. As a result of this, net types on their own can not be used to store data values or drive data.

To better demonstrate when we would use a net type, consider the circuit diagram shown below.

In this circuit, we would use a net type to connect the output of the multiplexor to the input of the flip flop.

A circuit diagram showing a two input multiplexor and a d type flip flop. The output of the multiplexor is the input to the flip flop.

We normally use continuous assignment to drive data onto a wire type. To do this we must use the assign keyword, as shown in the code snippet below. We talk about continuous assignment in more detail in the next post.

// Driving a net type to a constant 0 using the assign keyword
assign a = 1'b0;

We can not use net types in procedural code such as always blocks. The always block is discussed in more detail in a later blog post.

Wire Type in Verilog

The most commonly used net type in verilog is the wire type which we discussed in the previous post.

We use the wire type in verilog to declare signals which are very basic point to point connections in our design. As the name suggests, they are roughly equivalent to an electrical wire in a traditional circuit.

The verilog code below shows how we use the wire type together with the assign keyword.

// Declaration of a single wire
wire a;

// Driving data onto the wires using assign
assign a = c;
assign b = d;

wand & wor Types

Although the wire type is the most commonly used of the net data types, there are several other types of net which we can use in our verilog designs.

The wand and the wor net types are used to insert basic logic gates into our circuit. We use the wand to insert an and gate and the wor type to create an or gate.

When we use the wand and wor types, we must assign the signal more than once. We do this as each of the assignments represents one input to the underlying logic gate.

The verilog code below shows how we use the wand and wor types together with the assign keyword.

// Declaration of our wand and wor types
wor a;
wand b;

// Wires which connect to our gates
wire c, d, e, f;

// Create an or gate with the function c or d
assign a = c;
assign a = d;

// create an and gate with the function e and f
assign b = e;
assign b = f;

As we will see in our next post, we can easily use the wire type to model combinational logic in verilog. As a result of this, the use of the wor and wand types is not recommended.

tri, triand & trior Types

In addition to the wire, wand and wor state, we can also use an equivalent tri, triand or trior type.

We use these types in the exact same way as the wire, wand and wor types. In fact, the functionality of these types is exactly the same. However, we can use them to more clearly show the intent of our design.

The code snippet below shows a basic example where the tri type is driven to high impedance.

// Declaration of our tri type
tri a;

// Drive the tri type to high impedance
assign tri = 1'bz;

However, as the wire type can also can take tristate values, we rarely use the tri type in practise. The same is also true with the trior and triand types, which can also easily be replicated using the wire type in our verilog designs.

supply0 & supply1 Types

The final net types which we can use in our verilog designs are the supply0 and supply1 types.

We can use these types to tie our signal to a constant value of either binary 1 or 0. As this has the effect of creating a net which is tied to either ground or Vcc, we don’t need to assign any data to this type.

The code snippet below shows how we use these types to create a signal which is tied either high or low.

// Create a net which is tied to 0b
supply0 a;

// Create a net which is tired to 1b 
supply1 b;

However, we rarely need to tie a signal high or low in our design and when we do, it is simple to accomplish using a wire type. Therefore, the supply0 and supply1 types are rarely used in practise.

Variable Types in Verilog

Unlike net types, we use variable data types in verilog to store values. When we assign a value to a variable type it maintains this value until it is assigned again.

The variable types are generally more intuitive to understand than net types as they behave in a similar manner to variables in other programming languages such as C.

To better demonstrate when we would use a variable type, consider the circuit diagram shown below.

In this circuit, we would use a variable type to model the flip flop output as it effectively stores a single bit of data.

A circuit diagram showing a two input multiplexor and a d type flip flop. The output of the multiplexor is the input to the flip flop.

We must use variable types within blocks of procedural code such as an always block, as shown in the code snippet below.

always @(posedge clock)
  q <= d;
end

Reg Type in Verilog

The most commonly used variable type in verilog is the reg type. We can use this type whenever we need to store a value in our design.

We most commonly use the reg type to model the behaviour of flip flops.

However, the reg type can also be used to model combinational logic in verilog in some circumstances.

We discuss the use of the reg type for modelling both types of logic in more detail in the post on the verilog always block.

The verilog code snippet below shows how we use the reg type to model a basic flip flop.

// Declaration of our reg types
reg q;

// Code for a basic flip flop
always @(posedge clock)
  q <= d;
end

Numeric Variable Types

The types which we have looked at so far are all used with single bits of data. However, we can also represent data numerically in our verilog designs.

In verilog, there are two commonly used numeric types – the integer type and the real type. Let’s take a closer a look at both of these types.

Verilog Integer Type

The most commonly used type for numerical data in verilog is the integer type. However, we normally use this for internal signals in a module rather than for ports. 

By default, the integer is a 32 bit 2s complement number which we can use to represent any whole number in our verilog design. When we use an integer type, we assign numerical rather than binary values to the variable.

As we can also assign numeric values to the reg type, we typically use integers for constants or loop variables in verilog.

Our synthesis tools will automatically trim any unused bits in our integer type. For example, if we declare an integer constant with a value of 255 then our synthesis tool will trim this down to 8 bits.

The code snippet below shows how we declare and assign an integer type in verilog.

// Example of an integer
integer a = 255;
Verilog Real Type

In addition to the integer type, we can also use the real type in verilog. We use this type to store non-integer numbers, i.e. numbers which also have a decimal part.

The real type is typically implemented as a 64 bit floating point number in verilog. As a result of this, it can’t be directly synthesized and we typically only use the real type in our testbench code.

We can use either decimal or engineering type notation to assign values to the real type.

The code snippet below shows how we declare a real type and assign data to it.

// Declaration of a real type
real a;

// Assign of data using decimal notation
a = 2.5;

// Assignment of data using engineering notation
a = 1e-3;

Vector Types in Verilog

With the exception of the numerical types, all of the types which we have looked at so far consist of a single bit.

However, we often use data busses to transfer data within a digital circuit.

In verilog, we can use vector types to create data buses. This allows us to declare a signal which has more than one bit.

The code snippet below shows the general syntax which we use to declare a vector type in verilog.

// General syntax to declare a vector type
<type> <size> <variable_name>;

When we define the size of the vector we must specify the most significant and least significant bits (MSB and LSB). Therefore, the <size> field takes the form [MSB:LSB].

For example, to declare a 4 bit little endian type vector we would use the construct [3:0].

As we talked about earlier in this post, we can represent data using binary, hex, octal or decimal formats. When we assign data to a vector we can use any of these representations.

The verilog code below shows how we would declare a 4 bit wide reg type. We also see how we can use the different data representations to assign the value of 1010b to the variable.

// Declare our reg type vector
reg [3:0] a;

// Assign binary data
a = 4'b1010;

// Assign hex data
a = 4'ha;

// Assign decimal data
a = 4'd10;

// Assign octal data
a = 4'o12;

Signed and Unsigned Data in Verilog

When declaring reg, wire or integer types in verilog, we can also use the signed and unsigned keywords to change the way our variable interprets data.

When we declare a type as signed in verilog, it is interpreted as a 2’s complement number. This means that we can assign negative numbers to these signals.

By default, the integer types is signed whilst both the reg and wire types are unsigned. We only need to use these keywords if we wish to modify this default behaviour.

The verilog code below shows how we can declare signed and unsigned data using the reg, wire and integer types. In this case, all of the variables which we declare are 32-bits wide.

// Declarations for signed and unsigned reg type
reg [31:0] a;
reg signed [31:0] b;

// Declaration for signed and unsigned wire type
wire [31:0] a;
wire signed [31:0] b;

// Declaration for signed and unsigned wire type
integer unsigned a;
integer b;

Arrays in Verilog

We can also create and use array types in verilog. These are particularly useful in the modelling of memories.

In order to declare an array in verilog, we simply add an extra field after the variable name which declares how many elements there are in our array.

This declaration takes the same format as the vector size field which we talked about previously.

The code snippet below shows the general syntax which we use to declare an array type in verilog. We use the <elements> field to declare the size of our array.

// General syntax to declare an array type
<type> <size> <variable_name> <elements>;

As an example, let’s say we want to create an array of 4 bit wire types. We want to have a total of 16 elements in the array. The verilog code below shows how we would create this array.

wire [3:0] example [15:0];

We can access individual elements in the array type using square brackets. For example, the verilog code below shows how we would assign the value of Fh to the first element in our example array.

example[0] = 4'hF;

Multi Dimensional Arrays

We can also create arrays which have more than one dimension in verilog. To do this, we simply add another field which defines the number of elements we need.

The code snippet below shows the general syntax we would use to create a 2D array in verilog.

// General syntax to declare an array type
<type> <size> <variable_name> <elements> <elements>;

As an example, let’s consider the case where we want to modify the size of our previous example array. We now want to create a variable which can store 2 elements both of which have 16 4 bit wire elements.

To do this, we simply add an extra field to the end of our declaration. The code snippet below shows how we would do this.

wire [3:0] example [15:0][1:0];

We also use the same method to assign a multidimensional array as we would for a 1D array. However, we now use a pair of square brackets to define the element in both dimensions of the array.

As an example, suppose we want to assign the value of Ah to the the last element in both dimensions. The verilog code below shows how we would assign data to this element in our array.

example [15][1] = 4'ha; 

Exercises

Which types of data can we represent in our verilog design?

show answer

Binary, hexidecimal, octal and decimal. We can also represent decimal numbers but this is not synthesizable.

hide answer

What are the two main data types in verilog? What is the difference between them?

show answer

Net types are used to model connections in our design and can’t store values. Variable types can store data values and behave like variables in other programming languages.

hide answer

Which type do we most commonly use to model point to point connections in verilog?

show answer

The wire type

hide answer

Which type do we most commonly use to model the behaviour of storage elements like flip flops?

show answer

The reg type.

hide answer

Name the two different types of numeric types. What are the differences between them?

show answer

The integer type represents whole numerical values. The real type can be used to represent decimal values as well.

hide answer

Write the code to declare an 8 bit wire type and assign it the value of AAh.

show answer
// Using big endian convention
wire [7:0] example = 8'haa;
// Using little endian convention
wire [0:7] example = 8'haa;
hide answer

Declare an array of 16 bit reg types. The array should have a total of 4 elements. Assign the value of FFFFh to the first element in the array and AAAAh to the fourth element in the array.

show answer
// Variable declaration
reg[16:0] example [3:0];
// Assigning data to the first element
example[0] = 16'hffff;
// Assigning data to the fourth element
example[3] = 16'haaaa;
hide answer

Enjoyed this post? Why not share it with others

Share on facebook
Share on twitter
Share on linkedin

Leave a Comment

ENJOYING THIS ARTICLE?

Why not join our mailing list and be the first to hear about our latest FPGA tutorials

Close