[UVM] uvm_config_db 완전 정리 | 인터페이스 설정 전달, set/get, 경로 와일드카드

2026. 4. 10. 20:02UVM

반응형

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_namecntxt를 기준으로 하는 상대 경로입니다. cntxtnull이면 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