[UVM] UVM RAL 완전 정리 | uvm_reg, uvm_reg_block, Adapter, Frontdoor/Backdoor

2026. 4. 10. 20:02UVM

반응형

UVM RAL이란?

RAL(Register Abstraction Layer)은 DUT 내부의 레지스터와 메모리를 객체로 모델링하여, 테스트벤치에서 일관된 방식으로 읽고 쓸 수 있게 해주는 UVM 표준 계층입니다. 직접 트랜잭션을 만들지 않아도 reg.write(), reg.read() 한 줄로 레지스터 접근이 가능합니다.


핵심 클래스 구조

RAL 모델은 세 가지 핵심 클래스로 구성됩니다.

uvm_reg_block          // 레지스터 블록 (최상위 컨테이너)
  uvm_reg              // 개별 레지스터 (예: CTRL_REG, STATUS_REG)
    uvm_reg_field      // 레지스터 내 비트 필드 (예: [7:4] MODE, [0] EN)
  uvm_mem              // 메모리 모델
  uvm_reg_map          // 주소 맵 (베이스 주소 + 오프셋 매핑)

uvm_reg_block 작성 예시

class my_reg_block extends uvm_reg_block;
  `uvm_object_utils(my_reg_block)

  rand ctrl_reg  CTRL;    // 컨트롤 레지스터
  rand status_reg STATUS; // 상태 레지스터
  uvm_reg_map default_map;

  function void build();
    // 레지스터 생성 및 구성
    CTRL = ctrl_reg::type_id::create("CTRL");
    CTRL.configure(this, null, "");
    CTRL.build();

    STATUS = status_reg::type_id::create("STATUS");
    STATUS.configure(this, null, "");
    STATUS.build();

    // 주소 맵 생성: 베이스 주소 0x0, 버스 폭 32bit
    default_map = create_map("default_map", 0, 4, UVM_LITTLE_ENDIAN);
    default_map.add_reg(CTRL,   32'h00, "RW");
    default_map.add_reg(STATUS, 32'h04, "RO");
  endfunction
endclass

uvm_reg + uvm_reg_field 작성 예시

class ctrl_reg extends uvm_reg;
  `uvm_object_utils(ctrl_reg)

  rand uvm_reg_field MODE;  // [3:2] 동작 모드
  rand uvm_reg_field EN;    // [0]   활성화

  function void build();
    MODE = uvm_reg_field::type_id::create("MODE");
    EN   = uvm_reg_field::type_id::create("EN");
    // configure(parent, size, lsb_pos, access, volatile, reset, has_reset, is_rand, individually_accessible)
    MODE.configure(this, 2, 2, "RW", 0, 2'b00, 1, 1, 0);
    EN.configure(this,   1, 0, "RW", 0, 1'b0,  1, 1, 0);
  endfunction

  function new(string name = "ctrl_reg");
    super.new(name, 32, UVM_NO_COVERAGE);
  endfunction
endclass

Adapter — RAL과 버스 프로토콜 연결

RAL의 write()/read()는 내부적으로 uvm_reg_bus_op를 만들고, Adapter가 이를 실제 버스 트랜잭션(APB, AXI 등)으로 변환합니다.

class my_reg_adapter extends uvm_reg_adapter;
  `uvm_object_utils(my_reg_adapter)

  // RAL op → 버스 트랜잭션 변환
  function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    my_bus_seq_item tr = my_bus_seq_item::type_id::create("tr");
    tr.addr  = rw.addr;
    tr.data  = rw.data;
    tr.write = (rw.kind == UVM_WRITE);
    return tr;
  endfunction

  // 버스 트랜잭션 → RAL op 변환
  function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    my_bus_seq_item tr;
    $cast(tr, bus_item);
    rw.kind = tr.write ? UVM_WRITE : UVM_READ;
    rw.addr = tr.addr;
    rw.data = tr.data;
    rw.status = UVM_IS_OK;
  endfunction
endclass

Frontdoor vs Backdoor 접근

Frontdoor는 실제 버스 트랜잭션을 통해 레지스터에 접근합니다 — 버스 프로토콜 검증에 사용합니다. Backdoor는 시뮬레이터 계층에서 직접 DUT 내부 신호를 읽고 씁니다 — 속도가 매우 빠르고 시간이 소요되지 않습니다.

task run_phase(uvm_phase phase);
  uvm_status_e status;
  uvm_reg_data_t val;

  // Frontdoor write: 버스를 통해 CTRL 레지스터에 0x3 기록
  regmodel.CTRL.write(status, 32'h3, UVM_FRONTDOOR);

  // Backdoor read: 시뮬레이터 직접 접근 (시간 소요 없음)
  regmodel.STATUS.read(status, val, UVM_BACKDOOR);
  `uvm_info("RAL", $sformatf("STATUS = 0x%0h", val), UVM_LOW)

  // 필드 단위 접근
  regmodel.CTRL.EN.set(1'b1);
  regmodel.CTRL.update(status); // set 값을 DUT에 반영
endtask

Predictor — 미러 값 자동 갱신

uvm_reg_predictor는 버스 모니터에서 트랜잭션을 받아 RAL 미러(mirror) 값을 자동으로 업데이트합니다. Scoreboard에서 reg.get_mirrored_value()로 예상값을 가져올 수 있습니다.

class my_env extends uvm_env;
  my_agent                        agent;
  my_reg_block                    regmodel;
  uvm_reg_predictor #(my_bus_seq_item) predictor;

  function void build_phase(uvm_phase phase);
    agent     = my_agent::type_id::create("agent", this);
    predictor = uvm_reg_predictor#(my_bus_seq_item)::type_id::create("predictor", this);
    regmodel  = my_reg_block::type_id::create("regmodel");
    regmodel.build(); regmodel.lock_model();
  endfunction

  function void connect_phase(uvm_phase phase);
    // adapter 연결
    my_reg_adapter adapter = my_reg_adapter::type_id::create("adapter");
    regmodel.default_map.set_sequencer(agent.sequencer, adapter);
    // predictor 연결
    predictor.map     = regmodel.default_map;
    predictor.adapter = adapter;
    agent.monitor.ap.connect(predictor.bus_in);
  endfunction
endclass