[UVM] UVM Reporting 완전 정리 | `uvm_info, `uvm_error, `uvm_fatal, verbosity, report_server

2026. 4. 7. 00:45UVM

반응형

UVM 리포팅 시스템이란?

UVM은 `uvm_info, `uvm_warning, `uvm_error, `uvm_fatal 네 가지 매크로로 메시지를 출력하는 통합 리포팅 시스템을 제공합니다. 단순 $display와 달리 심각도(severity), 식별자(ID), 상세 수준(verbosity)을 함께 관리할 수 있어 대규모 테스트벤치에서 필수입니다.


4가지 심각도 매크로

// 형식: `uvm_("", "", )

`uvm_info("DRV",  "트랜잭션 전송 시작",     UVM_LOW)
`uvm_info("DRV",  "addr = 0x100",           UVM_MEDIUM)
`uvm_info("DRV",  "내부 상태 덤프",          UVM_HIGH)

`uvm_warning("SCB", "예상과 다른 데이터 수신")      // 계속 진행
`uvm_error("SCB",   "scoreboard 미스매치 발생")     // 에러 카운트 +1
`uvm_fatal("CFG",   "필수 인터페이스 연결 안됨")    // 즉시 시뮬 중단

Verbosity 레벨

Verbosity는 메시지의 상세 수준입니다. 런타임에 설정된 레벨보다 높은 verbosity의 메시지는 출력되지 않습니다. 기본값은 UVM_MEDIUM입니다.

// 낮을수록 항상 출력, 높을수록 필터링됨
UVM_NONE   = 0    // 항상 출력
UVM_LOW    = 100  // 중요 정보
UVM_MEDIUM = 200  // 기본값 (default 출력 임계치)
UVM_HIGH   = 300  // 디버그용 상세 정보
UVM_FULL   = 400  // 매우 상세 (개발 중에만 사용)
UVM_DEBUG  = 500  // 최대 상세

Verbosity 실시간 변경

// 커맨드라인으로 변경
// +UVM_VERBOSITY=UVM_HIGH

// 코드에서 변경 (특정 컴포넌트에만 적용)
env.agent.driver.set_report_verbosity_level(UVM_HIGH);

// 전체 레벨 변경
uvm_top.set_report_verbosity_level_hier(UVM_HIGH);

$sformatf로 동적 메시지 출력

task run_phase(uvm_phase phase);
  my_item tr;
  forever begin
    seq_item_port.get_next_item(tr);
    `uvm_info("DRV",
      $sformatf("Driving: addr=0x%0h data=0x%0h wr=%0b",
                tr.addr, tr.data, tr.write),
      UVM_LOW)
    // ... 드라이빙
    seq_item_port.item_done();
  end
endtask

에러/경고 제어 — set_report_*

특정 ID의 메시지 행동을 변경하거나 억제할 수 있습니다.

// 특정 ID의 UVM_ERROR를 UVM_WARNING으로 격하
set_report_severity_id_override(UVM_ERROR, "SCB", UVM_WARNING);

// 특정 ID 메시지 완전히 억제
set_report_id_action(UVM_INFO, "NOISY_ID", UVM_NO_ACTION);

// 특정 ID 로그 파일로 출력
set_report_id_file("DRV", log_file_handle);
set_report_id_action("DRV", UVM_LOG | UVM_DISPLAY);

uvm_report_server — 전역 통계 관리

uvm_report_server는 시뮬레이션 전체의 에러/경고 카운트를 관리합니다. 시뮬레이션 종료 시 자동으로 요약 리포트를 출력합니다.

// 에러 카운트 직접 확인
function void check_phase(uvm_phase phase);
  uvm_report_server rs = uvm_report_server::get_server();
  int err_cnt = rs.get_severity_count(UVM_ERROR);
  if (err_cnt > 0)
    `uvm_fatal("TEST", $sformatf("Test FAILED: %0d errors", err_cnt))
  else
    `uvm_info("TEST", "Test PASSED", UVM_NONE)
endfunction

// 시뮬레이션 끝 자동 요약 예시:
// --- UVM Report Summary ---
// UVM_INFO   : 1523
// UVM_WARNING:    2
// UVM_ERROR  :    0
// UVM_FATAL  :    0

 

uvm_do 매크로 시리즈 — create + randomize + start 자동화

시퀀스 body() 안에서 아이템을 직접 create → randomize → start_item → finish_item 하는 대신, UVM이 제공하는 매크로로 이 과정을 한 줄에 처리할 수 있습니다. 복잡성이 낮은 시퀀스에서 코드를 간결하게 만들 때 유용합니다.

매크로 설명 내부 동작
`uvm_do(item) 아이템 생성 + 랜덤화 + 전송 create → randomize → start_item → finish_item
`uvm_do_with(item, {constraint}) constraint 지정 랜덤화 후 전송 위와 동일, randomize with {} 사용
`uvm_do_on(item, sqr) 특정 시퀀서 위에서 실행 해당 sqr에 아이템 전송
`uvm_do_on_with(item, sqr, {c}) 시퀀서 + constraint 동시 지정 Virtual Sequence에서 주로 사용
class fast_seq extends uvm_sequence #(my_item);
  `uvm_object_utils(fast_seq)
  task body();
    my_item req;
    // 매크로 사용 — 한 줄로 처리
    `uvm_do(req)
    // constraint 지정
    `uvm_do_with(req, { addr inside {[0:255]}; wr_en == 1; })
    // 특정 시퀀서에 전송 (Virtual Sequence에서)
    `uvm_do_on_with(req, p_sequencer.axi_sqr, { addr == 32'h1000; })
  endtask
endclass

📌 주의사항: uvm_do 매크로는 편리하지만 randomize 실패 시 에러 처리가 없습니다. 실무에서는 start_item/finish_item을 직접 쓰고 if (!req.randomize()) `uvm_fatal(...)로 명시적으로 처리하는 방식이 더 안전합니다.

