적극적 생각/FPGA

0715 - 섹션3 - AXI4-Lite Interface 코드리뷰(Xilinx Template Code)

무말랭이 2022. 7. 16. 00:17

---

 

AXI Outstanding

AHB 대비 AXI 프로토콜의 장점 몇 가지

http://egloos.zum.com/donghyun53/v/4087409

 

AHB 대비 AXI 프로토콜의 장점 몇 가지

지난 번 AHB 프로토콜에 대해서 간단히 글을 썼었는데, 이번엔 좀 더 글의 범위를 넓혀서 AHB 프로토콜과 비교하여 AXI 프로토콜이 가진 장점에 대해서 한 번 정리해 보려고 합니다. AXI 프로토콜은

egloos.zum.com

 

---

 

07_axi4_lite_project 라는 프로젝트를 생성해주고

Tools > Create new IP

레지스터 개수가 512인 이유는 template이 대략 지원하는 범위라서 그렇고

실무적으로 512개까지 쓰는 경우도 거의 없다.

 

자동으로 timing diagram을 보여준다.

 

open block design을 해주자.

 

우리가 만든 부분은 이 부분인데

자동으로 AXI VIP 부분과 연결이 되어있다.

(시뮬레이션을 위한 펑션을 검증하기 위한 모듈이라서
합성을 한다거나 하는 목적으로 활용하지는 않음)

 

다시 diagram으로 돌아오면

 

우리가 레지스터 4개를 사용했으니깐

이렇게 나오는건데

 

Write를 4번하고

Read를 4번한후에

그 둘이 맞는지를 검증하는

시뮬레이션이 셋팅되어있는 것이다.

 

tb가 system verilog로 작성되어 있는 것

`timescale 1ns / 1ps
`include "lab7_axi4_lite_v1_0_tb_include.svh"

import axi_vip_pkg::*;
import lab7_axi4_lite_v1_0_bfm_1_master_0_0_pkg::*;

module lab7_axi4_lite_v1_0_tb();


xil_axi_uint                            error_cnt = 0;
xil_axi_uint                            comparison_cnt = 0;
axi_transaction                         wr_transaction;   
axi_transaction                         rd_transaction;   
axi_monitor_transaction                 mst_monitor_transaction;  
axi_monitor_transaction                 master_moniter_transaction_queue[$];  
xil_axi_uint                            master_moniter_transaction_queue_size =0;  
axi_monitor_transaction                 mst_scb_transaction;  
axi_monitor_transaction                 passthrough_monitor_transaction;  
axi_monitor_transaction                 passthrough_master_moniter_transaction_queue[$];  
xil_axi_uint                            passthrough_master_moniter_transaction_queue_size =0;  
axi_monitor_transaction                 passthrough_mst_scb_transaction;  
axi_monitor_transaction                 passthrough_slave_moniter_transaction_queue[$];  
xil_axi_uint                            passthrough_slave_moniter_transaction_queue_size =0;  
axi_monitor_transaction                 passthrough_slv_scb_transaction;  
axi_monitor_transaction                 slv_monitor_transaction;  
axi_monitor_transaction                 slave_moniter_transaction_queue[$];  
xil_axi_uint                            slave_moniter_transaction_queue_size =0;  
axi_monitor_transaction                 slv_scb_transaction;  
xil_axi_uint                           mst_agent_verbosity = 0;  
xil_axi_uint                           slv_agent_verbosity = 0;  
xil_axi_uint                           passthrough_agent_verbosity = 0;  
bit                                     clock;
bit                                     reset;
integer result_slave;  
bit [31:0] S00_AXI_test_data[3:0]; 
 localparam LC_AXI_BURST_LENGTH = 8; 
 localparam LC_AXI_DATA_WIDTH = 32; 
task automatic COMPARE_DATA; 
  input [(LC_AXI_BURST_LENGTH * LC_AXI_DATA_WIDTH)-1:0]expected; 
  input [(LC_AXI_BURST_LENGTH * LC_AXI_DATA_WIDTH)-1:0]actual; 
  begin 
    if (expected === 'hx || actual === 'hx) begin 
      $display("TESTBENCH ERROR! COMPARE_DATA cannot be performed with an expected or actual vector that is all 'x'!"); 
 result_slave = 0;    $stop; 
  end 
  if (actual != expected) begin 
    $display("TESTBENCH ERROR! Data expected is not equal to actual.",     " expected = 0x%h",expected,     " actual   = 0x%h",actual); 
    result_slave = 0; 
    $stop; 
  end 
  else  
    begin 
     $display("TESTBENCH Passed! Data expected is equal to actual.", 
              " expected = 0x%h",expected,               " actual   = 0x%h",actual); 
    end 
  end 
