What You'll Learn
- How to declare scalar, vector and multi-dimensional arrays of reg, wire, integer and real
- How to assign and read array elements, and how a memory differs from a register vector
- How to model a simple memory with a reg array
- Common array and memory mistakes, and how to avoid them
What is a Verilog array ?
An array declaration of a net or variable can be either scalar or vector. Any number of dimensions can be created by specifying an address range after the identifier name and is called a multi-dimensional array. Arrays are allowed in Verilog for reg, wire, integer and real data types.
reg y1 [11:0]; // y is a scalar reg array of depth=12, each 1-bit wide
wire [0:7] y2 [3:0] // y is an 8-bit vector net with a depth of 4
reg [7:0] y3 [0:1][0:3]; // y is a 2D array rows=2,cols=4 each 8-bit wide
An index for every dimension has to be specified to access a particular element of an array and can be an expression of other variables. An array can be formed for any of the different data-types supported in Verilog.
Note that a memory of n 1-bit reg is not the same as an n-bit vector reg.
Array Assignment
y1 = 0; // Illegal - All elements can't be assigned in a single go
y2[0] = 8'ha2; // Assign 0xa2 to index=0
y2[2] = 8'h1c; // Assign 0x1c to index=2
y3[1][2] = 8'hdd; // Assign 0xdd to rows=1 cols=2
y3[0][0] = 8'haa; // Assign 0xaa to rows=0 cols=0
Array Example
The code shown below simply shows how different arrays can be modeled, assigned and accessed. mem1 is an 8-bit vector, mem2 is an 8-bit array with a depth of 4 (specified by the range [0:3]) and mem3 is a 16-bit vector 2D array with 4 rows and 2 columns. These variables are assigned different values and printed.
module des ();
reg [7:0] mem1;
reg [7:0] mem2 [0:3];
reg [15:0] mem3 [0:3][0:1];
integer i, j;
initial begin
mem1 = 8'ha9;
$display ("mem1 = 0x%0h", mem1);
mem2[0] = 8'haa;
mem2[1] = 8'hbb;
mem2[2] = 8'hcc;
mem2[3] = 8'hdd;
for(i = 0; i < 4; i = i+1) begin
$display("mem2[%0d] = 0x%0h", i, mem2[i]);
end
for(i = 0; i < 4; i = i+1) begin
for(j = 0; j < 2; j = j+1) begin
mem3[i][j] = i + j;
$display("mem3[%0d][%0d] = 0x%0h", i, j, mem3[i][j]);
end
end
end
endmodule
ncsim> run mem1 = 0xa9 mem2[0] = 0xaa mem2[1] = 0xbb mem2[2] = 0xcc mem2[3] = 0xdd mem3[0][0] = 0x0 mem3[0][1] = 0x1 mem3[1][0] = 0x1 mem3[1][1] = 0x2 mem3[2][0] = 0x2 mem3[2][1] = 0x3 mem3[3][0] = 0x3 mem3[3][1] = 0x4 ncsim: *W,RNQUIE: Simulation is complete.
What are memories ?
Memories are digital storage elements that help store a data and information in digital circuits. RAMs and ROMs are good examples of such memory elements. Storage elements can be modeled using one-dimensional arrays of type reg and is called a memory. Each element in the memory may represent a word and is referenced using a single array index.
Register Vector
Verilog vectors are declared using a size range on the left side of the variable name and these get realized into flops that match the size of the variable. In the code shown below, the design module accepts clock, reset and some control signals to read and write into the block.
It contains a 16-bit storage element called register which simply gets updated during writes and returns the current value during reads. The register is written when sel and wr are high on the same clock edge. It returns the current data when sel is high and wr is low.
module des ( input clk,
input rstn,
input wr,
input sel,
input [15:0] wdata,
output [15:0] rdata);
reg [15:0] register;
always @ (posedge clk) begin
if (!rstn)
register <= 0;
else begin
if (sel & wr)
register <= wdata;
else
register <= register;
end
end
assign rdata = (sel & ~wr) ? register : 0;
endmodule
The hardware schematic shows that a 16-bit flop is updated when control logic for writes are active and the current value is returned when control logic is configured for reads.

