An Introduction to Verilog Data Types and Arrays
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.
We’d really appreciate your feedback on these videos and the Fourdotday payment portal. For feedback on the videos, please contact us on video@fpgatutorial.com. For feedback on the Fourdotpay platform, please contact us on paul@fourdotzero.io
Payments for our videos are handled by a new company called Fourdotpay. Anyone who signs up with them during this introductory period will receive a £5 welcome bonus. This means you can try out one of our new videos absolutely free. See our FAQS page for details about how to take advantage of this offer.
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 value.
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 fundamentally 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.
0 | Binary 0 value |
1 | Binary 1 value |
z | High impedance value |
x | unknown value |
We use the same syntax to declare a variable in verilog, regardless of the exact 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.
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 a later 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 a later 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 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.
We must use variable types within blocks of procedural code such as an always block, as shown in the code snippet below which models a D type flip flop.
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 verilog testbenches.
We can use either decimal or scientific 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
Prior to the release of the verilog 2001 standard all variable and net types could only be used to store unsigned data types.
Similarly, the integer type was always interpreted as a signed value.
However, the signed and unsigned keywords were introduced as a part of the verilog 2001 standard. This allows us 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 type 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 3 bit reg types. We want to have a total of 8 elements in the array. The verilog code below shows how we would create this array.
reg [2:0] example [7: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 5h to the final element in our example array.
example[7] = 3'h5;
We can also simulate this example on EDA playground.
Multi Dimensional Arrays
In the verilog 1995 standard, it is only possible for us to create one dimensional arrays such as those we used in the previous section.
However, we can also create arrays which have more than one dimension when we use the verilog 2001 standard.
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 the array from our previous example.
We now want to create a variable which can store 2 elements both of which have 8 4 bit reg type 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.
reg [3:0] example2d [7: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 0xa to the the last element in both dimensions. The verilog code below shows how we would assign data to this element in our array.
example2d [7][1] = 4'ha; example2d [7][0] = 4'ha;
We can also simulate this example on EDA playground.