[UVM] Phase 완전 정리 | build_phase 실행 순서, 컴포넌트 간 동작 흐름, run_phase objection

2026. 4. 10. 20:02UVM

반응형

UVM을 처음 쓸 때 가장 헷갈리는 것 중 하나가 Phase다. build_phase에서 왜 하위 컴포넌트가 안 만들어졌다고 에러가 나는지, connect_phase와 run_phase는 어떤 순서로 실행되는지, objection은 왜 써야 하는지 — 이 글에서는 UVM Phase의 종류와 실행 순서를 컴포넌트 계층 구조와 함께 완벽하게 정리한다.


1. UVM Phase 전체 목록

UVM Phase는 크게 Build-Time Phase Run-Time Phase로 나뉜다.

1-1. Build-Time Phase (함수형, 순차 실행)

Phase역할실행 방향

build_phase 컴포넌트 생성 (new), uvm_config_db get Top-Down (상위 → 하위)
connect_phase TLM 포트 연결, 핸들 연결 Bottom-Up (하위 → 상위)
end_of_elaboration_phase 토폴로지 확정 후 설정 조정, 계층 출력 Bottom-Up
start_of_simulation_phase 시뮬레이션 시작 직전 초기화, 배너 출력 Bottom-Up

1-2. Run-Time Phase (태스크형, 병렬 실행)

Phase역할특징

run_phase 실제 시뮬레이션 실행 (시퀀스 실행, 모니터링) 모든 컴포넌트 병렬 실행
extract_phase 결과 데이터 수집, 통계 계산 Bottom-Up
check_phase 오류 체크, 예상값 비교 Bottom-Up
report_phase 결과 보고서 출력 Bottom-Up
final_phase 종료 처리, 파일 닫기 Top-Down

1-3. Run-Time Sub-Phase (12개 세부 단계)

run_phase와 병렬로 실행되는 세부 phase들이다. 복잡한 테스트 시나리오에서 단계별 제어가 필요할 때 사용한다.

Sub-Phase 그룹포함 Phase

Reset pre_reset_phase → reset_phase → post_reset_phase
Configure pre_configure_phase → configure_phase → post_configure_phase
Main pre_main_phase → main_phase → post_main_phase
Shutdown pre_shutdown_phase → shutdown_phase → post_shutdown_phase

일반적인 블록 레벨 검증에서는 sub-phase를 잘 쓰지 않고 run_phase 하나로 모든 시나리오를 처리하는 경우가 많다. 복잡한 SoC 레벨에서 reset 시퀀스와 메인 트래픽을 분리해야 할 때 유용하다.

2. 핵심 — 컴포넌트 간 Phase 실행 순서

이것이 가장 중요한 부분이다. 같은 Phase라도 컴포넌트 종류에 따라 실행 순서가 다르다.

2-1. build_phase: Top-Down (상위 → 하위)

build_phase는 UVM에서 유일하게 Top-Down으로 실행되는 phase다. 상위 컴포넌트(test)가 먼저 build_phase를 실행하고, 그 안에서 하위 컴포넌트(env)를 create한 후, env의 build_phase가 실행되는 방식이다.

실행 순서 (Top-Down):
1. my_test::build_phase()      ← 최상위부터
2.   my_env::build_phase()
3.     my_axi_agent::build_phase()
4.       my_axi_driver::build_phase()
5.       my_axi_monitor::build_phase()
6.       my_axi_sequencer::build_phase()
7.     my_apb_agent::build_phase()
8.       my_apb_driver::build_phase()
9.       my_apb_monitor::build_phase()
10.      my_apb_sequencer::build_phase()

왜 Top-Down인가? 하위 컴포넌트를 create하는 것은 상위 컴포넌트의 build_phase 책임이기 때문이다. 상위가 먼저 실행되어야 하위 컴포넌트가 존재할 수 있다. 만약 Bottom-Up이었다면 아직 생성되지 않은 컴포넌트의 build_phase를 호출하는 모순이 생긴다.

2-2. connect_phase 이후: Bottom-Up (하위 → 상위)

build_phase가 끝나면 이후 모든 phase는 Bottom-Up으로 실행된다. 가장 아래 leaf 컴포넌트(driver, monitor)부터 실행하고, 위로 올라오는 방식이다.

connect_phase 실행 순서 (Bottom-Up):
1. my_axi_driver::connect_phase()
2. my_axi_monitor::connect_phase()
3. my_axi_sequencer::connect_phase()
4. my_axi_agent::connect_phase()    ← 여기서 포트 연결
5. my_apb_driver::connect_phase()
6. my_apb_monitor::connect_phase()
7. my_apb_sequencer::connect_phase()
8. my_apb_agent::connect_phase()
9. my_env::connect_phase()          ← 여기서 scoreboard 연결
10. my_test::connect_phase()

왜 Bottom-Up인가? connect_phase에서는 하위 컴포넌트의 포트(port)를 상위 컴포넌트가 연결한다. 하위의 포트가 먼저 준비되어 있어야 상위에서 연결할 수 있기 때문이다. TLM 포트 연결, virtual sequencer 핸들 연결이 대표적이다.

2-3. run_phase: 모든 컴포넌트가 동시에 병렬 실행

