How to Unpack Data Using the SystemVerilog Streaming Operators (>>, <<)

In this post I show how to use the streaming operators to unpack data into variables or data structures in SystemVerilog. This is the second part of a 3-post series on data packing/unpacking operations and the natural follow-up to the first part that focuses on packing data using streaming operators.

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

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 shortints

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


Comments

Leave a Comment:

Your comment will be visible after approval.

(will not be published)