지노랩 /JinoLab

[UVM] 5.5.2 레지스터 타입(Register Type) ― uvm_reg를 상속해 “하나의 레지스터 클래스”를 만드는 방법 본문

UVM(Universal Verification Methodology)/5. Register Layer Class 사용하기

[UVM] 5.5.2 레지스터 타입(Register Type) ― uvm_reg를 상속해 “하나의 레지스터 클래스”를 만드는 방법

지노랩/JinoLab 2025. 5. 7. 12:12

 

1 | 기본 골격 & 규칙

규칙 설명

클래스 1 개 = 레지스터 타입 1 개 같은 구조·필드 조합을 공유하는 레지스터는 하나의 타입으로 표현
uvm_reg 상속 class my_reg_type extends uvm_reg;
uvm_object_utils() 팩터리·print/randomize 지원
유일한 이름 생성기가 파일·패키지 범위에서 중복되지 않도록 관리
class MY_CFG_REG extends uvm_reg;
  `uvm_object_utils(MY_CFG_REG)
endclass

2 | 필드 프로퍼티 & 제약

class MY_CFG_REG extends uvm_reg;
  // 2.1 모든 필드를 rand 프로퍼티로 선언
  rand uvm_reg_field MODE;
  rand uvm_reg_field CH[2];

  // 2.2 (선택) 레지스터 레벨 제약
  constraint VALID_C { MODE.value < 4; }
endclass
  • 대문자 이름 권장 → uvm_reg 내부 심볼과 충돌 방지
  • 한 필드짜리 레지스터일 때 → value라는 rand 프로퍼티 + dummy field 패턴으로 자연스러운 randomize 사용(가이드 예시 참고)

3 | 생성자(new)

function new(string name="MY_CFG_REG");
  // super.new(name, 비트폭, 커버리지)
  super.new(.name(name), .n_bits(32), .has_coverage(UVM_NO_COVERAGE));
endfunction

4 | build() 메서드

virtual function void build();
  // 4.1 MODE 필드 인스턴스화
  MODE = uvm_reg_field::type_id::create("MODE", null, get_full_name());
  MODE.configure(this,   // 부모(레지스터)
                 2,      // 비트폭
                 0,      // LSB
                 "RW",   // access
                 0, 1,   // reset_val, has_reset
                 1, 1,   // is_rand, individually_accessible
                 0);     // volatile

  // 4.2 CH[0], CH[1] 배열 필드
  foreach (CH[i]) begin
    CH[i] = uvm_reg_field::type_id::create($sformatf("CH%0d",i),
                                           null, get_full_name());
    CH[i].configure(this, 1, 2+i, "RW", 0,0,1,1,0);
  end
endfunction
  • create() 의 parent 인자 null, context = get_full_name() → uvm_object 계층 규칙
  • configure() 인자 순서: parent, size, lsb, access, reset, has_reset, is_rand, indv_acc, volatile

5 | (선택) 추가 메서드 – RMW 예

task RMW(output uvm_status_e status,
         input  uvm_reg_data_t data,
         input  uvm_reg_data_t mask);
  uvm_reg_data_t tmp;
  read (status, tmp);
  tmp = (tmp & ~mask) | (data & mask);
  write(status, tmp);
endtask

주의: UVM 표준에는 없는 “확장 API” 이므로 대문자 / 회사 접두어로 이름 충돌 방지!


6 | 레지스터-단 커버리지 모델 (옵션)

class MY_CFG_REG extends uvm_reg;
  local covergroup cg_bits;
    coverpoint m_data[1:0];  // 사용자 정의
  endgroup

  function new(string name="MY_CFG_REG");
    super.new(name, 32,
      build_coverage(UVM_CVR_REG_BITS)); // 레지스터 비트 커버리지 선언

    if (has_coverage(UVM_CVR_REG_BITS))
      cg_bits = new();
  endfunction

  virtual function void sample(uvm_reg_data_t data,
                               uvm_reg_data_t be,
                               bit is_rd,
                               uvm_reg_map map);
    if (get_coverage(UVM_CVR_REG_BITS)) begin
      m_data = data;
      cg_bits.sample();
    end
  endfunction
endclass
  • build_coverage() 또는 add_coverage() 로 어떤 타입 ID를 지원할지 등록
  • 실제 샘플링은 get_coverage()로 ON/OFF 확인 후 수행

7 | 블록/레지스터 파일 build()에서 인스턴스화

class codec_blk extends uvm_reg_block;
  rand MY_CFG_REG   CONFIG;

  virtual function void build();
    default_map = create_map("sfr", 'h0, 4, UVM_LITTLE_ENDIAN);

    CONFIG = MY_CFG_REG::type_id::create("CONFIG");
    CONFIG.configure(this);   // parent 블록 설정
    CONFIG.build();

    default_map.add_reg(CONFIG, 'h00, "RW", 0);
  endfunction
endclass

 

핵심 체크리스트 (Generator 관점)

  1. uvm_reg 상속 + utils 매크로
  2. rand uvm_reg_field 프로퍼티 선언 & 대문자
  3. new() → super.new(name, width, coverage)
  4. build() → 필드 create + configure
  5. 필요 시 제약, 도우미 메서드, covergroup 추가
  6. 블록·파일 build()에서 타입 인스턴스화 + 맵 등록

이렇게 하면 스펙 변경 시 생성기 재실행만으로 새로운 레지스터 구조가 자동 반영됩니다.

 

 

 

본 내용은
accellera에서 공개한
Universal Verification Methodology
(UVM) 1.2 User's Guide
를 바탕으로 작성된 글입니다.