아이템 전송 헬퍼 매크로

매크로 설명
`uvm_create(item) 아이템 생성만 (randomize, start 안 함)
`uvm_rand_send(item) randomize + start_item + finish_item
`uvm_send(item) start_item + finish_item (randomize 없이)
// uvm_create로 생성 → 수동 제어 → uvm_send로 전송
`uvm_create(req)
req.addr = 32'hDEAD;   // 직접 값 설정
req.data = 32'hBEEF;
`uvm_send(req)          // randomize 없이 그대로 전송

랜덤 변수 처리 심화 — randomize() with {} 와 변수 공유

시퀀스 안에서 rand 변수를 다룰 때 알아두면 실수를 줄일 수 있는 패턴을 정리합니다.

randomize() with {} — 선택적 랜덤화

randomize()는 클래스 내 모든 rand 변수를 한꺼번에 랜덤화합니다. 내부 constraint가 복잡하면 충돌이 생길 수 있습니다. randomize() with {}를 쓰면 해당 조건만 추가 constraint로 적용됩니다.

class my_seq extends uvm_sequence #(my_item);
  task body();
    my_item req;
    req = my_item::type_id::create("req");
    start_item(req);
    // mode만 1로 고정, 나머지(addr, data 등)는 기존 constraint로 랜덤화
    if (!req.randomize() with { mode == 1; })
      `uvm_fatal("RAND", "randomize failed")
    finish_item(req);
  endtask
endclass

상위 시퀀스 → 하위 시퀀스 랜덤 변수 공유

상위 시퀀스에서 랜덤화한 값을 하위 시퀀스들이 공유해야 할 때, config_db를 통해 전달하는 패턴이 깔끔합니다.

// 상위 시퀀스 — 랜덤화 후 config_db에 등록
class top_seq extends uvm_sequence;
  rand bit shared_mode;
  task body();
    void'(this.randomize());
    uvm_config_db#(bit)::set(null, "*", "shared_mode", shared_mode);
    // 하위 시퀀스 실행
    sub_seq_a sa = sub_seq_a::type_id::create("sa");
    sa.start(m_sequencer, this, -1, 0);
  endtask
endclass

// 하위 시퀀스 — config_db에서 공유 값 수신
class sub_seq_a extends uvm_sequence;
  bit shared_mode;
  task body();
    if (!uvm_config_db#(bit)::get(null, "*", "shared_mode", shared_mode))
      `uvm_fatal("CFG", "shared_mode not found")
    // shared_mode 사용
  endtask
endclass

핵심: set()은 값이 확정된 후(body() 안)에서, get()은 실제 사용 직전에 호출해야 합니다. set()보다 get()이 먼저 실행되면 get() 실패로 uvm_fatal이 발생합니다.