2026. 4. 10. 20:02ㆍUVM
uvm_factory란?
UVM Factory는 컴포넌트나 오브젝트를 직접 new()로 생성하지 않고, 이름 기반으로 간접 생성하게 해주는 메커니즘입니다. 이를 통해 테스트별로 특정 클래스를 다른 클래스로 동적으로 교체(Override)할 수 있습니다. 실제 DUT에 손 대지 않고 테스트 레벨에서 동작을 바꾸는 핵심 기법입니다.
예를 들어, 일반 테스트에서는 my_driver를 쓰다가 에러 주입 테스트에서는 error_driver로 교체하고 싶을 때, 코드 수정 없이 Factory Override 한 줄로 해결할 수 있습니다.
type_id::create — Factory를 통한 생성
Factory를 활용하려면 반드시 new() 대신 type_id::create()로 인스턴스를 만들어야 합니다.
// 잘못된 방법 — Factory를 거치지 않음
my_driver drv = new("drv", this);
// 올바른 방법 — Factory를 통해 생성
my_driver drv = my_driver::type_id::create("drv", this);
type_id::create()는 내부적으로 Factory에 등록된 클래스를 찾아 인스턴스를 생성합니다. Override가 걸려 있으면 원래 클래스 대신 Override된 클래스가 생성됩니다.
uvm_component_utils / uvm_object_utils 매크로
Factory에 클래스를 등록하려면 반드시 아래 매크로를 클래스 안에 선언해야 합니다.
// uvm_component 계열 클래스
class my_driver extends uvm_driver #(my_item);
`uvm_component_utils(my_driver)
// ...
endclass
// uvm_object 계열 클래스
class my_item extends uvm_sequence_item;
`uvm_object_utils(my_item)
// ...
endclass
이 매크로들이 Factory에 클래스를 자동 등록합니다. 매크로를 빠뜨리면 Override가 동작하지 않습니다.
Override 종류
1. Type Override — 전체 교체
해당 타입의 모든 인스턴스를 교체합니다. Test의 build_phase에서 호출합니다.
class error_test extends uvm_test;
`uvm_component_utils(error_test)
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// my_driver 대신 error_driver를 전역으로 사용
my_driver::type_id::set_type_override(error_driver::get_type());
// 또는 동일한 의미:
factory.set_type_override_by_type(
my_driver::get_type(),
error_driver::get_type()
);
endfunction
endclass
2. Instance Override — 특정 경로만 교체
경로(inst_path)를 지정해서 특정 인스턴스만 교체합니다. 와일드카드 *를 사용할 수 있습니다.
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// env.agent0.drv 경로의 driver만 교체
my_driver::type_id::set_inst_override(
error_driver::get_type(),
"env.agent0.drv"
);
// 또는:
factory.set_inst_override_by_type(
my_driver::get_type(),
error_driver::get_type(),
{get_full_name(), ".env.agent0.drv"}
);
endfunction
Instance Override는 Type Override보다 우선합니다. 같은 경로에 두 가지가 모두 설정되어 있으면 Instance Override가 적용됩니다.
실전 예제 — 에러 주입 드라이버 교체
// 기본 드라이버
class my_driver extends uvm_driver #(my_item);
`uvm_component_utils(my_driver)
task run_phase(uvm_phase phase);
forever begin
my_item req;
seq_item_port.get_next_item(req);
// 정상 동작 구동
drive_normal(req);
seq_item_port.item_done();
end
endtask
endclass
// 에러 주입 드라이버 (my_driver 상속)
class error_driver extends my_driver;
`uvm_component_utils(error_driver)
task run_phase(uvm_phase phase);
forever begin
my_item req;
seq_item_port.get_next_item(req);
// 에러 조건 추가 후 구동
inject_error(req);
drive_normal(req);
seq_item_port.item_done();
end
endtask
task inject_error(my_item req);
if ($urandom_range(0,9) == 0)
req.data = ~req.data; // 10% 확률로 비트 반전
endtask
endclass
// 테스트에서 Override 적용
class error_injection_test extends uvm_test;
`uvm_component_utils(error_injection_test)
my_env env;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Override 먼저 설정 — create() 호출 전에 해야 함
my_driver::type_id::set_type_override(error_driver::get_type());
env = my_env::type_id::create("env", this);
endfunction
endclass
Factory Print — 등록 현황 확인
시뮬레이션에서 현재 Factory에 등록된 클래스와 Override 상태를 출력할 수 있습니다.
// 모든 등록된 컴포넌트 출력
factory.print();
// 특정 타입의 Override 결과 확인
factory.debug_create_by_type(my_driver::get_type(), "env.agent.drv");
커맨드라인에서 Override — +uvm_set_type_override
코드 수정 없이 시뮬레이터 실행 시 플러스아규먼트로도 Override를 지정할 수 있습니다.
// 실행 커맨드
// simv +UVM_TESTNAME=base_test +uvm_set_type_override=my_driver,error_driver
이 방법은 회귀 테스트 자동화에서 특히 유용합니다. CI 스크립트에서 테스트명과 Override를 동시에 제어할 수 있습니다.
핵심 정리
Factory Override를 올바르게 사용하기 위한 포인트입니다. 첫째, 반드시 type_id::create()를 써야 Override가 적용됩니다. new()로 생성하면 Override를 설정해도 무시됩니다. 둘째, Override는 create() 호출 이전에 설정해야 합니다. 셋째, Override 대상 클래스는 반드시 원본 클래스의 자식(또는 같은 계층)이어야 합니다. 넷째, `uvm_component_utils 또는 `uvm_object_utils 매크로를 빠뜨리면 Factory에 등록되지 않아 Override가 동작하지 않습니다.