Memory Example
In this example, register is an array that has four locations with each having a width of 16-bits. The design module accepts an additional input signal which is called addr to access a particular index in the array.
module des ( input clk,
input rstn,
input [1:0] addr,
input wr,
input sel,
input [15:0] wdata,
output [15:0] rdata);
reg [15:0] register [0:3];
integer i;
always @ (posedge clk) begin
if (!rstn) begin
for (i = 0; i < 4; i = i+1) begin
register[i] <= 0;
end
end else begin
if (sel & wr)
register[addr] <= wdata;
else
register[addr] <= register[addr];
end
end
assign rdata = (sel & ~wr) ? register[addr] : 0;
endmodule
It can be seen in the hardware schematic that each index of the array is a 16-bit flop and the input address is used to access a particular set of flops.

Industry Application
In the real world, the most common application for Verilog memories and arrays is the implementation of a Network Packet Buffer or a FIFO (First-In-First-Out) Queue. These are critical components in devices like Ethernet switches, Wi-Fi routers, and CPU-to-peripheral interfaces. They act as "waiting rooms" for data when the sender is faster than the receiver.
Common Beginner Mistakes
Mistake #1: Confusing Array Declaration Dimensions
Beginners often swap the "width" of a word with the "depth" of the memory. In reg [7:0] mem [0:255], the [7:0] defines an 8-bit word, while [0:255] defines 256 entries. Reversing these or forgetting the second set of brackets results in a single, massive 2048-bit register instead of a memory array.
Mistake #2: Attempting to Access an Entire Array at Once
In standard Verilog, you cannot assign one entire array to another in a single line (e.g., mem_a = mem_b) or use an array as a single port in a module. You must iterate through the elements using a for loop or access a specific index like mem[address]. SystemVerilog relaxes this, but in vanilla Verilog, it will trigger a syntax error.
Mistake #3: Using Non-Constant Expressions in Port Connections
When passing an array element to a module port, beginners often try to use a variable index like .data_in(mem[i]). While this works in simulation, many synthesis tools struggle with dynamic indexing in port maps. It is safer to assign the indexed value to a temporary wire or reg before passing it to a sub-module.
Mistake #4: Forgetting the Multi-Dimensional Indexing Syntax
To access a specific bit within a memory word, you must use two sets of indices: mem[word_index][bit_index]. A common mistake is trying to use mem[word_index, bit_index] or mem[index_range], which is not valid Verilog syntax for memory arrays and will cause the compiler to fail.
Best Practice: When modeling large memories (RAM/ROM) for synthesis, use a coding style that allows the tool to infer "Block RAM" (BRAM) rather than individual flip-flops. This usually involves a clocked always block with a single read/write address to save significant hardware resources.
Practice Exercise
Challenge: Fill and Read a Memory
Declare a memory of 16 words, each 8 bits wide. Fill word i with the value i * 3 in a loop, then display words 0, 5 and 15.
Hint
Declare it as reg [7:0] mem [0:15]; and use an integer loop variable. Each word needs its own index, so the whole array cannot be assigned at once.
Solution
module tb;
reg [7:0] mem [0:15]; // 16 words of 8 bits
integer i;
initial begin
for (i = 0; i < 16; i = i + 1)
mem[i] = i * 3; // One word per iteration
$display("mem[0]=%0d mem[5]=%0d mem[15]=%0d", mem[0], mem[5], mem[15]);
end
endmodule
The output is mem[0]=0 mem[5]=15 mem[15]=45. The largest value, 45, fits in 8 bits. A single statement such as mem = 0; would be illegal, because an array element must be accessed through its index.
Summary
In this tutorial, you learned:
- An array is declared with its range after the name, as in
reg [7:0] mem [0:255]: the first range is the word width and the second is the depth - Arrays can be made of
reg,wire,integerandreal, with any number of dimensions - An element is accessed with one index per dimension, and a whole array cannot be assigned in one statement
- A memory is a one-dimensional array of
regwords, selected by an address - A register vector is one wide register, which is not the same as a memory of 1-bit words
- A memory written in a clocked block and read into a register can be mapped to RAM by synthesis