Lesson 04: Verilog Scalar, Vector, and Array
Scalar
Scalar
A net or reg declaration without a range specification is considered 1-bit wide and is a scalar.
wire nor_o; // Single bit scalar net reg parity; // Single bot scalar variable
Vector
Vector (Packed Array, Bus)
If a range is specified, then the net or reg becomes a multibit entity known as a vector. A vector is also called a packed array, or a bus. Vectors are used to group related signals using one name to make them more convenient to manipulate. For example:
The syntax of vector:
- BIT_INDEXLEFT < BIT_INDEXRIGHT: [lsb_index:msb_index] Big-endian bit order.
For example, reg [0:7] op; - BIT_INDEXLEFT > BIT_INDEXRIGHT: [msb_index:lsb_index] Little-endian bit order.
For example, wire [31:0] address;
Where msb_index is the most significant bit index and lsb_index is the least.
The sequence of a multiple-bits binary stored in memory is called bit-order. There are two types of bit-orders:
- Big-Endian Bit Order: The most significant bit is stored at the lowest bit address.
- Little-Endian Bit Order: The least significant bit is stored at the lowest bit address.
Most digital systems use the little-endian bit-order to store binary numbers, which means the bit 0 value is stored in the index=0 location.
Vectors are indexed from BIT_INDEXLEFT to BIT_INDEXRIGTH, that range gives the ability to address individual bits in a vector. Vectors can be declared for all types of net data types and reg data types. Specifying vectors for integer, real, realtime, and time data types is illegal. Vector nets and registers are treated as unsigned values.
Notice that the declaration of a vector places the dimensions before the name of the vector, which is unusual compared to C syntax. However, the part selected has the dimensions after the vector name as you would expect.
wire [15:0] address; // Declare a 16-element vector, or 16-bit Address Bus
assign out = address[10]; // Part-select one bit out of the vector
reg [3:0] a; // Little-Endina Bit-Order
reg [0:3] b; // Big-Endian Bit-Order
wire c;
a = 4'b1100; // a[0] = 0, a[1] = 0, a[2] = 1, a[3] = 1
b = 4'b1100; // b[0] = 1, b[1] = 1, b[2] = 0, a[3] = 0
c = (a == b); // c = 1
Switching Bits Ends
Switching Bits Ends
Verilog does not auto-convert the different bit-orders. The following code assigns big-endian to little-endian, but it would not work.
wire [0:7] be_byte; // 8-bit wire (big-endian)
reg [7:0] le_byte; // 8-bit reg (little-endian)
always @(posedge clk)
le_byte <= be_byte; // Won't work :-(
Instead, you need to reverse the bits explicitly. All bits are swapped in parallel:
always @(posedge clk) begin
le_byte[0] <= be_byte[7];
le_byte[1] <= be_byte[6];
le_byte[2] <= be_byte[5];
le_byte[3] <= be_byte[4];
le_byte[4] <= be_byte[3];
le_byte[5] <= be_byte[2];
le_byte[6] <= be_byte[1];
le_byte[7] <= be_byte[0];
end
Or in this case, a for-loop can be used to update each individual bit.
always @(posedge clk) begin
for (i = 0; i < 8; i = i + 1)
le_byte[i] <= be_byte[7-i];
end
Verilog for is NOT like a software loop, it will be unrolled into parallel bit swaps as previous code.
Single Unit Select
Single Unit Select
The vector can be assigned as single units.
reg [7:0] address; // 8-bit vector reg variable [7, 6, 5, 4, 3, 2, 1, 0]
address = 8'b1010_1111; // Assign 10101111 to address variable
Bit-Select
Bit-Select
Any bit in a vectored variable can be individually selected and assigned a new value, as known in the below example. This is called a bit select.
reg [7:0] address; // 8-bit vector reg variable [7, 6, 5, 4, 3, 2, 1, 0]
address[0] = 1; // Assign 1 to bit 0 of address
address[4] = 1; // Assign 1 to bit 4 of address
address[8] = 1; // illegal: index 8 does not exist in address variable
If the bit-select is out of the address bounds or the bit-select is x or z, then the value returned by reference shall be x.
Part-Select
Part-Select
The selection of the range of contiguous bits is called the part selected. There are two types of part-selects.
- Fixed Part-Select
- Indexed Part-Select
Fixed Part-Select
A fixed part-select of a vector reg or net is given with the following syntax:
reg [7:0] address;
address[7:5] = 3'b101; // bits 7 to 5 will be replaced by the new value 'b101 --> fixed-select
Indexed Part-Select
An indexed part-select of a vector net, vector variable is given with the following syntax:
reg [7:0] data;
data[5+:3] = 3'b010; // starting bit = 5, width = 3 ==> data[7:5]
reg [255:0] data1; // Little-endian bit order notation
reg [0:255] data2; // Big-endian bit order notation
reg [7:0] byte;
// Using a variable part-select, one can choose parts
byte = data1[31-:8]; // starting bit = 31, width =8 => data1[31:24]
byte = data1[24+:8]; // starting bit = 24, width =8 => data1[31:24]
byte = data2[31-:8]; // starting bit = 31, width =8 => data1[24:31]
byte = data2[24+:8]; // starting bit = 24, width =8 => data1[24:31]
// The starting bit can also be a variable. The width has to be constant.
// Therefore, one can use the variable part-select in a loop to select all bytes of the vector
for (i = 0; i <= 31; i = i + 1)
byte = data1[(i*8)+:8]; // Sequence is [7:0], [15:8], ..., [255:248]
// Can initialize a part of the vector
data1[(byteNum*8)+:8] = 8'b0; // If byteNum = 1, clear 8 bits [15:8]
Common Errors
Common Errors
reg [15:0] data;
data[0:9] = 10'h1F2; // Error: Reversed part-select index expression ordering
Multi-Dimension Vector (Multiple Packed Array Dimensions) is a SystemVerilog feature,
// Multiple Packed Array on support by SystemVerilog
reg [0:2][0:2][0:2] rgb;
rgb[0][0][0] = 1'b0; // Assign one bit
rgb[0][0] = 3'h0A; // Assign one element
rgb[0] = 9'hbcd; // Assign slice
rgb = 27'h01234; // Assign entire array as vector
Arrays
Arrays
An array declaration of a net or variable can be either scalar or vector. It is also called an unpacked array.
Array are allowed in Verilog for reg, integer, time, real, realtime and vector register data types.
A multi-dimensional array can be declared by having multiple dimensions after the array declaration. Each fixed-size dimension is represented by an address range, such as [0:1023].
SystemVerilog also supports a single value range, which is a single positive number to specify the size of a fixed-size array, such as [1024]. The notation size is equivalent to [0:size-1].
reg m1 [0:11]; // m1 is an scalar reg array of depth=12, each 1-bit wide
reg [7:0] m2 [0:3]; // m2 is an 8-bit vector array reg with a depth=4
reg [7:0] m3 [0:1][0:3]; // m3 is an 8-bit vector 2D array rows=2, cols=4 each 8-bit wide
reg [63:0] m4 [0:15][0:7][0:7][0:255]; // Four dimensional array
integer count[0:7]; // An array of 8 count variables
integer matrix[4:0][0:255]; // Two dimensional array of integer
time chk_point[1:100]; // Array of 100 time checkpoint variables
wire [7:0] w_array2 [0:5]; // Declare an array of 8=bit vector wire
wire w_array1 [7:0][5:0]; // Declare an array of single bit wires
// Single value range allowed only in SystemVerilog.
reg [7:0] m4 [128] // m4 is an 8-bit vectore array with 128 rows ==> same as reg [7:0] m4 [0:127]
In C language, arrays are indexed from 0 by integers or converted to pointers. The whole array can be initialized, and each element must be read or separately written in procedural statements.
In Verilog, arrays are indexed from INDEXFIRST to INDEXLAST. To access the array, the index number for every dimension has to be specified to access a particular element of an array. It can be an expression of other variables. The following statements are shown examples of accessing the array.
m1 = 1; // Illegal - m1 is an array. All elements in m1 can not be assigned in a single assignment
m1[0] = 1'b0; // Assign 0 to m1 first element
m2[0] = 8'hE2; // Assign 0xE2 to index=0
m2[2] = 8'h1A; // Assign 0x1A to index=2
m3[1][2] = 8'hBB; // Assign 0xBB to rows=1 cols=2
m3[0][0] = 8'hCC; // assign 0xCC to rows=0 cols=0