SystemVerilog Arrays, Flexible and Synthesizable

In my last article on plain old Verilog Arrays, I discussed their very limited feature set. In comparison, SystemVerilog arrays have greatly expanded capabilities both for writing synthesizable RTL, and for writing non-synthesizable test benches. In this article, we’ll take a look at the synthesizable features of SystemVerilog Arrays we can use when writing design RTL.

Packed vs Unpacked SystemVerilog Arrays

Verilog had only one type of array. SystemVerilog arrays can be either packed or unpacked. Packed array refers to dimensions declared after the type and before the data identifier name. Unpacked array refers to the dimensions declared after the data identifier name.

bit [7:0] c1;         // packed array of scalar bit
real      u [7:0];    // unpacked array of real

int Array[0:7][0:31]; // unpacked array declaration using ranges
int Array[8][32];     // unpacked array declaration using sizes

Packed Arrays

A one-dimensional packed array is also called a vector. Packed array divides a vector into subfields, which can be accessed as array elements. A packed array is guaranteed to be represented as a contiguous set of bits in simulation and synthesis.

Packed arrays can be made of only the single bit data types (bit, logic, reg), enumerated types, and other packed arrays and packed structures. This also means you cannot have packed arrays of integer types with predefined widths (e.g. a packed array of byte).

Unpacked arrays

Unpacked arrays can be made of any data type. Each fixed-size dimension is represented by an address range, such as [0:1023], or a single positive number to specify the size of a fixed-size unpacked array, such as [1024]. The notation [size] is equivalent to [0:size-1].

Indexing and Slicing SystemVerilog Arrays

Verilog arrays could only be accessed one element at a time. In SystemVerilog arrays, you can also select one or more contiguous elements of an array. This is called a slice. An array slice can only apply to one dimension; other dimensions must have single index values in an expression.

Multidimensional Arrays

Multidimensional arrays can be declared with both packed and unpacked dimensions. Creating a multidimensional packed array is analogous to slicing up a continuous vector into multiple dimensions.

When an array has multiple dimensions that can be logically grouped, it is a good idea to use typedef to define the multidimensional array in stages to enhance readability. But notice the order of the dimensions become a little confusing.

bit [3:0] [7:0] joe [0:9] // 10 elements of 4 8-bit bytes
                          // (each element packed into 32 bits)

typedef bit [4:0] bsix;   // multiple packed dimensions with typedef
bsix [9:0] v5;            // equivalent to bit[9:0][4:0] v5 - thanks Yunsung!

typedef bsix mem_type [0:3]; // array of four unpacked 'bsix' elements
mem_type ba [0:7];           // array of eight unpacked 'mem_type' elements
                             // equivalent to bit[4:0] ba [0:7][0:3] - thanks Yunsung!

SystemVerilog Array Operations

SystemVerilog arrays support many more operations than their traditional Verilog counterparts.

+: and -: Notation

When accessing a range of indices (a slice) of a SystemVerilog array, you can specify a variable slice by using the [start+:increment width] and [start-:decrement width] notations. They are simpler than needing to calculate the exact start and end indices when selecting a variable slice. The increment/decrement width must be a constant.

bit signed [31:0] busA [7:0]; // unpacked array of 8 32-bit vectors
int busB [1:0];               // unpacked array of 2 integers
busB = busA[7:6];             // select a 2-vector slice from busA
busB = busA[6+:2];            // equivalent to busA[7:6]; typo fixed, thanks Tomer!

Assignments, Copying, and other Operations

SystemVerilog arrays support many more operations than Verilog arrays. The following operations can be performed on both packed and unpacked arrays.

A       = B;       // reading and writing the array
A[i:j]  = B[i:j];  // reading and writing a slice of the array
A[x+:c] = B[y+:d]; // reading and writing a variable slice of the array
A[i]    = B[i];    // accessing an element of the array
A       == B;      // equality operations on the array
A[i:j]  != B[i:j]; // equality operations on slice of the array

