UVM automation macros also include mechanisms to pack class variables into a bit or byte stream, and unpack a bit stream and populate the class contents. This is particularly useful when dealing with serial forms of communication like SPI, I2C and RS-232.

There are three main functions available to pack and unpack.

MethodDescription
packPerforms a bit-wise concatenation of the class contents into an array of type bit
pack_bytesPerforms a bit-wise concatenation of the class contents into an array of type byte
pack_intsPerforms a bit-wise concatenation of the class contents into an array of type int
unpackExtracts property values from an array of type bit and stores into correct class variable
unpack_bytesExtracts property values from an array of type byte and stores into correct class variable
unpack_intsExtracts property values from an array of type int and stores into correct class variable

In the following examples, usage of all the three types of functions will be explored.

Using automation macros

Pack

A class called Packet is defined with some variables to store address and data, and is registered with `uvm_field_int macros to enable automation. UVM_DEFAULT specifies that all the default automation methods should be applied to the given variable.

Note that the order of variables as listed within `uvm_object_utils_* will be used for both packing and unpacking data.


class Packet extends uvm_object;
  rand bit [3:0] m_addr;
  rand bit [3:0] m_wdata;
  rand bit [3:0] m_rdata;
  rand bit 		 m_wr;
  
  `uvm_object_utils_begin(Packet)
  	`uvm_field_int(m_addr, 	UVM_DEFAULT)
  	`uvm_field_int(m_wdata, UVM_DEFAULT)
  	`uvm_field_int(m_rdata, UVM_DEFAULT)
  	`uvm_field_int(m_wr,		UVM_DEFAULT)
  `uvm_object_utils_end
  
  function new(string name = "Packet");
    super.new(name);
  endfunction
endclass

Let us create a test class called pack_test where an object of Packet is created, randomized and packed.


class pack_test extends uvm_test;
  `uvm_component_utils(pack_test)
  function new(string name = "pack_test", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  // Declare some arrays to store packed data output from different pack functions
  bit 						m_bits[];
  byte unsigned		m_bytes[];
  int  unsigned		m_ints[];
  
  virtual function void build_phase(uvm_phase phase);
  
  	// First create an object of class "Packet", randomize and print its contents
    Packet m_pkt = Packet::type_id::create("Packet");
    m_pkt.randomize();
    m_pkt.print();
    
    // Now, call "pack", "pack_bytes", and "pack_ints" and pass appropriate array type
    m_pkt.pack(m_bits);
    m_pkt.pack_bytes(m_bytes);
    m_pkt.pack_ints(m_ints); 
    
    // Print the array contents
    `uvm_info(get_type_name(), $sformatf("m_bits=%p", m_bits), UVM_LOW)
    `uvm_info(get_type_name(), $sformatf("m_bytes=%p", m_bytes), UVM_LOW)
    `uvm_info(get_type_name(), $sformatf("m_ints=%p", m_ints), UVM_LOW)
  endfunction
endclass

module tb;
	initial begin
		run_test("pack_test");
	end
endmodule

See that the function pack returned an array of bits, pack_bytes returned an array of bytes and pack_ints returned an array of ints. Note that pack_bytes padded 0 to the content from m_wr to turn it from 1 to 8.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test pack_test...
--------------------------------
Name       Type      Size  Value
--------------------------------
Packet     Packet    -     @1907
  m_addr   integral  4     'hd  
  m_wdata  integral  4     'h9  
  m_rdata  integral  4     'h6  
  m_wr     integral  1     'h1  
--------------------------------
UVM_INFO testbench.sv(74) @ 0: uvm_test_top [pack_test] m_bits='{'h1, 'h1, 'h0, 'h1, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h1, 'h0, 'h1}
UVM_INFO testbench.sv(75) @ 0: uvm_test_top [pack_test] m_bytes='{'hd9, 'h68}
UVM_INFO testbench.sv(76) @ 0: uvm_test_top [pack_test] m_ints='{3647471616}
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 

Unpack

Let us create a different test class called unpack_test which will create two Packet objects, but randomize only one of them leaving the other empty. Then pack contents of the first object into an array, and then unpack it into the second object and display its contents. Repeat the same thing with other types of pack functions.


