uvm_component는 검증 환경 내에서 driver, monitor, scoreboard와 같은 모든 UVM component의 기초 역할을 하는 기본적인 base class입니다. Verilog나 SystemVerilog에 익숙하지만 UVM은 처음 접하는 엔지니어에게 필수적인 개념입니다. uvm_component는 다음과 같은 주요 특징을 가지고 있습니다:
- Hierarchy (계층 구조): 각
component가 자식component를 가질 수 있는 계층적 구조를 지원하여 트리 형태의 구조를 형성합니다. 이 구조 내에서 검색하고 탐색하기 위한method들을 제공합니다. - Phasing (페이징):
component들은 시뮬레이션을 build, connect, run, cleanup 등과 같은 여러 단계(phase)로 구성하는 UVM phasing 메커니즘에 참여할 수 있습니다. 각 phase 동안component는 특정 작업을 수행할 수 있습니다. - Reporting (리포팅):
component는 UVM 메시징 인프라를 사용하여 시뮬레이션 중 이벤트, 경고, 오류 등을 보고할 수 있습니다. - Factory (팩토리):
component는 UVMfactory메커니즘에 등록될 수 있어, 동적object생성 및 조회를 가능하게 합니다.
Class Hierarchy (클래스 계층 구조)
driver, monitor, scoreboard와 같은 주요 검증 testbench component들은 모두 uvm_component class에서 파생됩니다. 따라서 이들은 UVM 프레임워크의 표준 phasing 및 reporting 메커니즘을 상속받습니다.
UVM component들은 phasing 메커니즘을 사용하여 특정 순서에 따라 초기화 및 구성 작업을 수행합니다. Phase를 통해 component들은 내부 계층 구조를 구축하고, 다른 component와 연결하며, 실제 테스트가 시작되기 전에 구성을 완료할 수 있습니다. 이는 잘 구조화된 설정 및 구성 프로세스를 보장합니다.
Class Declaration (클래스 선언)
virtual class uvm_component extends uvm_report_object;
uvm_component class 정의는 하위 component들을 찾고, 빌드하고, 연결하고, 구성하는 데 사용되는 많은 함수(function)와 태스크(task)를 제공합니다. 이들은 크게 component 계층 구조를 탐색하는 기능, testbench를 빌드하고 실행하는 방식을 제어하는 phase를 관리하는 기능, 그리고 factory를 사용하여 testbench 재사용성을 높이는 기능으로 분류됩니다.
Hierarchy Methods (계층 구조 관련 메서드)
uvm_component는 자신의 상위 또는 하위 component를 찾거나 계층 구조 정보를 얻기 위한 다양한 method를 제공합니다.
// 현재 component를 인스턴스화한 부모 uvm_component의 핸들을 얻습니다.
extern virtual function uvm_component get_parent ();
// 현재 component의 전체 계층적 이름을 얻습니다.
extern virtual function string get_full_name ();
// 현재 component 바로 아래에 인스턴스화된 모든 자식들의 큐(queue)를 얻습니다. (중첩되지 않음)
extern function void get_children(ref uvm_component children[$]);
// 주어진 이름으로 자식 component의 핸들을 얻습니다.
extern function uvm_component get_child (string name);
// 반복문에서 다음 자식 component 핸들을 얻기 위해 사용됩니다.
extern function int get_next_child (ref string name);
// 현재 component 아래의 첫 번째 자식 핸들을 얻습니다.
extern function int get_first_child (ref string name);
// 자식 component의 총 개수를 얻습니다. (중첩되지 않음)
extern function int get_num_children ();
// 지정된 이름의 component가 하나 이상 인스턴스화되었는지 확인합니다. (1이면 존재)
extern function int has_child (string name);
// 계층적 경로를 사용하여 중첩된 자식 component 핸들을 찾습니다.
extern function uvm_component lookup (string name);
// uvm_top=0, uvm_test=1 기준으로 현재 component의 깊이를 얻습니다.
extern function int unsigned get_depth();
다음은 component class의 계층적 인스턴스화 예제와 uvm_component의 표준 함수를 사용하여 자식들을 찾는 방법을 보여줍니다.
class apb_monitor extends uvm_component;
// ... monitor 내부 구현 ...
endclass
class apb_agent extends uvm_component;
`uvm_component_utils(apb_agent) // Factory 등록
apb_monitor m_apb_monitor;
function new(string name = "apb_agent", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Factory를 사용하여 monitor 생성
m_apb_monitor = apb_monitor::type_id::create("m_apb_monitor", this);
endfunction
endclass
class child_comp extends uvm_component;
`uvm_component_utils(child_comp) // Factory 등록
function new(string name = "child_comp", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass
class top_comp extends uvm_component;
`uvm_component_utils(top_comp) // Factory 등록
// 자식 component 인스턴스 선언
child_comp m_child_comp_sa [5];
apb_agent m_apb_agent;
function new(string name = "top_comp", uvm_component parent = null);
super.new(name, parent);
endfunction
// 자식 component 인스턴스 생성 (build_phase 에서)
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_apb_agent = apb_agent::type_id::create("m_apb_agent", this);
foreach (m_child_comp_sa[i]) begin
string inst_name = $sformatf("m_child_comp_%0d", i);
m_child_comp_sa[i] = child_comp::type_id::create(inst_name, this);
end
endfunction
// Hierarchy method 사용 예시 (end_of_elaboration_phase 에서)
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
// 임시 결과를 저장할 지역 변수
uvm_component l_comp_h;
uvm_component l_comp_q [$];
// 부모 가져와서 출력하기
l_comp_h = get_parent();
`uvm_info ("tag", $sformatf("get_parent=%s", l_comp_h.get_name()), UVM_LOW)
// 모든 자식 가져와서 출력하기
get_children(l_comp_q);
foreach (l_comp_q[i])
`uvm_info ("tag", $sformatf("child_%0d = %s", i, l_comp_q[i].get_name()), UVM_LOW)
// 특정 자식 component 가져와서 이름 출력하기
l_comp_h = get_child("m_child_comp_2");
if (l_comp_h) // Check if found
`uvm_info ("tag", $sformatf("Found %s", l_comp_h.get_name()), UVM_LOW)
else
`uvm_warning("tag", "Could not find child m_child_comp_2")
// 다른 함수 호출 예시
`uvm_info ("tag", $sformatf("number of children = %0d", get_num_children()), UVM_LOW)
`uvm_info ("tag", $sformatf("has_child('abc') = %0d", has_child("abc")), UVM_LOW)
`uvm_info ("tag", $sformatf("has_child('m_apb_agent') = %0d", has_child("m_apb_agent")), UVM_LOW)
`uvm_info ("tag", $sformatf("get_depth = %0d", get_depth()), UVM_LOW)
// Lookup 실패 시 경고 발생
l_comp_h = lookup("m_apb_monitor"); // 잘못된 경로 (top_comp 아래에 직접 없음)
if (l_comp_h)
`uvm_info ("tag", $sformatf("Found %s", l_comp_h.get_full_name()), UVM_LOW)
// Lookup은 component까지의 전체 계층 경로가 필요함
l_comp_h = lookup("m_apb_agent.m_apb_monitor"); // 올바른 경로
if (l_comp_h)
`uvm_info ("tag", $sformatf("Found %s", l_comp_h.get_full_name()), UVM_LOW)
endfunction
endclass
// --- 가정: Testbench Top ---
// module testbench;
// import uvm_pkg::*;
// `include "uvm_macros.svh"
//
// // ... 기타 필요한 코드 ...
//
// initial begin
// my_test test = new("uvm_test_top"); // my_test는 top_comp를 인스턴스화한다고 가정
// run_test("my_test");
// end
// endmodule
Phasing Methods (페이징 관련 메서드)
Component들은 자식 class에서 기능을 구현하기 위해 재정의(override)할 수 있는 각 UVM phase에 대한 가상 method를 제공합니다. 예를 들어, 모든 testbench component는 각자의 build_phase에서 인스턴스화되고, connect_phase에서 연결되며, run_phase에서 시뮬레이션됩니다. 기능을 구현하는 데 사용할 수 있는 사전 정의된 UVM phase는 훨씬 더 많습니다.
더 자세한 내용은 UVM Phases에 대해 더 알아보기! (추후 추가 예정)
// 이 자식 class는 uvm_component의 build_phase와 connect_phase 메서드만 구현합니다.
// 나머지 phase 메서드는 변경되지 않았으며 uvm_component 내부의 정의에 의존합니다.
class apb_agent extends uvm_component;
`uvm_component_utils(apb_agent) // Factory 등록 추가
// ... 기타 선언 ...
apb_monitor m_monitor;
apb_driver m_driver;
apb_sequencer m_seqr;
function new(string name = "apb_agent", uvm_component parent = null);
super.new(name, parent);
endfunction
// 이 component의 모든 자식들은 build_phase에서 인스턴스화됩니다.
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// obj_type::type_id::create는 factory로부터 "obj_type" 타입의
// object를 얻는 방법입니다. "m_monitor", "m_driver", "m_seqr"는 인스턴스 이름,
// 'this'는 부모 component(현재 apb_agent)를 지정합니다.
m_monitor = apb_monitor::type_id::create("m_monitor", this);
m_driver = apb_driver::type_id::create("m_driver", this);
m_seqr = apb_sequencer::type_id::create("m_seqr", this);
endfunction
// 자식 component들은 connect_phase에서 서로 연결될 수 있습니다.
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// sequencer와 driver 연결
// driver의 seq_item_port를 sequencer의 seq_item_export에 연결합니다.
m_driver.seq_item_port.connect(m_seqr.seq_item_export);
endfunction
endclass
Factory Methods (팩토리 관련 메서드)
Component들은 오직 component들만 사용하도록 의도된 특정 macro를 사용하여 factory에 등록됩니다. UVM object class 정의는 `uvm_object_utils macro를 사용한다는 점에 유의하세요.
더 자세한 내용은 UVM Factory에 대해 더 알아보기! (추후 추가 예정)
class my_comp extends uvm_component;
// Factory에 등록
`uvm_component_utils (my_comp)
// Constructor: 이름과 부모 component를 인자로 받습니다.
function new(string name = "my_comp", uvm_component parent = null);
super.new(name, parent); // 부모 class의 constructor 호출
endfunction
// ... 기타 component 로직 ...
endclass
Factory는 싱글톤(singleton) object이며 UVM 환경에는 단 하나의 factory 인스턴스만 존재합니다. uvm_component는 단순화된 인터페이스로 uvm_factory 멤버 함수를 호출하는 편리한 함수 세트를 제공합니다.
// 예를 들어, "set_type_override_by_type"은 실제로는 uvm_factory class에 정의된 함수입니다.
// 동일한 이름의 래퍼(wrapper) 함수가 uvm_component class에 정의되어 있으며,
// 인자들은 uvm_factory 함수 호출로 전달됩니다.
function void uvm_component::set_type_override_by_type (uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
bit replace=1);
// UVM 환경의 factory 인스턴스 핸들을 얻습니다.
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory = cs.get_factory();
// 동일한 인자들을 factory 함수 호출에 전달합니다.
factory.set_type_override_by_type(original_type, override_type, replace);
endfunction
// 사용 예시: "m_comp"는 uvm_component class 핸들입니다.
// apb_driver 타입을 apb_driver_2 타입으로 재정의(override)합니다.
m_comp.set_type_override_by_type(apb_driver::get_type(), apb_driver_2::get_type());
이 글이 UVM uvm_component를 이해하는 데 도움이 되었기를 바랍니다.
더 많은 UVM 및 SystemVerilog 튜토리얼을 보려면 블로그를 구독해주세요.