Packed Array Assignment

A SystemVerilog packed array can be assigned at once like a multi-bit vector, or also as an individual element or slice, and more.

logic [1:0][1:0][7:0] packed_3d_array;

always_ff @(posedge clk, negedge rst_n)
  if (!rst_n) begin
    packed_3d_array <= '0;      // assign 0 to all elements of array
  end
  else begin
    packed_3d_array[0][0][0]   <= 1'b0;         // assign one bit
    packed_3d_array[0][0]      <= 8'h0a;        // assign one element
    packed_3d_array[0][0][3:0] <= 4'ha;         // assign part select
    packed_3d_array[0]         <= 16'habcd;     // assign slice
    packed_3d_array            <= 32'h01234567; // assign entire array as vector
  end

Unpacked Array Assignment

All or multiple elements of a SystemVerilog unpacked array can be assigned at once to a list of values. The list can contain values for individual array elements, or a default value for the entire array.

logic [7:0] a, b, c;
logic [7:0] d_array[0:3];
logic [7:0] e_array[3:0]; // note index of unpacked dimension is reversed
                          // personally, I prefer this form
logic [7:0] mult_array_a[3:0][3:0];
logic [7:0] mult_array_b[3:0][3:0];

always_ff @(posedge clk, negedge rst_n)
  if (!rst_n) begin
    d_array <= '{default:0};      // assign 0 to all elements of array
  end
  else begin
    d_array        <= '{8'h00, c, b, a}; // d_array[0]=8'h00, d_array[1]=c, d_array[2]=b, d_array[3]=a
    e_array        <= '{8'h00, c, b, a}; // e_array[3]=8'h00, e_array[2]=c, e_array[1]=b, d_array[0]=a
    mult_array_a   <= '{'{8'h00, 8'h01, 8'h02, 8'h03},
                        '{8'h04, 8'h05, 8'h06, 8'h07},
                        '{8'h08, 8'h09, 8'h0a, 8'h0b},
                        '{8'h0c, 8'h0d, 8'h0e, 8'h0f}}; // assign to full array
    mult_array_b[3] <= '{8'h00, 8'h01, 8'h02, 8'h03}; // assign to slice of array
  end

Conclusion

This article described the two new types of SystemVerilog arrays—packed and unpacked—as well as the many new features that can be used to manipulate SystemVerilog arrays. The features described in this article are all synthesizable, so you can safely use them in SystemVerilog based RTL designs to simplify coding. In the next part of the SystemVerilog arrays article, I will discuss more usages of SystemVerilog arrays that can make your SystemVerilog design code even more efficient. Stay tuned!

Resources

Sample Source Code

The accompany source code for this article is a toy example module and testbench that illustrates SystemVerilog array capabilities, including using an array as a port, assigning multi-dimensional arrays, and assigning slices of arrays. Download and run it to see how it works!

[lab_subscriber_download_form download_id=11].