class unpack_test extends uvm_test;
  `uvm_component_utils(unpack_test)
  function new(string name = "unpack_test", uvm_component parent=null);
    super.new(name, parent);
  endfunction
  
  // Declare a few arrays to hold packed data for each type of "pack" function
  bit 					m_bits[];
  byte unsigned	m_bytes[];
  int  unsigned	m_ints[];
  
  // Variables to hold return value from pack function
  int m_val1, m_val2, m_val3;
  
  virtual function void build_phase(uvm_phase phase);
    Packet m_pkt = Packet::type_id::create("Packet");
    Packet m_pkt2 = Packet::type_id::create("Packet");
    
    `uvm_info(get_type_name(), $sformatf("Start pack"), UVM_LOW)
    // Randomize the first object, print and pack into bit array, then display
    m_pkt.randomize();
    m_pkt.print();
    m_pkt.pack(m_bits);
    `uvm_info(get_type_name(), $sformatf("packed m_bits=%p", m_bits), UVM_LOW)
    
    // Randomize the first object, print and pack into byte array, then display
    m_pkt.randomize();
    m_pkt.print();
    m_pkt.pack_bytes(m_bytes);
    `uvm_info(get_type_name(), $sformatf("packed m_bytes=%p", m_bytes), UVM_LOW)
    
    // Randomize the first object, print and pack into int array, then display
    m_pkt.randomize();
    m_pkt.print();
    m_pkt.pack_ints(m_ints); 
    `uvm_info(get_type_name(), $sformatf("packed m_ints=%p", m_ints), UVM_LOW)
    
    `uvm_info(get_type_name(), $sformatf("Start unpack"), UVM_LOW)
    // Now unpack the packed bit array into the second object, and display
    m_val1 = m_pkt2.unpack(m_bits);
    `uvm_info(get_type_name(), $sformatf("unpacked m_val1=0x%0h", m_val1), UVM_LOW)
    m_pkt2.print();
    
    // Now unpack the packed byte array into the second object, and display
    m_val2 = m_pkt2.unpack_bytes(m_bytes);
    `uvm_info(get_type_name(), $sformatf("unpacked m_val2=0x%0h", m_val2), UVM_LOW)
    m_pkt2.print();
    
    // Now unpack the packed int array into the second object, and display
    m_val3 = m_pkt2.unpack_ints(m_ints);
    `uvm_info(get_type_name(), $sformatf("unpacked m_val3=0x%0h", m_val3), UVM_LOW)
    m_pkt2.print();
  endfunction
endclass

module tb;
	initial begin
		run_test("unpack_test");
	end
endmodule

Remember that the order in which it is packed should be same as the order in which it is unpacked. Any change in packer policy regarding order may cause the results to be different.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test unpack_test...
UVM_INFO testbench.sv(93) @ 0: uvm_test_top [unpack_test] Start pack
--------------------------------
Name       Type      Size  Value
--------------------------------
Packet     Packet    -     @1907
  m_addr   integral  4     'hd  
  m_wdata  integral  4     'h9  
  m_rdata  integral  4     'h6  
  m_wr     integral  1     'h1  
--------------------------------
UVM_INFO testbench.sv(95) @ 0: uvm_test_top [unpack_test] packed m_bits='{'h1, 'h1, 'h0, 'h1, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h1, 'h0, 'h1}
--------------------------------
Name       Type      Size  Value
--------------------------------
Packet     Packet    -     @1907
  m_addr   integral  4     'h8  
  m_wdata  integral  4     'he  
  m_rdata  integral  4     'h4  
  m_wr     integral  1     'h0  
--------------------------------
UVM_INFO testbench.sv(100) @ 0: uvm_test_top [unpack_test] packed m_bytes='{'h8e, 'h40}
--------------------------------
Name       Type      Size  Value
--------------------------------
Packet     Packet    -     @1907
  m_addr   integral  4     'h9  
  m_wdata  integral  4     'he  
  m_rdata  integral  4     'he  
  m_wr     integral  1     'h1  
--------------------------------
UVM_INFO testbench.sv(105) @ 0: uvm_test_top [unpack_test] packed m_ints='{2666004480}
UVM_INFO testbench.sv(109) @ 0: uvm_test_top [unpack_test] Start unpack
UVM_INFO testbench.sv(109) @ 0: uvm_test_top [unpack_test] unpacked m_val1=0xd
--------------------------------
Name       Type      Size  Value
--------------------------------
Packet     Packet    -     @1909
  m_addr   integral  4     'hd  
  m_wdata  integral  4     'h9  
  m_rdata  integral  4     'h6  
  m_wr     integral  1     'h1  
--------------------------------
UVM_INFO testbench.sv(113) @ 0: uvm_test_top [unpack_test] unpacked m_val2=0xd
--------------------------------
Name       Type      Size  Value
--------------------------------
Packet     Packet    -     @1909
  m_addr   integral  4     'h8  
  m_wdata  integral  4     'he  
  m_rdata  integral  4     'h4  
  m_wr     integral  1     'h0  
--------------------------------
UVM_INFO testbench.sv(117) @ 0: uvm_test_top [unpack_test] unpacked m_val3=0xd
--------------------------------
Name       Type      Size  Value
--------------------------------
Packet     Packet    -     @1909
  m_addr   integral  4     'h9  
  m_wdata  integral  4     'he  
  m_rdata  integral  4     'he  
  m_wr     integral  1     'h1  
--------------------------------
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]

Automation macros introduce a lot of new code and is not generally recommemded to be used

Using do_pack/do_unpack

Just like print, copy and compare, pack and unpack also has user definable hooks called do_pack and do_unpack.


class Packet extends uvm_object;
  rand bit [3:0] m_addr;
  rand bit [3:0] m_wdata;
  rand bit [3:0] m_rdata;
  rand bit 		 m_wr;
  
  `uvm_object_utils(Packet)
  
  // Define do_print, so that print method displays contents of this class
  virtual function void do_print(uvm_printer printer);
    super.do_print(printer);
    printer.print_field_int("m_addr", m_addr, $bits(m_addr), UVM_HEX);
    printer.print_field_int("m_wdata", m_wdata, $bits(m_wdata), UVM_HEX);
    printer.print_field_int("m_rdata", m_rdata, $bits(m_rdata), UVM_HEX);
    printer.print_field_int("m_wr", m_wr, $bits(m_wr), UVM_HEX);
  endfunction
  
  virtual function void do_pack(uvm_packer packer);
    super.do_pack(packer);
    packer.pack_field_int(m_addr, $bits(m_addr));
    packer.pack_field_int(m_wdata, $bits(m_wdata));
    packer.pack_field_int(m_rdata, $bits(m_rdata));
                          packer.pack_field_int(m_wr, $bits(m_wr));                                            
  endfunction
                          
  virtual function void do_unpack(uvm_packer packer);
    super.do_pack(packer);
    m_addr = packer.unpack_field_int($bits(m_addr));
    m_wdata = packer.unpack_field_int($bits(m_wdata));
    m_rdata = packer.unpack_field_int($bits(m_rdata));
                                      m_wr = packer.unpack_field_int($bits(m_wr));                                            
  endfunction
  
  function new(string name = "Packet");
    super.new(name);
  endfunction
endclass

The same set of tests shown above can be run with this class and will yield the same results.