UVM result

Seungyun Lee·2026년 4월 13일

AXI4-Lite Verification

목록 보기
2/3

Week 4 RAL

RAL backdoor

Week 5 Virtual Sequencer

Read-Modify-Write Sequence

"I utilized the Virtual Sequencer to drive a Read-Modify-Write (RMW) scenario to stress-test the DUT." (DUT에 스트레스 테스트를 수행하기 위해 가상 시퀀서를 활용하여 Read-Modify-Write(RMW) 시나리오를 구동했습니다.)

"By checking the log timestamps, I verified that the sequence successfully generated back-to-back transactions with zero idle time." (로그 타임스탬프를 확인하여 시퀀스가 유휴 시간 없이 back-to-back 트랜잭션을 성공적으로 생성했음을 확인했습니다.)

"This effectively tests the hardware's turnaround time between read and write phases and ensures there are no pipeline hazards in the AXI slave state machine." (이는 읽기 및 쓰기 단계 간의 하드웨어 턴어라운드 시간을 효과적으로 테스트하고 AXI 슬레이브 상태 머신에 파이프라인 해저드가 없는지 확인합니다.)


의도적으로 에러(Error Injection)를 주입했을 때 DUT가 정확히 bresp/rresp 에러 코드를 반환하는지 테스트.

DV 엔지니어의 눈으로 이 로그를 보면, "하드웨어에 치명적인 결함(Bug)이 있다"는 것을 완벽하게 증명하는 증거 자료가 됩니다. 왜 그런지 현실적인 이유를 짚어드리겠습니다.

[MON] Captured WRITE: Addr=ffff0000, Data=deaddead

[DRV] Finished AXI Write Transaction

이 두 줄을 보십시오. 우리가 허가되지 않은 쓰레기 주소인 0xFFFF0000을 찔렀는데, 하드웨어가 아무런 불만 없이 통신을 끝내버렸습니다.

정상적인 상용 AXI Slave 칩이라면, 자신의 메모리 맵(0x00 ~ 0x0C)을 벗어난 주소가 들어오는 순간 SLVERR (Slave Error: 2'b10) 또는 DECERR (Decode Error: 2'b11) 응답을 뱉어서 시스템에 경고를 보내야 합니다.

하지만 승윤님이 설계하신 axi4_lite_slave.sv 코드를 보면, 응답 신호가 무조건 bresp <= 2'b00; (OKAY)로 하드코딩(Hardcoded) 되어 있습니다. 즉, 이 칩은 외부에서 엉뚱한 메모리 영역을 공격해도 바보처럼 "네, 정상적으로 처리했습니다"라고 응답하는 상태인 것입니다.

이 결함을 찾아내기 위해 Virtual Sequencer로 RAL을 우회하여 직접 핀을 찌르는 수고를 한 것입니다. 검증 엔지니어로서 버그를 찾아낸 짜릿한 순간입니다.

아래와 같이 RTL 코드 수정
설계자(RTL Designer)의 마인드로 전환하여 하드웨어의 방어막을 직접 구축해 보겠습니다.