36 thoughts on “SystemVerilog Arrays, Flexible and Synthesizable”

  1. Hi,

    I believe you have a mistake at the article.
    Regarding:
    busB = busA[7:6]; // select a 2-vector slice from busA
    busB = busA[6+:1]; // equivalent to busA[7:6]

    but, busA[6+:1] is different from busA[7:6]!
    busA[6+:1] is actually busA[6:6]
    and:
    busA[6+:2] is busA[7:6]

    Regards,
    Tomer

    Reply
  2. Hi Jason,

    I don’t see why we need packed/unpacked array for synthesis. FPGA is not limited with a fix size of memory like computer. The compiler should keep only what we need. Compiler will never allocate useless bits, it’s even opposite.

    Same for packed and unpacked struct. What is the purpose if not only simulation speed?

    Thank you,

    Reply
    • Hi Alexis. If your question is why the packed versions are needed, with traditional synthesis tools you need packed arrays/structs/unions to tell the synthesis tool exactly how many bits and how you want to pack a datastructure. FPGA actually does have limited resources, and each additional (unnecessary) bit of a calculation takes up an additional lookup table. Similarly an extra bit in a calculation will require more gates in an ASIC. I wonder if part of the reason came from this desire for hardware engineers to have complete low level control of the synthesis result.

      However, I think generally synthesis tools and Verilog/SystemVerilog rules are smart enough to figure out the minimum number of bits you want in a calculation. So you could argue since tools are so smart, why do we still need to specify the packing. I don’t have a good answer, but I think one day (maybe soon) you will be right and we won’t need to specify packed structures anymore.

      Reply
      • I see, exactly since the synthesis tools removes the unused bits from the RTL.
        Then even if I set unpacked array, at the end, the RTL will be as if I chose packed array.
        Thank you and sorry for my late reply.

        Reply
  3. Hi
    May I confirm if there is a typo on this example
    typedef bit [4:0] bsix; // multiple packed dimensions with typedef
    bsix [9:0] v5; // equivalent to bit[4:0][9:0] v5

    typedef bsix mem_type [0:3]; // array of four unpacked ‘bsix’ elements
    mem_type ba [0:7]; // array of eight unpacked ‘mem_type’ elements
    // equivalent to bit[4:0][9:0] ba [0:3][0:7]

    Should the last comment be bit[4:0] ba [0:3][0:7] instead of bit[4:0][9:0] ba [0:3][0:7]?

    Reply
      • Got it, thanks.
        By the way, if I have a 3D array, and I want to be able to slice of a 2D portion when accessing it, would you recommend to build a 3D array as “logic [HEIGHT-1:0][WIDTH-1:0] array [DEPTH]” or “logic [WIDTH-1:0] array [DEPTH][HEIGHT]”

        For example, I have several submodules that expect a 2D array input, and I plan to connect them by slicing off the 3D array, so just wondering which way is preferred, “logic [HEIGHT-1:0][WIDTH-1:0] array [DEPTH]” or “logic [WIDTH-1:0] array [DEPTH][HEIGHT]”

        Reply
        • You can slice a 2D array out of a 3D array with both ways of coding, or even “logic array [width][depth][height]”. The difference is whether you want the 2nd (middle) dimension to be packed (left of array name) or unpacked (right of array name). In your example, since each “element” is only a single bit (logic), it probably doesn’t matter which way you code it; I would actually probably code it as “logic array [width][depth][height]” to clearly show each element is only a single bit. But if you really meant a 2D array of multi-bit vector, then I would use “logic [vector_width-1:0] array [depth][height]” to show that more clearly.

          The packed vs unpacked dimensions matter more if you are instead creating multi-dimensional arrays of structs or more complex data types, in which case there may be rules that the data structures must be packed (on the left) for synthesis.

          Reply
  4. 1- Why do we still need to use [7:0]? We can use `busB = busA[0+:8];` and use directly the number of bits. It avoids using paraneter like `bus[PARAM-1:0]`, better to write `bus[0+=PARAM]`

    2- You said `bus[6+:2]` is equivalent to `bus[7:6]` how to use big endianness to get bus[6:7]? I guess it’s not possible.

    Reply
  5. Hi, I am writing a code in system verilog, and I wondered if there is a way to index an array from 1?
    Is this correct, and can I access array a from 1 to q_size?

    logic a [1:q_size];

    Also, unrelated question but I would be glad if you could give me an answer:
    I generate a queue made of number (q_size) of my blocks, which are connected with signals, so I need a number (q_size) of signals, which I intenteded to define as example above. In generate for loop I map signals to ports this way:

    my_module(
    .my_input(a[i+1]) //i is genvar
    )

    Is this correct, can I work with indexes this way?
    Thank you VERY MUCH 🙂

    Reply
    • Hi Ivana. It is certainly possible to index an array from 1, like what you wrote. Hardware designers generally like to think and code with index starting with 0 for convenience (for example in your code you wouldn’t need a +1 if the index started at 0). Yes you can use a genvar to index into a vector. Your code should work.

      Reply
  6. Hi Jason,

    Thanks for the awesome blog posts.
    Arrays with negative indices seem to compile (at least using the Cadence tools). What I am wondering is, do arrays with negative indices actually synthesize?

    parameter integer unsigned width = 0;
    logic [width-1:0] x;

    This compile and in SimVision I actually see x[-1:0] in waves.
    I am curious to know if this is a quirk in the simulator or does this actually synthesize to a two bit logic equivalent?

    Thanks,
    Sami

    Reply
    • Hi Sami. Another interesting question 🙂 I have never used a negative index because that goes against the coding guidelines at work, so I don’t know for sure… but I would guess it would work as expected (if your synthesis tool doesn’t complain).

      Reply
  7. I have a question about the comparison of mixed arrays that I think would fit in here:

    consider the following code:

    module testModule #(
    parameter logic [7:0] TP_A = 8’h01,
    parameter logic [7:0] TP_B = 8’h02,
    parameter logic [7:0] TP_C = 8’h03,
    parameter logic [7:0] TP_D = 8’h04
    )(
    input logic clock_104,
    input logic reset_n,
    input logic [7:0] sampleData [12:0]
    output logic [7:0] errorCnt
    );

    logic [7:0] comp0 [12:0];
    assign comp0 = ‘{TP_A,TP_B,TP_C,TP_D,TP_A,TP_B,TP_C,TP_D,TP_A,TP_B,TP_A,TP_B,TP_C};

    always @(posedge clock_104)
    begin
    if (reset_n == 1’b0)
    errorCnt <= '{default:0};
    else begin
    if (sampleData == {TP_A,TP_B,TP_C,TP_D,TP_A,TP_B,TP_C,TP_D,TP_A,TP_B,TP_A,TP_B,TP_C} ) begin // this generates an error of illegal comparison to unpacked array
    if (sampleData == comp0 ) begin // this works fine

    Why does one if statement work fine and the other one creates an error? Are they not doing the same thing?

    Thank you for your help!

    Reply
    • Hi Tom. I tried some variations of your experiment.

      A pure array literal. This worked.
      if (sampleData == ‘{8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00, 8’h00}

      Add an apostrophe in front of the array:
      if (sampleData == ‘{TP_A,TP_B,TP_C,TP_D,TP_A,TP_B,TP_C,TP_D,TP_A,TP_B,TP_A,TP_B,TP_C} )

      The simulator (VCS 2014.10) then reported the following error:

      Error-[SV-IUAP] Illegal use of assignment pattern
      design.sv, 22
      “(sampleData == ‘{TP_A, TP_B, TP_C, TP_D, TP_A, TP_B, TP_C, TP_D, TP_A, TP_B, TP_A, TP_B, TP_C})”
      An assignment pattern can only be used in one of the sides of an
      assignment-like context.
      Please consider rewriting it using individual array elements.

      I suspect the simulator is somehow trying to do two levels of assignments in the error case, and that cannot be done within a comparison? However, I couldn’t find a statement in the LRM that explained why it is not allowed either…

      Reply
  8. Hi Jason,
    Thank you for the wonderful forum.
    I have a few queries.
    How can I check the bit order ( MSB first and LSB last) of a packed array?
    e.g. input logic[7:0] test1;
    and output logic [7:0] out1;
    Would be kind to explain me.

    Thank you.

    Reply
    • There are several SystemVerilog system tasks that you can use to do that. See section 20.7 Array Querying Functions of the 2012 SystemVerilog LRM. Some examples are:

      • $left shall return the left bound of the dimension. For a packed dimension, this is the index of the most significant element. For a queue or dynamic array dimension, $left shall return 0.
      • $right shall return the right bound of the dimension. For a packed dimension, this is the index of the least significant element. For a queue or dynamic array dimension whose current size is zero, $right shall return –1.
      • For a fixed-size dimension, $increment shall return 1 if $left is greater than or equal to $right and –1 if $left is less than $right. For a queue or dynamic array dimension, $increment shall return –1.
      • $low shall return the same value as $left if $increment returns –1, and the same value as $right if $increment returns 1.
      • $high shall return the same value as $right if $increment returns –1, and the same value as $left if $increment returns 1.
      • $size shall return the number of elements in the dimension, which is equivalent to: $high – $low + 1.
      Reply
  9. I’d like to try this, but not having much luck.. for example, I’ve created an .sv module that declares eight, 16-bit registers:

    module test
    (
    output reg [ 15 : 0 ] regTest[ 0 : 7 ]

    );

    Then in another .sv file, I try to instantiate like this:

    wire [15:0]test[0:7];
    test test_u0
    (
    .regTest (test)

    );

    However the above says an error that it cannot bind.. I’ve tried using the packed syntax, but it doesn’t seem to make any difference.. any suggestion what might be the problem?

    Thanks!
    Ben

    Reply
  10. Hi want to extract only columns in packed 2-D array, for eg
    logic [255:0][299:0] array1
    has 256 rows each having 300 bits ; if I wanted to extract 1st column through array slicing operation how do I do it?
    Like array1[0] gives first row array1[1] gives second row etc ;
    Thanks a lot

    Reply
    • Hi Venkat. The array definition makes extracting one dimension easier than the other, so you should be careful about how you define the array. To accomplish what you want to do, you can write a loop to extract each element that you need and assign that element to a new 256-entry single dimensional array.

      Reply
  11. Hi Jason,

    Can I include an unpacked array in a struct or an interface? For example, I like to pass an FIFO and some other info of a module to another to snooping. can I have a struct like this:
    typedef struct packed {
    logic [5:0] dev_sel;
    logic [31:0] wr_pending_fifo_dv;
    logic [31:0] wr_pending_fifo_mem [128];
    } dev_wr_fifo_s;

    Would an interface have the same elements?

    Thanks
    Agnes

    Reply
    • Hi Agnes. If you create an unpacked struct (typedef struct, without the packed keyword), then you can have an unpacked array like the wr_pending_fifo_mem in the structure. However, Design Compiler will not synthesize an unpacked struct. What are you trying to achieve? Putting a FIFO into a struct seems to be a bit of a strange construct.

      Reply
  12. Always thanks to your post. Again, I hope to ask clarify below comment.
    ///////////////////////////////////////////////////////////////////////////
    bit [3:0] [7:0] joe [0:9] // 10 elements of 4 8-bit bytes
    // (each element packed into 32 bits)
    typedef bit [4:0] bsix; // multiple packed dimensions with typedef
    bsix [9:0] v5; // equivalent to bit[4:0][9:0] v5
    typedef bsix mem_type [0:3]; // array of four unpacked ‘bsix’ elements
    mem_type ba [0:7]; // array of eight unpacked ‘mem_type’ elements
    // equivalent to bit[4:0] ba [0:3][0:7] – thanks Dennis!
    ///////////////////////////////////////////////////////////////////////////
    Considering its semantic, I think it needs to be fixed as below.
    In my previous experience, I was also confused for that.
    1. equivalent to bit[4:0][9:0] v5 => equivalent to bit[9:0][4:0] v5
    2. equivalent to bit[4:0] ba [0:3][0:7] => equivalent to bit[4:0] ba [0:7][0:3]

    Reply
  13. Is accessing to array slices with dynamic indexing but fixed width synthesizable?
    For example:

    // input_byte is a 8 bit logic input
    // input_index is 3 bit logic input
    localparam int unsigned FIXED_WIDTH = 8;
    logic[63:0] data_array;
    data_array[input_index *FIXED_WIDTH +: FIXED_WIDTH] <= input_byte;

    Reply
    • Hi Veli. I think that should be synthesizable. I have written similar code where the LHS is a dynamic index to an array. I have not additionally used a fixed width slice, but I think since the width is a fixed parameter, it shouldn’t be a problem. A dynamic index and a dynamic width, I think would be a problem.

      Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.