APB Reset Handling

Seungyun Lee·2026년 3월 17일

UVM

목록 보기
14/14

1. 문제 상황: 리셋이 터지면 무슨 일이 생길까?

  • Driver (일꾼): 리셋이 눌려도 눈치채지 못하고, 쥐고 있던 핀(Pin)에 계속 쓰레기 값을 밀어 넣습니다.

  • Sequencer (조감독): 일꾼이 "택배 배달 완료(item_done)" 보고를 해야 다음 상자를 주는데, 일꾼이 리셋 때문에 멈춰버리면 조감독은 영원히 다음 상자를 주지 못하고 교착 상태(Deadlock/Stuck)에 빠집니다.

  • Monitor & Coverage: 리셋으로 버스가 초기화되었는데도, 계속 이상한 값을 훔쳐보며 엉뚱한 채점표를 작성합니다.

  • 해결 목표: 리셋이 감지되는 즉시 모든 컴포넌트가 하던 일을 즉각 멈추고(Stop), 핀을 기본값으로 되돌린 후, 처음부터 다시 시작(Restart)해야 합니다.

2. 강사의 3단계 아키텍처 빌드업

강사는 이 문제를 해결하기 위해 코드를 어떻게 짤 것인가에 대해 3가지 방법을 비교합니다.

하수 (Naive Approach): "모두 각자도생해라!"

Driver, Sequencer, Monitor, Coverage 4개 파일의 run_phase 안에 각자 알아서 리셋을 기다리는 무한 루프(forever)를 복붙해서 넣습니다.

단점: 코드가 4배로 중복되고 엄청나게 지저분해집니다.

중수 (Centralized Approach): "에이전트가 중앙 통제한다!"

개별 직원들은 무한 루프를 지우고 순수하게 handle_reset()이라는 초기화 함수만 가집니다.

대신 관리자인 에이전트(Agent) 혼자 무한 루프를 돌면서 리셋을 감시하다가, 리셋이 터지면 driver.handle_reset(), monitor.handle_reset() 처럼 직원들의 이름을 일일이 호명하며 초기화시킵니다.

단점: 나중에 직원이 추가되거나 빠지면 에이전트 코드를 매번 수정(하드코딩)해야 합니다.

고수 (Interface Class & OOP): "자격증 있는 사람만 모여라!" (최종 채택)

SystemVerilog의 고급 문법인 인터페이스 클래스(Interface Class)를 활용합니다. reset_handler라는 일종의 '리셋 자격증'을 하나 만듭니다.

리셋이 필요한 4명의 직원(Driver, Seq, Mon, Cov)에게 이 자격증(Interface)을 따게(Implement) 만듭니다.

에이전트는 직원들의 이름을 일일이 부를 필요 없이, "내 밑에 있는 직원들(get_children) 중에 reset_handler 자격증 있는 사람 전부 다 handle_reset() 실행해!" 라고 우아하게 일괄 처리($cast 캐스팅)합니다.

실무 엔지니어의 시각

강사가 마지막에 선택한 3번째 방법이 바로 현업 UVM 프레임워크에서 가장 사랑받는 다형성(Polymorphism)의 극치입니다!
이렇게 짜두면 나중에 새로운 컴포넌트 100개를 추가하더라도, 에이전트 코드는 단 한 줄도 수정할 필요가 없습니다. 그냥 새 컴포넌트에 자격증(implements reset_handler)만 달아주면 에이전트가 알아서 리셋을 챙겨주게 됩니다.

cfs_apb_agent.sv / reset 부분


