The unpacking operation is the reverse of packing: it distributes a single high granularity value to lower granularity values. In order to perform an unpack operation, the streaming operator must be used on the left hand side of an assignment. The article’s sections below detail typical unpacking operations:
- 1. Unpacking an int into byte variables
- 2. Unpacking a packed array into an unpacked array
- 3. Unpacking an array of bytes
- 3.1. Array of bytes into byte variables
- 3.2. Array of bytes into queue of bytes
- 4. Reversing the elements of a byte array
- 5. Unpack an array into the fields of a structure
- 6. Unpacking a structure into another structure or a class
- References
1. Unpacking an int into byte variables
A 32-bit int variable can be unpacked into four byte (8-bit) variables in the following way:
module example_1;
initial begin
static int value = 32'h8C00A4FF;
static byte a;
static byte b;
static byte c;
static byte d;
{>>{a, b, c, d}} = value;
$display("a = 0x%h", a);
$display("b = 0x%h", b);
$display("c = 0x%h", c);
$display("d = 0x%h", d);
end
endmodule
If only three byte variables are used (a, b, and c), the least significant byte of the int variable (0xFF) will be discarded. If five or more variables are used, the extra variables will be set to 0, regardless of their previous value.
2. Unpacking a packed array into an unpacked array
Instead of writing unpacked_array = ‘{ packed_array[2], packed_array[1], packed_array[0] }, a left-to-right ( {>>{}} ) streaming operator can be used to get the same result. The syntax is useful when the array size gets larger.
module example_2;
initial begin
static bit [2:0] packed_array = 3'b011;
static bit unpacked_array[3];
{>>{unpacked_array}} = packed_array;
foreach (unpacked_array[i])
$display("unpacked_array[%0d] = %b", i, unpacked_array[i]);
end
endmodule
3. Unpacking an array of bytes
3.1. Array of bytes into byte variables
The left-to-right streaming operator ( {>>{}} ) can be used to copy items from an array and place them into distinct variables of the same size. You can see it as a shorthand operator for copying array values.
module example_3_1;
initial begin
static byte array[4] = '{ 8'h8C, 8'h00, 8'hA4, 8'hFF };
static byte a;
static byte b;
static byte c;
static byte d;
{>>{a, b, c, d}} = array;
$display("a = 0x%h", a);
$display("b = 0x%h", b);
$display("c = 0x%h", c);
$display("d = 0x%h", d);
end
endmodule
3.2. Array of bytes into queue of bytes
As above, the left-to-right streaming operator can be used to copy items from an array and place them into a queue. You can see it as a shorthand operator for transforming arrays into queues.
module example_3_2;
initial begin
static byte array[4] = '{ 8'h8C, 8'h00, 8'hA4, 8'hFF };
static byte queue[$];
{>>{queue}} = array;
foreach (queue[i])
$display("queue[%0d] = 0x%h", i, queue[i]);
end
endmodule
4. Reversing the elements of a byte array
When you want to change the order of the elements inside an array, right-to-left streaming operator ( {<<{}} ) can be of great help. Below is an example of reversing the order of the elements inside an array. The slice size of the right-to-left streaming operator should be equal to the size of the element’s type (byte in our case).
module example_4;
initial begin
static byte array_a[4] = '{ 8'h8C, 8'h00, 8'hA4, 8'hFF };
static byte array_b[4];
{<<byte{array_b}} = array_a;
foreach (array_b[i])
$display("array_b[%0d] = 0x%h", i, array_b[i]);
end
endmodule
If you want to reverse the order of the elements inside an array using a granularity of 2 elements (one element = byte = 8 bits, two elements = shortint = 16 bits), you can use {<<shortint{array_b}} = array_a. This will make array_b equal to ‘{8’hA4, 8’hFF, 8’h8C, 8’h00} .
5. Unpack an array into the fields of a structure
Data from a parallel or serial bus might initially get collected inside an array. The meaning of data can then be revealed by unpacking it into a more abstract level, like a structure. Here is how you can unpack array values into the fields of a structure.
module example_5;
typedef struct {
bit [3:0] address;
bit [3:0] data;
} packet_t;
initial begin
static bit [1:0] array[] = '{ 2'b10, 2'b01, 2'b11, 2'b00 };
static packet_t packet;
{>>{packet.address, packet.data}} = array;
$display("packet address = %b", packet.address);
$display("packet data = %b", packet.data);
end
endmodule
Instead of manually specifying all the fields, the following syntax can be used: {>>{packet}} = array;
6. Unpacking a structure into another structure or a class
Streaming operators can be used to transform a structure of a specific type into a structure of another type. In a similar way as mentioned in the first example, if the structure sizes are different, either trimming or zero-padding will be performed when the assignment is done.
module example_6_1;
typedef struct {
bit [3:0] high_nibble;
bit [3:0] low_nibble;
bit [4:0] id;
} layer1_t;
typedef struct {
bit [7:0] address;
bit [3:0] data;
bit crc;
} frame_t;
initial begin
static layer1_t layer1 = '{ 4'b1000, 4'b1100, 5'b11101 };
static frame_t frame;
{>>{frame}} = layer1;
$display("frame address = 0x%h", frame.address);
$display("frame data = 0x%h", frame.data);
$display("frame crc = %b" , frame.crc );
end
endmodule
When a class is involved in the unpack operation, the same transform operation using the left-to-right streaming operator ( {>>{}} ) can be applied, but this time the class fields must be explicitly specified.
module example_6_2;
typedef struct {
bit [3:0] high_nibble;
bit [3:0] low_nibble;
bit [4:0] id;
} layer1_t;
class frame_t;
bit [7:0] address;
bit [3:0] data;
bit crc;
endclass
initial begin
static layer1_t layer1 = '{ 4'b1000, 4'b1100, 5'b11101 };
static frame_t frame = new;
{>>{frame.address, frame.data, frame.crc}} = layer1;
$display("frame address = %b", frame.address);
$display("frame data = %b", frame.data);
$display("frame crc = %b", frame.crc );
end
endmodule
The picture below illustrates the unpack operation for both code examples.
References
The above diagrams have been created using an open source design tool called Inkscape.
That’s all for now! Stay tuned for another tutorial about packing and unpacking using the predefined UVM methods.
13 Responses
I never knew about the SystemVerilog stream operator. This is incredibly useful.
I’m glad it helped you!
I think all the unpacking statements above need the ‘assign’ keyword.
Hi Tuan, according to the SystemVerilog standard, it is not legal to use assign statements in initial blocks.
In #2 should it read “unpacked_array = ‘{ packed_array[0], packed_array[1], packed_array[2] }” instead?
Hi Winston,
Not really. The left-to-right streaming operator ( {>>{}} ) will insert the leftmost bit of packed_array (bit 2) in the leftmost element of unpacked_array (element 0).
If you take a close look at the assignment:
unpacked_array = ‘{ packed_array[2], packed_array[1], packed_array[0] }
you will notice that this is the case: packed_array[2] is assigned to index 0 of unpacked_array, packed_array[1] is assigned to index 1 of unpacked_array and packed_array[0] is assigned to index 2 of unpacked_array.
Also check the picture in the second example, as it’s easier to see where each of the bits ends up in the unpacked array.
Regards,
Horia
Is this synthesis friendly or any experience that would behave differently?
Hello, John.
We have not analyzed these streaming operators from the synthesis point of view, but only from the verification perspective. Still, your question is of interest and according to this paper, they are synthesizable:
https://sutherland-hdl.com/papers/2013-SNUG-SV_Synthesizable-SystemVerilog_presentation.pdf
https://sutherland-hdl.com/papers/2013-SNUG-SV_Synthesizable-SystemVerilog_paper.pdf
If you experience a different behavior, probably the best thing to do is to contact your synthesis tool vendor.
Hi thanks for this amzing article.
i have one question about the example 3.2. Array of bytes into queue of shortints.
i tried on vcs, and found that the results queue has only 2 elements, queue[0]=16’h8c00, queue[1]=16’ha4ff. is there something i misunderstand?
Hi, kr.
No, you are not misunderstanding. You’ve just found a bug in the code.
The queue definition is wrong. Instead of:
we should be using
I’ve fixed it in the article as well.
Thank you for pointing this out.
I’m glad you appreciate this article.
The original code (https://web.archive.org/web/20210506010632/https://www.consulting.amiq.com/2017/06/23/how-to-unpack-data-using-the-systemverilog-streaming-operators/#array_of_bytes_into_queue_of_shortints) was correct and the image was the one that contained a bug: the resulting queue should have been indeed depicted with only two 16-bit elements, as kr noticed, to show that an array of bytes can be unpacked into a queue of a different type (shortint).
I ususlly put the streaming operaton on the right hand side of an assignment, and never seen an issue.
After reading this I tried in Questa simulation variations of example_1:
From
{>>{a, b, c, d}} = value;
to
{a, b, c, d} = {>>{value}};
and
{a, b, c, d} = {>>8{value}};
And in both cases I got the same results
Is it possible to use this operator with higher dimension arrays?