Dimensions before the name are packed. The whole array is one contiguous vector that supports arithmetic, comparison and part-selects.
Dimensions after the name are unpacked. Each element is independent, so whole-array arithmetic is illegal, while whole-array copy and == work element by element on matching shapes.
Packed | Unpacked | |
|---|---|---|
Declaration |
|
|
Meaning | One 8-bit vector | 1024 independent bytes |
| Legal | Illegal: no whole-array arithmetic |
| Legal | Legal when both arrays have the same element type and shape |
| Legal: selects bits | Legal: selects whole elements, not bits |
Element type |
| Anything, including classes |
The declaration logic [7:0] mem [0:1023] reads as 1024 unpacked elements of packed 8-bit vectors. That is exactly the shape of a byte memory.
Packed dimensions apply to single-bit types (bit, logic, reg), packed structs and unions, and enums. Integer types such as byte, shortint and int already count as packed vectors, but they cannot take extra packed dimensions: int [3:0] x is illegal, while int x [4] (unpacked) is fine.
Packed ranges conventionally run [msb:lsb]. Unpacked ranges traditionally run [0:n-1], with [n] as shorthand for [0:n-1].
Index unpacked dimensions first from left to right, then packed dimensions from left to right. Bit part-selects need a packed dimension. On an unpacked dimension, [3:0] and +: select a slice of whole elements.
Declaration: packed dimensions sit before the name, unpacked dimensions after it.
Indexing: consume unpacked dimensions first left to right, then packed dimensions left to right.
Slicing: bit part-selects apply to packed dimensions only; unpacked slices select whole elements.
Copies: whole-array assignment and equality need identical element type and shape on both sides.
bus + 1 compile for a packed array but not for an unpacked one?Only packed arrays are vectors. Arithmetic is a vector operation, and unpacked arrays are collections of independent elements.
logic [31:0] mem [0:4095] synthesizes to RAM, but one flat 131072-bit vector does not. Why?RAM inference looks for unpacked elements of packed words with an indexed read and a clocked write. One flat vector looks like discrete flops, so the tool builds flops.
32'hDEAD_BEEF to frame[0], then 8'hAA to lane 3, then clears bit 7 of lane 3. What do frame[0][3] and frame[0][0] hold?8'h2A and 8'hEF. Lane 3 is bits 31:24, so it becomes AA and then loses its top bit; lane 0 keeps the EF from the first write.
Layout, lane access, slicing and copy rules, proved with one self-checking testbench.
module tb_packed_layout;
logic [3:0][7:0] frame [0:1]; // 2 unpacked rows of packed 4-lane vectors
logic [7:0] a [0:3];
logic [7:0] b [0:3];
int errors = 0;
initial begin
frame[0] = 32'hDEAD_BEEF; // whole packed value of row 0
frame[0][3] = 8'hAA; // lane 3 (bits 31:24) of row 0
frame[0][3][7] = 1'b0; // clear top bit of lane 3: AA -> 2A
frame[1][1:0] = 16'hCAFE; // packed slice: lanes 1..0
if (frame[0][3] !== 8'h2A) errors++;
if (frame[0][0] !== 8'hEF) errors++;
if (frame[1][1:0] !== 16'hCAFE) errors++;
a = '{8'h11, 8'h22, 8'h33, 8'h44};
b = '{default: 8'h00};
b = a; // legal: identical shape, element-wise copy
// c = a; // ILLEGAL when c has 8 elements vs 4
if (a != b) errors++;
if (b[2] !== 8'h33) errors++;
if (errors != 0) $fatal(1, "FAIL: packed layout self-check");
$display("PASS: packed lanes and slices read back, identical-shape copies compare equal");
$finish;
end
endmodulePASS: packed lanes and slices read back, identical-shape copies compare equalRun with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. Icarus 13 cannot run this bench (it rejects the default: assignment pattern).