`ifndef CFS_APB_AGENT_SV
  `define CFS_APB_AGENT_SV

  class cfs_apb_agent extends uvm_agent implements cfs_apb_reset_handler;
    
    // handler를 만들기 위해 아래 다섯개 파일을 가지고 온다
    //Agent configuration handler
    cfs_apb_agent_config agent_config;
    
    //Driver handler
    cfs_apb_driver driver;
    
    //Sequencer handler
    cfs_apb_sequencer sequencer;
    
    //Monitor handler
    cfs_apb_monitor monitor;
    
    //Coverage handler
    cfs_apb_coverage coverage;

    `uvm_component_utils(cfs_apb_agent) // cfs_apb_agent를 uvm에 등록
    
    function new(string name = "", uvm_component parent);
      super.new(name, parent);
    endfunction
    
    //------------------------------------------------------------------------------
    //                                 만드는 단계
    //------------------------------------------------------------------------------
    virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      
      // agent_config 만들기 
      agent_config = cfs_apb_agent_config::type_id::create("agent_config", this);
      
      // monitor 만들기
      monitor = cfs_apb_monitor::type_id::create("monitor", this);
      
      // 코드 해석: "리모컨 설정에서 커버리지 수집 기능이 켜져 있다면(get_has_coverage() == 1) 
      // ➔ 통계 장부를 작성할 서기(cfs_apb_coverage) 객체를 메모리에 생성(create)해서 에이전트 부서에 배치해라!"
      if(agent_config.get_has_coverage()) begin
        coverage = cfs_apb_coverage::type_id::create("coverage", this);
      end
      
      // 에이전트(cfs_apb_agent)는 상황에 따라 **'직접 경기에 뛰는 선수(Active)'**가 될 수도 있고,
      // **'관중석에서 지켜보기만 하는 심판(Passive)'**이 될 수도 있습니다.
      // 이것을 결정하는 리모컨 버튼이 바로 get_active_passive()입니다.
      if(agent_config.get_active_passive() == UVM_ACTIVE) begin
        driver    = cfs_apb_driver::type_id::create("driver", this);
        sequencer = cfs_apb_sequencer::type_id::create("sequencer", this);
        //코드 해석: "리모컨 설정이 ACTIVE라면 ➔ 드라이버와 시퀀서 객체를 메모리에 생성(create)해서 채용해라!"
      end
      // UVM_PASSIVE 일때는 Monitor 와 Coverage만 남겨둔다. 
      // 코드 해석: if 문 조건이 거짓이 되므로, driver와 sequencer는 생성되지 않고 비어있는 상태(null)로 남게 됩니다.
      // 에이전트는 조용히 데이터만 수집하는 스파이가 됩니다.
    endfunction
    
    //-------------------------------------------------------------------------------
    //                                 연결하는 단계
    //-------------------------------------------------------------------------------
    virtual function void connect_phase(uvm_phase phase);
      cfs_apb_vif vif; // vif를 담을 빈 바구니(포인터)만들기 
      string      vif_name = "vif"; // testbench에서 vif를 config_db에 넣어둘때 vif라고 이름 붙였음,
      // 그거 꺼내오는거임
      
      super.connect_phase(phase);
      
      // uvm_config_db안에 virtual cfs_apb_if을 찾아라 그리고 내 위치 agent(this)에서 vif_name을 찾고 vif에 넣어라
      if(!uvm_config_db#(virtual cfs_apb_if)::get(this, "", vif_name, vif)) begin
        // vif를 못찾으면 오류를 내고 시뮬레이션을 중단해라
        `uvm_fatal("APB_NO_VIF", $sformatf("Could not get from the database the APB virtual interface using name \"%0s\"", vif_name))
      end
      else begin
        // vif를 찾았다면 vif를 smart remote control(agent_config)에 넣어라
        agent_config.set_vif(vif);
      end
      
      // monitor 안의 config에 agent_config값을 넣자
      monitor.agent_config = agent_config;
      
      // coverage 검사하면 coverage config값에 agent_config 값 넣어주자
      if(agent_config.get_has_coverage()) begin
        coverage.agent_config = agent_config;
        
        // 모니터 결과 포트랑 coverage port랑 연결
        monitor.output_port.connect(coverage.port_item);
      end
      
      // active 이면 driver 포트를 sequencer 이랑 연결하자 
      if(agent_config.get_active_passive() == UVM_ACTIVE) begin
        driver.seq_item_port.connect(sequencer.seq_item_export);
        
        // driver config에도 agent_config 값을 넣자
        driver.agent_config = agent_config;
      end
    endfunction
         

   //-------------------------------------------------------------------------------
   //                        reset
   //-------------------------------------------------------------------------------

    //Task for waiting the reset to start
    protected virtual task wait_reset_start();
      agent_config.wait_reset_start();
    endtask
         
    //Task for waiting the reset to be finished
    protected virtual task wait_reset_end();
      agent_config.wait_reset_end();
    endtask
    


    //칩에 리셋이 떨어졌을 때, 소대장(Agent)이 대원들(Driver, Sequencer, Monitor, Coverage)에게 일일이 달려가서 "너 하던 일 멈춰!"라고 지시하면
    // 코드가 굉장히 지저분해집니다. 대신, 전파 방송을 통해 훈련된 인원들이 각자 알아서 대응하게 만드는 방식을 사용합니다.
    //Function to handle the reset
    virtual function void handle_reset(uvm_phase phase);
      uvm_component children[$]; // Queue to hold all child components, $=가변 Queue
      
      get_children(children); //1. Gather all personnel in the tent (Driver, Monitor, etc.)
      
      // 2. Iterate through each person
      foreach(children[idx]) begin // foreach= 배열에 들어있는 데이터 개수만큼 알아서 처음부터 끝까지 돌아라
        cfs_apb_reset_handler reset_handler; // Pointer for the "Reset License"
        
        // 3. Dynamic Casting: "Do you have the Reset Handler license?"
        if($cast(reset_handler, children[idx])) begin // $cast(A,B) = B가 A 타입으로 취급해도 안전하나? 
          reset_handler.handle_reset(phase);           // = B가 자격증 A를 가지고 있나?
        end
      end
    endfunction
    

    //에이전트(cfs_apb_agent)는 평소에는 직원들(Driver, Monitor 등)을 연결해 주고 뒤로 빠져있지만,
    // 칩에 **리셋(Reset)**이 떨어지는 순간 전체를 통제하는 지휘관으로 돌변합니다.
    virtual task run_phase(uvm_phase phase);
      forever begin
        wait_reset_start();  // Wait until the physical reset pin drops to 0
        handle_reset(phase); // Execute emergency protocol!
        wait_reset_end();  //// Wait until the physical reset pin rises back to 1
      end
    endtask
    //에이전트의 run_phase는 시뮬레이션이 끝날 때까지 영원히(forever) 도는 감시 레이더입니다.
    //비상벨이 울리면(wait_reset_start), 즉시 아래에 있는 handle_reset() 지휘를 내리고, 상황이 종료될 때까지 대기(wait_reset_end)합니다.
    
  endclass

`endif

// uvm_config_db의 정체: "UVM 전용 마법의 우체통"
profile
RTL, FPGA Engineer

0개의 댓글