run_phase는 Build-Time Phase와 완전히 다르다. Top-Down도 Bottom-Up도 아니라 모든 컴포넌트의 run_phase가 동시에(fork-join_none) 시작된다.

run_phase — 모든 컴포넌트 동시 시작:
fork
  my_test::run_phase()          ← 시퀀스 실행
  my_axi_driver::run_phase()    ← 항상 대기하며 item 처리
  my_axi_monitor::run_phase()   ← 항상 대기하며 트랜잭션 캡처
  my_apb_driver::run_phase()
  my_apb_monitor::run_phase()
  my_scoreboard::run_phase()
join  ← 모두 끝날 때까지 대기 (objection이 0이 되면 종료)

Driver와 Monitor는 보통 forever 루프로 동작하며, 시뮬레이션이 끝날 때까지 계속 실행된다. run_phase의 종료는 objection 메커니즘으로 제어한다.

3. Objection 메커니즘 — run_phase를 끝내는 방법

run_phase는 영원히 실행되지 않는다. Objection 카운터가 0이 되는 순간 run_phase가 종료되고, 이후 extract → check → report → final phase로 넘어간다.

task run_phase(uvm_phase phase);
  phase.raise_objection(this);  // 카운터 +1 → run_phase 유지

  // ... 시퀀스 실행, 검증 작업 ...
  my_seq.start(env.v_sqr);

  phase.drop_objection(this);   // 카운터 -1 → 0이 되면 종료
endtask

Objection은 보통 Test 클래스 하나에서만 raise/drop한다. 여러 컴포넌트에서 raise하면 모든 objection이 drop되어야 종료되므로 관리가 복잡해진다.

상황Objection 카운터run_phase 상태

run_phase 시작 0 즉시 종료될 수 있음 (주의!)
raise_objection 호출 +1 (이상) 유지됨
drop_objection 호출 후 0 0 종료 → extract_phase로

⚠️ 주의: raise_objection 없이 run_phase를 시작하면 시뮬레이션이 즉시 종료될 수 있다. Driver와 Monitor는 보통 objection을 raise하지 않는다 — forever 루프로 돌기 때문에 자연스럽게 Test의 drop을 기다린다.

4. 전체 실행 흐름 — 실전 예제로 트레이스하기

test → env → agent(axi, apb) → driver/monitor/sequencer 계층 구조에서 전체 phase 실행 순서를 처음부터 끝까지 트레이스해보자.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1] build_phase (Top-Down)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  my_test::build_phase
    → env 생성
  my_env::build_phase
    → axi_agent, apb_agent, v_sqr 생성
  my_axi_agent::build_phase
    → axi_driver, axi_monitor, axi_sequencer 생성
  my_apb_agent::build_phase
    → apb_driver, apb_monitor, apb_sequencer 생성

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[2] connect_phase (Bottom-Up)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  axi_driver, axi_monitor, axi_sequencer::connect_phase
  my_axi_agent::connect_phase
    → driver.seq_item_port.connect(sequencer.seq_item_export)
  apb_driver, apb_monitor, apb_sequencer::connect_phase
  my_apb_agent::connect_phase
  my_env::connect_phase
    → monitor.ap.connect(scoreboard.imp)
    → v_sqr.axi_sqr = axi_agent.sequencer
  my_test::connect_phase

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[3] end_of_elaboration / start_of_simulation (Bottom-Up)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  계층 확인, 초기화 메시지 출력

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[4] run_phase (모든 컴포넌트 동시 시작)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ┌─ test::run_phase      → raise_objection → 시퀀스 실행 → drop_objection
  ├─ axi_driver::run_phase    (forever: seq_item 받아 DUT 구동)
  ├─ axi_monitor::run_phase   (forever: 인터페이스 샘플링)
  ├─ apb_driver::run_phase    (forever)
  └─ apb_monitor::run_phase   (forever)
  
  → drop_objection() 후 카운터 0 → run_phase 전체 종료

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[5] extract → check → report → final (Bottom-Up)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  scoreboard::check_phase → 결과 비교
  scoreboard::report_phase → Pass/Fail 출력

5. Phase 핵심 정리

Phase타입실행 방향핵심 역할

build_phase 함수 Top-Down ⬇ 컴포넌트 생성, config_db get
connect_phase 함수 Bottom-Up ⬆ TLM 포트 연결, 핸들 연결
end_of_elaboration 함수 Bottom-Up ⬆ 토폴로지 확정 후 보정
start_of_simulation 함수 Bottom-Up ⬆ 시뮬레이션 직전 초기화
run_phase 태스크 전체 병렬 ↔ 실제 트래픽 실행 (objection으로 종료 제어)
extract_phase 함수 Bottom-Up ⬆ 결과 수집
check_phase 함수 Bottom-Up ⬆ Pass/Fail 판정
report_phase 함수 Bottom-Up ⬆ 결과 출력
final_phase 함수 Top-Down ⬇ 종료 처리

한 줄 요약: build는 위에서 아래로 컴포넌트를 만들고, connect부터는 아래에서 위로 연결하며, run_phase에서 모두 동시에 실행된다. 시뮬레이션 종료는 objection 카운터가 0이 될 때다.