endtask 
integer                                 i; 
integer                                 j;  
xil_axi_uint                            trans_cnt_before_switch = 48;  
xil_axi_uint                            passthrough_cmd_switch_cnt = 0;  
event                                   passthrough_mastermode_start_event;  
event                                   passthrough_mastermode_end_event;  
event                                   passthrough_slavemode_end_event;  
xil_axi_uint                            mtestID;  
xil_axi_ulong                           mtestADDR;  
xil_axi_len_t                           mtestBurstLength;  
xil_axi_size_t                          mtestDataSize;   
xil_axi_burst_t                         mtestBurstType;   
xil_axi_lock_t                          mtestLOCK;  
xil_axi_cache_t                         mtestCacheType = 0;  
xil_axi_prot_t                          mtestProtectionType = 3'b000;  
xil_axi_region_t                        mtestRegion = 4'b000;  
xil_axi_qos_t                           mtestQOS = 4'b000;  
xil_axi_data_beat                       dbeat;  
xil_axi_data_beat [255:0]               mtestWUSER;   
xil_axi_data_beat                       mtestAWUSER = 'h0;  
xil_axi_data_beat                       mtestARUSER = 0;  
xil_axi_data_beat [255:0]               mtestRUSER;      
xil_axi_uint                            mtestBUSER = 0;  
xil_axi_resp_t                          mtestBresp;  
xil_axi_resp_t[255:0]                   mtestRresp;  
bit [63:0]                              mtestWDataL; 
bit [63:0]                              mtestRDataL; 
axi_transaction                         pss_wr_transaction;   
axi_transaction                         pss_rd_transaction;   
axi_transaction                         reactive_transaction;   
axi_transaction                         rd_payload_transaction;  
axi_transaction                         wr_rand;  
axi_transaction                         rd_rand;  
axi_transaction                         wr_reactive;  
axi_transaction                         rd_reactive;  
axi_transaction                         wr_reactive2;   
axi_transaction                         rd_reactive2;  
axi_ready_gen                           bready_gen;  
axi_ready_gen                           rready_gen;  
axi_ready_gen                           awready_gen;  
axi_ready_gen                           wready_gen;  
axi_ready_gen                           arready_gen;  
axi_ready_gen                           bready_gen2;  
axi_ready_gen                           rready_gen2;  
axi_ready_gen                           awready_gen2;  
axi_ready_gen                           wready_gen2;  
axi_ready_gen                           arready_gen2;  
xil_axi_payload_byte                    data_mem[xil_axi_ulong];  
lab7_axi4_lite_v1_0_bfm_1_master_0_0_mst_t          mst_agent_0;

  `BD_WRAPPER DUT(
      .ARESETN(reset), 
      .ACLK(clock) 
    ); 
  
initial begin
     mst_agent_0 = new("master vip agent",DUT.`BD_INST_NAME.master_0.inst.IF);//ms  
   mst_agent_0.vif_proxy.set_dummy_drive_type(XIL_AXI_VIF_DRIVE_NONE); 
   mst_agent_0.set_agent_tag("Master VIP"); 
   mst_agent_0.set_verbosity(mst_agent_verbosity); 
   mst_agent_0.start_master(); 
     $timeformat (-12, 1, " ps", 1);
  end
  initial begin
    reset <= 1'b0;
    #200ns;
    reset <= 1'b1;
    repeat (5) @(negedge clock); 
  end
  always #5 clock <= ~clock;
  initial begin
      S_AXI_TEST ( );

      #1ns;
      $finish;
  end