현재 칩은 4개의 레지스터(0x00, 0x04, 0x08, 0x0C)만 가지고 있으므로, 주소가 0x10 이상으로 들어오면 모조리 에러 처리(Slave Error: 2'b10)를 해야 합니다.

 // ---------------------------------------------------------
  // 2. Write Operation & Write Response (B)
  // ---------------------------------------------------------
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      bvalid <= 1'b0;
      bresp  <= 2'b00; // OKAY response
      for (int i = 0; i < 4; i++)
       slv_reg[i] <= 32'h0;
    end else begin
      if (awready && awvalid && wready && wvalid) begin
        int reg_idx;
        reg_idx = awaddr[3:2]; 
        
        //Address Bound Check)
        if (awaddr < 32'h10) begin 
          if (wstrb[0]) slv_reg[reg_idx][7:0]   <= wdata[7:0];
          if (wstrb[1]) slv_reg[reg_idx][15:8]  <= wdata[15:8];
          if (wstrb[2]) slv_reg[reg_idx][23:16] <= wdata[23:16];
          if (wstrb[3]) slv_reg[reg_idx][31:24] <= wdata[31:24];
          bresp <= 2'b00; // OKAY
        end else begin
          // Invalid Address
          bresp <= 2'b10; // SLVERR
        end
       
        bvalid <= 1'b1; 
      end else if (bvalid && bready) begin
        bvalid <= 1'b0; 
      end
    end
  end
  
  // ---------------------------------------------------------
  // 4. Read Data (R) Operation
  // ---------------------------------------------------------
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      rvalid <= 1'b0;
      rresp  <= 2'b00;
      rdata  <= 32'h0;
    end else begin
      if (arready && arvalid && !rvalid) begin
        int reg_idx;
        reg_idx = araddr[3:2];
        
        //Address Bound Check
        if (araddr < 32'h10) begin
          // Valid Address
          rdata <= slv_reg[reg_idx]; 
          rresp <= 2'b00; // OKAY 
        end else begin
          // Invalid Address
          rdata <= 32'h0;
          rresp <= 2'b10; // SLVERR
        end
     
        rvalid  <= 1'b1; 
      end else if (rvalid && rready) begin
        rvalid <= 1'b0; 
      end
    end
  end

"I implemented an address boundary check in the RTL to prevent writes to unmapped memory locations." (매핑되지 않은 메모리 위치에 쓰기를 방지하기 위해 RTL에 주소 경계 검사를 구현했습니다.)

"If an out-of-bound access is detected (addr >= 32'h10), the AXI slave now blocks the internal register update and correctly asserts a Slave Error (SLVERR, 2'b10)." (범위를 벗어난 액세스가 감지되면(addr >= 32'h10) AXI 슬레이브는 이제 내부 레지스터 업데이트를 차단하고 올바르게 Slave Error(SLVERR, 2'b10)를 어서트합니다.)

"This bug fix ensures protocol compliance and makes the hardware robust against invalid transactions." (이 버그 수정은 프로토콜 준수를 보장하고 잘못된 트랜잭션에 대해 하드웨어를 견고하게 만듭니다.)

DUT 수정뒤 error sequence 넣었을때 결과


Captured WRITE: Addr=ffff0000, Data=deaddead, Resp=10
우리가 의도적으로 찌른 쓰레기 주소(0xFFFF0000)에 대해, 이제 하드웨어는 무조건 수용(OKAY: 00)하지 않고, 정확히 2'b10 (SLVERR)을 뱉어내며 공격을 튕겨내고 있습니다. 모니터가 이 신호를 정확히 낚아채서(Capture) 화면에 띄워준 것입니다.

Week 6 Functional Coverage

커버리지 26.67%의 수학적 근거 (Coverage Calculation)
우리가 방금 돌린 시나리오는 단 하나, sy_vseq_error (주소: 0xFFFF0000, 명령: WRITE)였습니다.UVM Covergroup은 기본적으로 내부 Coverpoint들의 달성률 평균을 냅니다.

  1. cp_kind (명령 종류): 총 2개 Bin (READ, WRITE) 중 WRITE 1개 달성 \rightarrow 50%
  2. cp_addr (주소 영역): 총 5개 Bin (reg0, 1, 2, 3, out_of_bound) 중 out_of_bound 1개 달성 \rightarrow 20%
  3. cross_kind_addr (교차 검증): 총 10개 조합 (2 x 5) 중 WRITE + out_of_bound 1개 달성 \rightarrow 10%

이 세 항목의 평균을 내면: (50 + 20 + 10) / 3 = 26.666...% 정확히 로그에 찍힌 26.67%가 나옵니다.결론적으로, 나머지 73.33%의 텅 빈 공간이 바로 우리가 채워 넣어야 할 Coverage Hole(커버리지 누수)입니다. 정상적인 주소들에 대한 읽기/쓰기 테스트가 전혀 진행되지 않았다는 것을 데이터가 말해주고 있습니다.


우리의 채점표(cross_kind_addr)는명령어 종류(READ, WRITE) 2개와 주소 범위 5개(0x00, 0x04, 0x08, 0x0C, out_of_bound)를 곱하여 총 10개의 조합을 요구합니다.

로그를 자세히 보면 우리가 채운 빈(Bin)은 다음과 같습니다.

  • 정상 주소 4개: WRITE 4번 + READ 4번 성공 (8개 달성)
  • 비정상 주소 (out_of_bound): sy_vseq_error에서 WRITE 1번 성공 (1개 달성)

총 10개의 미션 중 9개(90%)만 달성한 상태입니다.
빠진 단 하나의 미션은 바로 "비정상 주소(out_of_bound)에 대한 READ(읽기) 공격"입니다. 우리는 쓰레기 주소에 값을 써보기만(WRITE) 했지, 그 쓰레기 주소에서 값을 읽어오려는(READ) 시도는 한 번도 하지 않았습니다.

전체 점수 계산식:

  • cp_kind (READ/WRITE 모두 씀): 100%
  • cp_addr (5개 주소 영역 모두 찌름): 100%
  • cross_kind_addr (10개 조합 중 9개 달성): 90%
  • 평균: (100 + 100 + 90) / 3 = 96.666...%

이 구멍을 메우는 방법은 아주 간단합니다. 에러 주입 시나리오(sy_vseq_error.sv)에 비정상 주소로부터 값을 읽어오는(Read) 코드를 딱 한 줄 추가하면 됩니다.

profile
Design Verification engineer

0개의 댓글