The 2 most commonly used data types in Verilog are net (wire) and register (reg). The other types can be understood as extensions or auxiliary versions of these two data types.
[3:0] = 4'hF, where the width can be omitted;
The wire type represents physical connections between hardware units, continuously driven by the output terminals of the devices connected to it. If no driving element is connected to a wire variable, the default value is generally "Z". An example is as follows:
Example
wire flag1, flag2 ;
wire gnd = 1'b0 ;
There are other net data types, including wand, wor, wri, triand, trior, trireg, etc. These data types are not used very frequently, so they are not introduced here.
Register (reg)
Register (reg) is used to represent storage elements. It retains the original value of data until it is rewritten. An example declaration is as follows:
Example
reg flag1, flag2 ;
For example, in an always block, a register may be synthesized into an edge-triggered flip-flop, and in combinational logic it may be synthesized into a wire-type variable. Registers do not require a driving source and do not necessarily need a clock signal. During simulation, the value of a register can be changed at any time through assignment operations. For example:
Example
initial begin
rstn = 1'b0 ;
#100 ;
rstn = 1'b1 ;
end
Vector
When the bit width is greater than 1, wire or reg can be declared in vector form. For example:
Example
wire [32-1:0] gpio_data; //Declare a 32-bit wide wire variable gpio_data
wire [8:2] addr ; //Declare a 7-bit wide wire variable addr, with bit width range 8:2
reg [0:31] data ; //Declare a 32-bit wide register variable data, with the most significant bit being 0
For the vectors above, we can specify a particular bit or several adjacent bits for use in other logic. For example:
Example
addr_temp[3:2] = addr[8:7] + 1'b1 ;
Verilog supports variable vector part-select. For example:
Example
reg [7:0] byte1 [3:0];
integer j ;
always@* begin
for (j=0; j<=3;j=j+1) begin
byte1[j] = data1[(j+1)*8-1 : j*8];
//Assign data1[7:0]…data1[31:24] to byte1
end
end
Verilog also supports fixed-width vector part-select access after specifying a bit position.
- [bit+: width]+: Increment from the starting bit position, with a bit width of width.
- [bit-: width]-: Decrement from the starting bit position, with a bit width of width.
Example
A = data1[31-: 8] ;
A = data1[31:24] ;
//The following 2 assignments are equivalent
B = data1[0+ : 8] ;
B = data1[0:7] ;
When recombining signals into a new vector, braces are required. For example:
Example
assign temp1 = {byte1[0][7:0], data1[31:8]}; //Data concatenation
assign temp2 = {32{1'b0}}; //Assign a 32-bit value 0
Integer, Real, and Time Register Variables
Data types such as integer, real, and time actually also belong to the register type.
Integer
The integer type is declared with the keyword integer. There is no need to specify the bit width when declaring; the bit width depends on the compiler and is generally 32 bits. reg variables are unsigned, while integer variables are signed. For example:
Example
reg [7:0] byte1 [3:0]; //Array variable, introduced later
integer j ; //Integer variable, used to assist in generating digital circuits
always@* begin
for (j=0; j<=3;j=j+1) begin
byte1[j] = data1[(j+1)*8-1 : j*8];
//Assign data1[7:0]…data1[31:24] to byte1
end
end
In this example, the integer signal j is used as an auxiliary signal to assign the data of data1 to the array byte1 in sequence. After synthesis, the signal j does not actually exist in the circuit; j only assists in generating the corresponding hardware circuit.
Real
Real numbers are declared with the keyword real and can be expressed in decimal or scientific notation. Real declarations cannot have a range, and the default value is 0. If a real number is assigned to an integer, only the integer part of the real number is assigned to the integer. For example:
Example
integer temp ;
initial begin
data1 = 2e3 ;
data1 = 3.75 ;
end
initial begin
temp = data1 ; //The value of temp is 3
end
Time
Verilog uses a special time register variable of type time to store simulation time. Its width is generally 64 bits. The current simulation time is obtained by calling the system function $time. For example:
Example
initial begin
#100 ;
current_time = $time ; //The value of current_time is 100
end
Array
In Verilog, arrays of reg, wire, integer, time, real, and their vector types are allowed.
There is no limit on the number of array dimensions. Net arrays can also be used to connect ports of instantiated modules. Each element in an array can be used as a scalar or a vector in the same way, in the form of:<array_name>[<index>]. For multi-dimensional arrays, the user needs to specify the index for each dimension. For example:
Example
reg [3:0] counter [3:0] ; //An array consisting of 4 4-bit counters
wire [7:0] addr_bus [3:0] ; //An array consisting of 4 8-bit wire variables
wire data_bit[7:0][5:0] ; //Declare a two-dimensional array of 1-bit wire variables
reg [31:0] data_4d[11:0][3:0][3:0][255:0] ; //Declare a 4-dimensional array of 32-bit data variables
The following shows assignment operations to array elements:
Example
counter[3] = 4'hF ; //Assign the value of the 4th element in the array counter to a 4-bit hexadecimal number F, equivalent to counter
assign addr_bus[0] = 8'b0 ; //Assign the value of the first element in the array addr_bus to 0
assign data_bit[0][1] = 1'b1; //Assign the element in row 1, column 2 of the array data_bit to 1. Here the second access index cannot be omitted; that is, assign data_bit
data_4d[0][0][0][0][15:0] = 15'd3 ; //Assign bits 15~0 of the register element labeled
Although arrays and vectors are accessed in a somewhat similar way, do not confuse vectors with arrays. A vector is a single element with a bit width of n; an array is composed of multiple elements, each with a bit width of n or 1. They differ in their structural definition.
Memory
A memory variable is a type of register array that can be used to describe the behavior of RAM or ROM. For example:
Example
reg [7:0] mem[0:1023] ; //1Kbyte memory with an 8-bit width
mem[511] = 8'b0 ; //Set the value of the 512th 8-bit memory cell to 0
Parameter
Parameters are used to represent constants, declared with the keyword parameter, and can only be assigned once. For example:
Example
parameter i=1, j=2, k=3 ;
parameter mem_size = data_width * 10 ;
However, the value of a parameter in a module can be changed through instantiation. This will be introduced later.
Local parameters are declared with localparam. Their function and usage are the same as parameter, except that their value cannot be changed. Therefore, when a parameter is only used within the current module, localparam can be used to declare it.
String
Strings are stored in variables of type reg, with each character occupying one byte (8 bits). Therefore, the width of the register variable should be large enough to prevent overflow.
Strings cannot be written across multiple lines; that is, a string cannot contain carriage return characters. If the width of the register variable is greater than the size of the string, 0s are used to fill the remaining bits on the left; if the width of the register variable is less than the size of the string, the excess data on the left of the string is truncated. For example, to store the string "run.example.com", 14*8 bits of storage are required:
Example
initial begin
str = "run.example.com";
end
Some special characters have special meanings in display strings, such as newline, tab, etc. If you need to display these special characters in a string, you need to prefix them with an escape character\. For example, as shown in the following table:
| Escape character | Displayed character |
|---|---|
| \n | Newline |
| \t | Tab |
| %% | % |
| \ | \ |
| \" | " |
| \ooo | 1 to 3 octal digit characters |
Actually, in SystemVerilog (a programming language mainly used for Verilog simulation), the keyword string can already be used directly to represent the string variable type, which brings great convenience to Verilog simulation. Interested scholars can briefly learn SystemVerilog.