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
Output
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.

memory array in verilog

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, integer and real, 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 reg words, 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
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5