UVM 자동화 매크로를 활용한 데이터 패킹 및 언패킹
UVM (Universal Verification Methodology)을 처음 접하는 엔지니어 여러분들을 환영합니다! Verilog 또는 SystemVerilog에 대한 기본적인 이해를 갖추셨다면, UVM의 강력한 기능 중 하나인 자동화 매크로를 활용하여 효율적인 검증 환경을 구축할 수 있습니다. 특히, 시리얼 통신 (SPI, I2C, RS-232 등)과 같이 데이터를 직렬 형태로 다루어야 하는 상황에서 pack 및 unpack 기능은 매우 유용합니다.
이번 포스팅에서는 UVM 자동화 매크로를 사용하여 class 변수들을 bit 또는 byte 스트림으로 패킹하고, 반대로 bit 스트림을 언패킹하여 class 내용을 복원하는 방법에 대해 자세히 알아보겠습니다. UVM에서 제공하는 세 가지 주요 패킹/언패킹 함수와 사용자 정의 후킹 함수인 do_pack 및 do_unpack의 활용 예시를 통해 여러분의 이해를 돕고자 합니다.
주요 pack 및 unpack 함수
UVM은 데이터를 효율적으로 처리하기 위해 다음과 같은 세 가지 주요 함수를 제공합니다.
pack: class의 내용을 bit 단위로 연결하여bit타입의 배열로 반환합니다.pack_bytes: class의 내용을 bit 단위로 연결한 후 byte 단위로 묶어byte타입의 배열로 반환합니다.pack_ints: class의 내용을 bit 단위로 연결한 후 integer 단위로 묶어int타입의 배열로 반환합니다.unpack:bit타입의 배열로부터 속성 값을 추출하여 해당하는 class 변수에 저장합니다.unpack_bytes:byte타입의 배열로부터 속성 값을 추출하여 해당하는 class 변수에 저장합니다.unpack_ints:int타입의 배열로부터 속성 값을 추출하여 해당하는 class 변수에 저장합니다.
자동화 매크로 사용 예시: Pack
다음 예제에서는 주소와 데이터를 저장하는 Packet 이라는 class를 정의하고, `uvm_field_int 매크로를 사용하여 자동화를 활성화합니다. UVM_DEFAULT는 해당 변수에 모든 기본 자동화 메서드를 적용하도록 지정합니다.
주의할 점은 `uvm_object_utils_* 내에 나열된 변수의 순서가 데이터를 패킹하고 언패킹하는 데 사용된다는 것입니다.
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
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
bit m_bits[];
byte unsigned m_bytes[];
int unsigned m_ints[];
virtual function void build_phase(uvm_phase phase);
Packet m_pkt = Packet::type_id::create("Packet");
m_pkt.randomize();
m_pkt.print();
m_pkt.pack(m_bits);
m_pkt.pack_bytes(m_bytes);
m_pkt.pack_ints(m_ints);
`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
자동화 매크로 사용 예시: Unpack
이번에는 unpack_test 라는 새로운 test class를 만들어 두 개의 Packet 객체를 생성합니다. 하나만 랜덤화하고 다른 하나는 비워둔 상태로 둡니다. 그런 다음 첫 번째 객체의 내용을 배열로 패킹하고, 이 배열을 두 번째 객체로 언패킹하여 내용을 표시합니다. 다른 타입의 pack 함수에 대해서도 동일한 과정을 반복합니다.
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
bit m_bits[];
byte unsigned m_bytes[];
int unsigned m_ints[];
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)
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)
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)
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)
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();
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();
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
do_pack/do_unpack 사용
print, copy, compare와 마찬가지로 pack 및 unpack 또한 사용자 정의 가능한 후킹 함수인 do_pack 및 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)
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
class hook_test extends uvm_test;
`uvm_component_utils(hook_test)
function new(string name = "hook_test", uvm_component parent=null);
super.new(name, parent);
endfunction
bit m_bits[];
Packet m_pkt;
Packet m_pkt2;
virtual function void build_phase(uvm_phase phase);
m_pkt = Packet::type_id::create("m_pkt");
m_pkt2 = Packet::type_id::create("m_pkt2");
m_pkt.randomize();
m_pkt.print();
m_pkt.pack(m_bits);
m_pkt2.unpack(m_bits);
`uvm_info(get_type_name(), "--- After Unpack with Hook ---", UVM_LOW)
m_pkt2.print();
endfunction
endclass
module tb;
initial begin
run_test("hook_test");
end
endmodule
