2026. 4. 10. 20:02ㆍUVM
uvm_config_db란?
UVM 테스트벤치에서 컴포넌트 계층 구조는 깊고 복잡하게 구성됩니다. 이때 상위 컴포넌트에서 하위 컴포넌트로 인터페이스(virtual interface)나 설정 값을 전달해야 하는 경우가 매우 자주 발생합니다. uvm_config_db는 바로 이 목적으로 사용되는 UVM의 전역 설정 데이터베이스입니다.
간단히 말하면, 전역 key-value 저장소처럼 동작합니다. set()으로 값을 등록하고, get()으로 꺼내 씁니다. 가장 흔한 사용 사례는 두 가지입니다.
- TB top에서 DUT의 virtual interface를 env/agent로 내려보내기
- test에서 각 컴포넌트의 동작 파라미터(timeout, count, enable 등)를 설정하기
기본 API
uvm_config_db는 템플릿 클래스이며 기본 사용법은 다음과 같습니다.
// 값 등록 (set)
uvm_config_db #(TYPE)::set(
uvm_component cntxt, // 기준 컨텍스트 (null이면 uvm_root)
string inst_name, // 대상 컴포넌트 경로 (와일드카드 가능)
string field_name, // 필드 이름 (key)
TYPE value // 저장할 값
);
// 값 조회 (get)
uvm_config_db #(TYPE)::get(
uvm_component cntxt, // 기준 컨텍스트
string inst_name, // 컴포넌트 경로
string field_name, // 필드 이름 (key)
ref TYPE value // 결과를 받을 변수
);
get()은 성공 시 1, 실패 시 0을 반환하므로, 반드시 반환값을 확인하는 습관을 들여야 합니다.
Virtual Interface 전달
가장 빈번한 사용 패턴입니다. DUT와 연결된 인터페이스를 SystemVerilog의 interface로 선언한 뒤, TB top에서 set()하고 driver/monitor에서 get()하는 구조입니다.
TB Top (모듈 레벨)
module tb_top;
import uvm_pkg::*;
`include "uvm_macros.svh"
import my_pkg::*;
// 인터페이스 인스턴스화
my_if dut_if(.clk(clk), .rst_n(rst_n));
// DUT 연결
my_dut u_dut(
.clk (clk),
.rst_n (rst_n),
.data_i (dut_if.data_i),
.data_o (dut_if.data_o)
);
initial begin
// virtual interface를 config_db에 등록
uvm_config_db #(virtual my_if)::set(
null, // 컨텍스트: uvm_root 기준
"uvm_test_top.env.agent", // 대상 경로
"vif", // key 이름
dut_if // 실제 인터페이스 핸들
);
run_test();
end
endmodule
Driver (컴포넌트 레벨)
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver)
virtual my_if vif; // virtual interface 선언
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// config_db에서 virtual interface 꺼내기
if (!uvm_config_db #(virtual my_if)::get(
this, // 현재 컴포넌트 기준
"", // 자기 자신의 경로
"vif", // key
vif // 결과 저장
)) begin
`uvm_fatal("NO_VIF",
"virtual interface not found in config_db")
end
endfunction
task run_phase(uvm_phase phase);
// vif를 통해 DUT 구동
forever begin
my_seq_item req;
seq_item_port.get_next_item(req);
vif.data_i <= req.data;
@(posedge vif.clk);
seq_item_port.item_done();
end
endtask
endclass
설정 값 전달
인터페이스 외에도 일반 변수 (int, string, 사용자 정의 객체 등)를 전달할 수 있습니다. Test에서 컴포넌트의 동작을 제어할 때 자주 쓰입니다.
class my_test extends uvm_test;
`uvm_component_utils(my_test)
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// int 값 전달
uvm_config_db #(int)::set(
this,
"env.agent.driver",
"num_transactions",
50
);
// string 값 전달
uvm_config_db #(string)::set(
this,
"env.scoreboard",
"mode",
"check_only"
);
endfunction
endclass
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver)
int num_transactions = 10; // 기본값
function void build_phase(uvm_phase phase);
super.build_phase(phase);
void'(uvm_config_db #(int)::get(
this, "", "num_transactions", num_transactions));
endfunction
endclass
경로(inst_name)와 와일드카드
inst_name은 cntxt를 기준으로 하는 상대 경로입니다. cntxt가 null이면 uvm_root가 기준이 됩니다. 와일드카드 *를 지원하여 여러 컴포넌트에 동시에 설정을 적용할 수 있습니다.
// 특정 경로에만 적용
uvm_config_db #(int)::set(null,
"uvm_test_top.env.agent.driver",
"timeout", 100);
// env 하위의 모든 driver에 적용 (와일드카드)
uvm_config_db #(int)::set(null,
"uvm_test_top.env.*.driver",
"timeout", 100);
// 모든 컴포넌트에 적용
uvm_config_db #(int)::set(null,
"*",
"timeout", 100);
경로 매칭은 가장 구체적인 경로가 우선합니다. 즉 와일드카드 설정과 명시적 경로 설정이 충돌하면 명시적 경로가 이깁니다.
uvm_config_db vs set_config_*
UVM 1.1 이전에는 set_config_int(), set_config_object() 같은 API가 있었습니다. 현재는 모두 uvm_config_db로 대체되었고 deprecated입니다. 새 코드에서는 반드시 uvm_config_db를 사용해야 합니다.
| 구분 | 구형 API | uvm_config_db |
|---|---|---|
| 정수 | set_config_int() | uvm_config_db #(int)::set() |
| 문자열 | set_config_string() | uvm_config_db #(string)::set() |
| 객체 | set_config_object() | uvm_config_db #(T)::set() |
| 타입 | 고정 | 템플릿으로 임의 타입 지원 |
디버깅 팁
get()이 실패해서 uvm_fatal이 터지는 경우, 원인을 찾기 어려울 때가 많습니다. 아래 방법들이 도움됩니다.
UVM_CONFIG_DB_TRACE
시뮬레이터 실행 시 +UVM_CONFIG_DB_TRACE 플러스아규먼트를 추가하면 모든 set/get 호출 내역이 출력됩니다. 경로 오타를 잡는 데 매우 효과적입니다.
// 시뮬레이터 실행 시
// simv +UVM_TESTNAME=my_test +UVM_CONFIG_DB_TRACE
get() 반환값 확인
void'(...)로 반환값을 버리지 말고 항상 체크하는 습관을 들이세요.
// 나쁜 습관
void'(uvm_config_db #(int)::get(this, "", "timeout", timeout));
// 좋은 습관
if (!uvm_config_db #(int)::get(this, "", "timeout", timeout)) begin
`uvm_warning("CFG", "timeout not set, using default")
end
경로 확인
get_full_name()으로 현재 컴포넌트의 전체 경로를 확인할 수 있습니다. set()의 경로와 정확히 매칭되는지 비교해보세요.
function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("PATH", $sformatf("My path: %s", get_full_name()), UVM_LOW)
endfunction
전체 예제 구조
아래는 virtual interface 전달부터 driver 사용까지의 전체 흐름을 보여주는 미니멀 예제입니다.
// --- interface ---
interface my_if(input logic clk);
logic [7:0] data_i;
logic [7:0] data_o;
endinterface
// --- seq_item ---
class my_item extends uvm_sequence_item;
`uvm_object_utils(my_item)
rand logic [7:0] data;
endclass
// --- driver ---
class my_driver extends uvm_driver #(my_item);
`uvm_component_utils(my_driver)
virtual my_if vif;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db #(virtual my_if)::get(
this, "", "vif", vif))
`uvm_fatal("NO_VIF", "vif not found")
endfunction
task run_phase(uvm_phase phase);
forever begin
my_item req;
seq_item_port.get_next_item(req);
@(posedge vif.clk);
vif.data_i <= req.data;
seq_item_port.item_done();
end
endtask
endclass
// --- agent ---
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
my_driver drv;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = my_driver::type_id::create("drv", this);
endfunction
endclass
// --- env ---
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent agent;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = my_agent::type_id::create("agent", this);
endfunction
endclass
// --- test ---
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_env env;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
endfunction
endclass
// --- tb_top ---
module tb_top;
import uvm_pkg::*;
`include "uvm_macros.svh"
logic clk;
always #5 clk = ~clk;
my_if dut_if(.clk(clk));
initial begin
uvm_config_db #(virtual my_if)::set(
null, "uvm_test_top.env.agent.drv", "vif", dut_if);
run_test("my_test");
end
endmodule