This post is the first in a series of tutorials about packing and unpacking in SystemVerilog.
The article’s sections are:
- Introduction
- 1. Pack bytes into an int
- 1.1 Byte variables to an int
- 1.2 Array of bytes to an int
- 2. Reverse the elements of a byte array and pack them into an int
- 3. Reverse the bits in a byte
- 4. Reverse the nibbles in a byte
- 5. Reverse the bits of an array and pack them into a shortint
- 6. Advanced packing
- References
Introduction
When doing packing/unpacking operations in SystemVerilog, a simple bit-stream cast is often enough:
typedef struct {
bit [7:0] address;
bit [7:0] payload[2];
} packet;
typedef bit [7:0] data_stream[$];
// ...
packet pkt;
data_stream stream;
// ...
stream = { stream, data_stream'(pkt) };
For extra flexibility, streaming operators can be used in the cases where the bit ordering is important or a simple bit-stream cast is not sufficient..
There are two streaming operators, {>>{}} and {<<{}}, which operate on data blocks (or slices). By default, slices have the size 1, but the slice size can be changed according to the needs. Using {>>{}} will cause the data blocks to be streamed from left to right, while {<<{}} will stream the data blocks from right to left.
Because an image is worth a thousand words, I’ll use graphics to show how to use the streaming operators and how the individual bits are affected by the stream operators.
1. Pack bytes into an int
1.1 Byte variables to an int
When we need to pack several variables into a single variable, we can use the left-to-right streaming operator ( {>>{}} ).
module example_1_1;
initial begin
static byte a = 8'h8C;
static byte b = 8'h00;
static byte c = 8'hA4;
static byte d = 8'hFF;
static int value = {>>{a, b, c, d}};
$display("value = 0x%h", value);
end
endmodule
Specifying a slice size for left-to-right streaming operator ( {>>{}} ) has the same effect as using the default slice size of 1. This means that {>>{}} is the same as {>>4{}}, {>>8{}}, or any other value.
1.2 Array of bytes to an int
Packing an array of bytes into a single variable is just as easy:
module example_1_2;
initial begin
static bit [7:0] array[4] = '{ 8'h8C, 8'h00, 8'hA4, 8'hFF };
static int value = {>>{array}};
$display("value = 0x%h", value);
end
endmodule
2. Reverse the elements of a byte array and pack them into an int
We can reverse the order of an array’s elements and then pack them into a single value in the following way:
module example_2;
initial begin
static bit [7:0] array[4] = '{ 8'h8C, 8'h00, 8'hA4, 8'hFF };
static int value = {<<8{array}};
$display("value = 0x%h", value);
end
endmodule
If the slice size would be 16 instead of 8, the output value would be 0xA4FF_8C00.
3. Reverse the bits in a byte
Below is an easy way to reverse the order of the bits within a byte. The slice size defaults to 1 if it is not specified. The slice size is a resolution/granularity like concept.
module example_3;
initial begin
static bit [7:0] value_a = 8'h8C;
static bit [7:0] value_b = {<<{value_a}};
$display("value_b = 0x%h", value_b);
end
endmodule
If the slice size would be 2 instead of 1, the bits would be reversed in groups of 2, leading to an output value of 8’b00_11_00_10.
If the slice size would be 3, groups of 3 bits would be created starting from the least significant 3 bits. Since we have an 8-bit input value, the last (leftmost) group will contain the remaining 2 bits. No padding or truncation is performed. The bit group reversing would then be performed, leading to the output value 8’b100_001_10.
4. Reverse the nibbles in a byte
A byte contains a low nibble and a high nibble. The order of the nibbles inside the byte can be reversed using the right-to-left streaming operator with a slice size of 4:
module example_4;
initial begin
static bit [7:0] value_a = 8'h8C;
static bit [7:0] value_b = {<<4{value_a}};
$display("value_b = 0x%h", value_b);
end
endmodule
5. Reverse the bits of an array and pack them into a shortint
Reversing the elements of an array and, at the same time, the bits of each element of the array is easily achievable using the right-to-left streaming operator:
module example_5;
initial begin
static bit [7:0] array[2] = '{ 8'h8C, 8'hA4 };
static shortint value = {<<{array}};
$display("value = 0x%h", value);
end
endmodule
6. Advanced packing
In this final example, we’ll take an array of 2-bit values and pack it into a structure. The ordering scheme used for the values in the array is little endian. We can achieve this by taking the result of a right-to-left streaming operator with a slice size of 2, and feeding it as input to another right-to-left streaming operator with a slice size of 4:
module example_6;
typedef struct {
bit [3:0] addr;
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 = {<<4{ {<<2{array}} }};
$display("packet addr = %b", packet.addr);
$display("packet data = %b", packet.data);
end
endmodule
If the ordering scheme is big endian, the packing can be performed with a single right-to-left streaming operator ( {>>{}} ), using the default slice size.
References
The above diagrams have been created using an open source design tool called Inkscape. Stay tuned for more packing/unpacking tutorials (>, <<)">Unpacking using streaming operators and UVM pack/unpack).
26 Responses
Very helpful post to get the basics cleared.
Thank you Gaurav, I’m glad that it was useful for you!
very well explained. Thank you so much
please keep adding new example
Thank you Mukund! Do you have any particular example in mind that you would like to view?
Would you guys be able to tell me what this piece of code is doing
Hi Varun!
It is not much to say: the code is self-explanatory and the are also some comments.
Also, note that the >> and << operators in your code are not streaming operators, but shift operators.
Really nice explanation .
Thanks and Best regards!
Hi,
In this line
static int value = {<<8{array}};
while reversing elements, instead of 8, can I use a variable? For ex:
int a = 4
static int value = {<<a{array}};
Would this be acceptable?
Hi, Anargha.
Unfortunately it is not possible to use the streaming operators with a variable or a const variable. You can use a parameter instead.
A parameterized version of example_2:
Another workaround to this problem would be utilizing MACROs.
Please note that example 2 shows different value than what is described.
I tried this as I got a doubt looking at the example.
This is the output: value = 0xffa4008c
What do you mean ? Example 2 shows the exact same output value: 0xffa4008c.
So let’s say that you are packing into a variable that is wider than the source data.
byte src_data[$] = {‘hAA, ‘hBB, ‘hCC, ‘hDD};
uvm_reg_data_t reg_data = {>>byte{pkt.data}};
This will pack the info from src_data into reg_data starting at the MSB, so it is left shifted:
reg_data = ‘hAABBCCDD00000000
That is not what I want. I want it right shifted:
reg_data = ‘hAABBCCDD
How to get this right?
Hi David,
There is no direct way that I know of to achieve what you need only using packing/unpacking operations.
I did however explore a couple of possibilities, which I will show below.
One option would be to shift the result by the number of bits needed to get rid of the padding.
The second option is to reverse the packet’s data bytes before packing it into the register, and then reversing the bytes in the register.
Example:
Option 2 is far less efficient because it involves more operations, but it doesn’t rely on the $bits system function, which does not work with dynamic arrays for all simulators.
So, in the end you will have to experiment to see what suits your needs.
If you find a better alternative, please share your solution.
Regards,
Dragos
Thank you Dragos. I didn’t think of the second solution, I like it. I suspect that we can skip src_data.reverse() by packing the reg_data using the right to left streaming operator and let it reverse the bytes for us (unconfirmed):
reg_data = {<<byte{src_data}}; // register now contains 'hDDCCBBAA_00000000
reg_data = {<<byte{reg_data}}; // register now contains 'h00000000_AABBCCDD
I'll experiment with it.
Thanks,
David
Hi David,
You are correct indeed. I just checked your version of the solution and it seems to work just fine.
Thanks for your input.
Best Regards,
Dragos
Thank you for post. I had some question : what program you used for pictures ?
I’m glad you like it. It is written in the last paragraph:
Greetings. Thanks for this great page, i keep coming back to it. Any thoughts on how streams can work with 2D dynamic arrays?
now i want to fill each element of the array with filler[width-1:0]. So for example if width is 16 and length is 5, the resulting table looks like
abcd
abcd
abcd
abcd
abcd
the ugly way:
this works too
is there anyway to assign a in a one-liner? I think the idea would be
but that does not compile because width is a variable.
Amazing Article.. bookmarking now.. Thanks alot!
Very good article!
Thanks
Greetings. This is a great article.
But I have a doubt in the last section: 6.) Advanced packing.
The last sentence says: “If the ordering scheme is big endian, the packing can be performed with a single right-to-left streaming operator ( {>>{}} ), using the default slice size.”
So, should I just use:
static packet_t packetBigEndian = {>>{array}};
for big-endian ordering scheme? How is it big-endian? Can someone please explain?
Thanks in advance…
Hi Sai,
The order is big-endian because it’s the way we chose to interpret the data and it refers to the data in the array, not the resulting packet. If the data is adjusted, using that right to left streaming operator, it should return the same result as the first example. In both cases, the packet’s arrays are little-endian.
If you run the code, you will see that both will return the same result. The first array contains the initial example, and the second contains the data changed to match the new big-endian order.
Thank you for your post!
I’m using stream operator to inverse bits of a signal. The result is correct but there are warnings when I do compile.
Do you know about the cause of the below warnings? I also tried adding “1” right before the operator but it has the same message.
Hello, LinhNV.
First of all you did not mention the simulator you are using.
I also suspect that the assign code is incomplete. You should also have a concatenation operator {,} after the equal sign of the assign.
I think the error code refers to the two RHS streaming operators.
They can’t be implicitly concatenated. You need to explicitly cast their result. Either use a cast operator, either use 2 other variables
Option1:
Option2
I generally use the bit slicing syntax to create a bus from an array of bytes from my C-DPI return value
bit[127:0] rx_det_out;
byte det_out[64];
for(int i=0; i<64; i++)
rx_det_out[((i*2)+1)-:2] =det_out[i];
Is it possible to use a streaming operator to do something equivalent? When I try I get LHS != RHS errors
rx_det_out = {<<2{{det_out}}};