task automatic S_AXI_TEST;  
begin   
#1; 
   $display("Sequential write transfers example similar to  AXI BFM WRITE_BURST method starts"); 
   mtestID = 0; 
   mtestADDR = 64'h00000000; 
   mtestBurstLength = 0; 
   mtestDataSize = xil_axi_size_t'(xil_clog2(32/8)); 
   mtestBurstType = XIL_AXI_BURST_TYPE_INCR;  
   mtestLOCK = XIL_AXI_ALOCK_NOLOCK;  
   mtestCacheType = 0;  
   mtestProtectionType = 0;  
   mtestRegion = 0; 
   mtestQOS = 0; 
   result_slave = 1; 
  mtestWDataL[31:0] = 32'h00000001; 
  for(int i = 0; i < 4;i++) begin 
  S00_AXI_test_data[i] <= mtestWDataL[31:0];   
  mst_agent_0.AXI4LITE_WRITE_BURST( 
  mtestADDR, 
  mtestProtectionType, 
  mtestWDataL, 
  mtestBresp 
  );   
  mtestWDataL[31:0] = mtestWDataL[31:0] + 1; 
  mtestADDR = mtestADDR + 64'h4; 
  end 
     $display("Sequential write transfers example similar to  AXI BFM WRITE_BURST method completes"); 
     $display("Sequential read transfers example similar to  AXI BFM READ_BURST method starts"); 
     mtestID = 0; 
     mtestADDR = 64'h00000000; 
     mtestBurstLength = 0; 
     mtestDataSize = xil_axi_size_t'(xil_clog2(32/8)); 
     mtestBurstType = XIL_AXI_BURST_TYPE_INCR;  
     mtestLOCK = XIL_AXI_ALOCK_NOLOCK;  
     mtestCacheType = 0;  
     mtestProtectionType = 0;  
     mtestRegion = 0; 
     mtestQOS = 0; 
 for(int i = 0; i < 4;i++) begin 
   mst_agent_0.AXI4LITE_READ_BURST( 
        mtestADDR, 
        mtestProtectionType, 
        mtestRDataL, 
        mtestRresp 
      ); 
   mtestADDR = mtestADDR + 64'h4; 
   COMPARE_DATA(S00_AXI_test_data[i],mtestRDataL); 
 end 
     $display("Sequential read transfers example similar to  AXI BFM READ_BURST method completes"); 
     $display("Sequential read transfers example similar to  AXI VIP READ_BURST method completes"); 
     $display("---------------------------------------------------------"); 
     $display("EXAMPLE TEST S00_AXI: PTGEN_TEST_FINISHED!"); 
     if ( result_slave ) begin                    
       $display("PTGEN_TEST: PASSED!");                  
     end    else begin                                       
       $display("PTGEN_TEST: FAILED!");                  
     end                                
     $display("---------------------------------------------------------"); 
  end 
endtask  

endmodule

 

이제 AXI를 펼쳐보자

 

여기서 Outstanding 개념이 나오는데

AXI Performance 향상에 주요 개념이다.

 

간단하게 짚고 넘어가면

Template Code에서는

Multiple Outstanding을 지원하지 않는다.

정도로 가볍게 확인해볼 수 있다.

 

자 이제 하나씩 살펴보자

 

<Write Address>

 

두개가 1로 set되는 handshake구간의 값을 확인하면 된다.

거기서 쓰고자 하는 값이 있고

 

<Write Response>

 

그 값이 잘 쓰여졌는지에 대한 Response를 보내주고

response의 값이 0이라는 것은 정상이라는 것

즉 현재 시뮬레이션은 모두 0

<Write Address>

address는 이렇게 주고있고

(왜 4씩 증가하냐? 4바이트라서. 

AXI의 address는 Byte단위라서

4바이트씩 접근해야

0,1,2,3을 순차적으로 접근할 수 있는 것)

 

이제는 Read

 

여기까지의 과정을 통해서

개념과 waveform의 matching을 마쳤다.

 

---

 

이제부터는 code review

 

---

 

커스터마이징을 위한 유저 파라미터 영역도 뚫려있다.

이런 코멘트 영역이 많다.

 

먼저 hierachy 를 보자.

 

32비트 4레지스터

 

5개의 채널의 시그널

 

그리고나서 Instantiation이 되어있으며

 

이 모듈들이 실제 AXI 역할을 수행

 

---

 

clk, reset을 시작으로

in out port 들이 선언되어있다.

 

 

권한관련 내용 - 미사용

 

 

wstrb는 write strobe라는 내용인데 조금 생소함

4바이트를 바이트 단위로 write를 할 수 있으며

one hot encoding으로 동작한다.

 

 

조금 복잡한 내용이라 우리는 우선 전체를 사용.

 

---

 

레지스터 선언

신호들을 캡처 또는 래칭할 때 사용

 

 

이 부분은 address 부분에서 하위 2bits를 안보겠다는 이야기 

(레지스터 접근할때 4씩 증가하기 때문에, 4 이전의 2bits는 필요가 없음.)

 

 

이 레지스터들이 우리가 쓰는 레지스터

 

포트와 신호들이 assign 되어있다.

 

그 다음부터의 @always 들이 실질적인 동작

여기서부터는 그냥 베릴로그임.

 

어떤 상황일때 어떤 레지스터를 이용할거냐는 부분도 있고

 

여기서부터는 read

 

 

원하는 레지스터의 값을 뽑아서 본다.

 

마지막으로 read data 출력

 

---

 

마무리