AXI4-lite总线读写Verilog可综合设计——可用于JESD204核配置

 

目录

AXI4系列总线简介

AXI4-lite总线

通道信号时序要求

读写的时序图

写时序的可综合程序

读时序的可综合程序

JESD的AXI配置调试心得


AXI4系列总线简介

Advanced eXtensible Interface (AXI)是ARM的Advanced Micro controller Bus Architecture(AMBA)总线的一部分,第一个版本的AXI在2003年的AMBA3.0中被提出,在2010年的AMBA4.0中提出了第二个版本AXI4。

AXI4包含三个类型:

AXI4:主要面向高性能地址映射通信的需求;
AXI4-Lite(axi):是AXI的简化版本,例如可以用来进行控制与状态寄存器的配置与读取。
AXI4-stream(axis):是ARM公司和Xilinx公司一起提出,主要用在FPGA进行以数据为主导的大量数据的传输应用。在vivado的IPcatalog里可以看到很多IP核的数据传输是基于AXI4或AXI4-stream。

参考文档:

ug1037-vivado-axi-reference-guide.pdf:介绍AXI对xilinx的IP核以及Device的支持特征。
IHI0022D_amba_axi_protocol_spec.pdf: AXI4、AXI4-lite的协议内容。(在http://www.amba.com可以下载到)
IHI0051A_amba4_axi4_stream_v1_0_protocol_spec.pdf:AXI4-stream的协议内容。(在http://www.amba.com可以下载到)

AXI4-lite总线

其包含了5个独立的传输通道:读地址通道、读数据通道、写地址通道、写数据通道、写响应通道。如Table B1-1。

注:我在使用vivado 的JESD204B的jesd core时配置IP核的参数需要使用AXI4-lite的配置接口(Table B1-1中的awport,arport没有用到),但是其example design只给出仿真时序,因此我又写出来了可综合的Verilog设计,主要思想是状态机控制。

AXI4-lite总线读写Verilog可综合设计——可用于JESD204核配置_第1张图片

 AXI是基于VALID/READY的握手机制数据传输协议,传输源端使用VALID表明地址/控制信号、数据是有效的,目的端使用READY表明自己已经接受信息。

通道信号时序要求

通道握手信号:每个通道有自己的xVALID/xREADY握手信号对。

写地址通道:当主机驱动有效awaddr与awvalid时,保持有效状态,直到主机在时钟上升沿采样到从机的awready有效后,可以将awaddr与awvalid无效。

写数据通道:当主机驱动有效wdata与wvalid时,保持有效状态,直到主机在时钟上升沿采样到从机的wready有效后,可以将wdata与wvalid无效。

写响应通道:在写地址与写数据均传输后且生效后,从机有效写响应信号bresp与bvalid,直到时钟上升沿采样到主机的bready信号。

读地址通道:当主机驱动有效araddr与arvalid时,保持有效状态,直到主机在时钟上升沿采样到从机的arready有效后,可以将araddr与arvalid无效。

读数据通道:主机传输完地址后就将rready有效表示准备好接收读数据,从机接收到地址后传输所读地址的数据,从机驱动有效rdata与rvalid、rresp,从机在时钟上升沿采样到主机的rready有效后,可以将rdata与rvalid、rresp无效,同时主机采样到有效的rdata与rvalid、rresp后下一个时钟也会使rready无效。

综上可以用状态机来控制读写的过程。

读写的时序图

AXI4-lite总线读写Verilog可综合设计——可用于JESD204核配置_第2张图片 读写时序仿真

写时序的可综合程序

module axi_write(
    input s_axi_aclk,
    input s_axi_aresetn,
    input s_axi_awready,
    input s_axi_wready,
    input s_axi_bvalid,
    input [1:0] s_axi_bresp,
    output reg [31:0] s_axi_awaddr,
    output reg s_axi_awvalid,
    output reg [31:0] s_axi_wdata,
    output reg s_axi_wvalid,
    output reg s_axi_bready
    );
localparam pLanes = 2;
// F = 1 K = 20
localparam pF        = 1;///
localparam pK        = 20;///

// Setup the link configuration parameters.
localparam [7:0] pDID      = 8'h55;//不变
localparam [3:0] pADJCNT   = 4'h0;//不变
localparam [3:0] pBID      = 4'hA;//不变
localparam       pADJDIR   = 1'b0;//不变
localparam       pPHADJ    = 1'b0;//不变
localparam       pSCR      = 1'b0;/扰码
localparam [4:0] pL        = (pLanes-1);///
localparam [7:0] pM        = 3;///
localparam [1:0] pCS       = 2'd0;
localparam [4:0] pN        = 5'd15;//
localparam [4:0] pNt       = 5'd15;/
localparam [2:0] pSUBCV    = 3'b001;//不变
localparam [2:0] pJESDV    = 3'b001;//不变
localparam [4:0] pS        = 5'd0;/
localparam       pHD       = 1'b0;/
localparam [4:0] pCF       = 5'd0;
localparam [7:0] pRES1     = 8'h5A;//不变
localparam [7:0] pRES2     = 8'hA5;//不变

localparam [4:0] IDLEW=      5'b00001;
localparam [4:0] DRIVEW=     5'b00010;
localparam [4:0] ADD_RES=   5'b00100;
localparam [4:0] DAT_RES=   5'b01000;
localparam [4:0] BRES=      5'b10000;
localparam WRITE_NUM = 14;///配置
reg [4:0] curr_ws,next_ws;
reg write_over;
reg [31:0] wadd;
reg [9:0] w_cnt;/对写入的数据个数进行计数,配置完成将write_over置高
reg [31:0] wdata;
reg [1:0] resp;

always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)  curr_ws <= IDLEW;
else                curr_ws <= next_ws;

always@(*)
begin
next_ws = 'dx;
case(curr_ws)
    IDLEW:      if(write_over==0)       next_ws = DRIVEW;   else next_ws = IDLEW;  
    DRIVEW:     if(s_axi_awready==1)    next_ws = ADD_RES;  else next_ws = DRIVEW;
    ADD_RES:    if(s_axi_wready==1)     next_ws = DAT_RES;  else next_ws = ADD_RES;
    DAT_RES:    if(s_axi_bvalid==1)     next_ws = BRES;     else next_ws = DAT_RES;
    BRES:       if(write_over==1)       next_ws = IDLEW;    else next_ws = DRIVEW;
    default:    next_ws = IDLEW;    
endcase
end

always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
begin
        s_axi_awaddr  <= 0;              s_axi_awvalid <= 0;    
        s_axi_wdata   <= 0;              s_axi_wvalid  <= 0;    
        s_axi_bready  <= 0;              w_cnt <= 0;        resp <= 0;
end
else 
case(curr_ws)                                                                 
    IDLEW: 
    begin 
        s_axi_awaddr  <= 0;             s_axi_awvalid <= 0;
        s_axi_wdata   <= 0;             s_axi_wvalid  <= 0;
        s_axi_bready  <= 0;             w_cnt <= w_cnt;     resp <= 0;
    end    
    DRIVEW:
    begin
        if(s_axi_awready==1)                                                  
        begin s_axi_awaddr <= 0;        s_axi_awvalid <= 0;   end     
        else                                                          
        begin s_axi_awaddr <= wadd;     s_axi_awvalid <= 1;   end            
        s_axi_wdata   <= wdata;         s_axi_wvalid  <= 1;
        s_axi_bready  <= 0;             w_cnt <= w_cnt;     resp <= 0;
    end       
    ADD_RES:  
    begin
        s_axi_awaddr  <= 0;             s_axi_awvalid <= 0;
        if(s_axi_wready==1)
        begin  s_axi_wdata <= 0;        s_axi_wvalid  <= 0;  w_cnt <= w_cnt+1;end //else keep//每传输一个数据加1
        s_axi_bready  <= 0;             resp <= 0;
    end     
    DAT_RES: 
    begin
       s_axi_awaddr  <= 0;              s_axi_awvalid <= 0;
       s_axi_wdata   <= 0;              s_axi_wvalid  <= 0;
       if(s_axi_bvalid==1)
       begin s_axi_bready  <= 1;        resp <= s_axi_bresp;end    //else keep
       w_cnt <= w_cnt;
    end        
    BRES:
    begin
        s_axi_awaddr  <= 0;             s_axi_awvalid <= 0;
        s_axi_wdata   <= 0;             s_axi_wvalid  <= 0;       w_cnt <= w_cnt;
        if(s_axi_bready)   
        begin s_axi_bready <= 0;        resp <= 0;          end//只持续一个高电平
        else 
        begin s_axi_bready <= 1;        resp <= s_axi_bresp;end
    end        
    default: 
    begin
       s_axi_awaddr  <= 0;              s_axi_awvalid <= 0;
       s_axi_wdata   <= 0;              s_axi_wvalid  <= 0;
       s_axi_bready  <= 0;              w_cnt <= 0;resp <= 0;
    end                                                  
endcase

always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)                      write_over <= 0;  
else if( w_cnt == WRITE_NUM)            write_over <= 1;//else keep

always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
begin    
        wadd <= 0;       wdata <= 0;
end     
else 
case( w_cnt )
0:begin wadd <=32'h008   ;wdata <= 32'h00000001  ; end 
1:begin wadd <=32'h00C   ;wdata <= {31'b0,pSCR}  ; end 
2:begin wadd <=32'h010   ;wdata <= {31'b0,1'b1}  ; end 
3:begin wadd <=32'h014   ;wdata <= 32'h00000003  ; end 
4:begin wadd <=32'h018   ;wdata <= 32'h00000000  ; end 
5:begin wadd <=32'h020   ;wdata <= {24'b0,8'b0}  ; end 
6:begin wadd <=32'h024   ;wdata <= {27'b0,5'd19}  ; end 
7:begin wadd <=32'h028   ;wdata <= {24'b0,8'hff}  ; end 
8:begin wadd <=32'h02C   ;wdata <= 32'h00000001  ; end 
9:begin wadd <=0   ;wdata <= 0  ; end //begin wadd <=32'h80C   ;wdata <= {3'b0, pL, 12'b0, pBID, pDID}  ; end 
10:begin wadd <=32'h810  ;wdata <= {6'b0, pCS, 3'b0, pNt, 3'b0, pN, pM}  ; end 
11:begin wadd <=32'h814  ;wdata <= {3'b0, pCF, 7'b0, pHD, 3'b0, pS, 7'b0, pSCR}  ; end 
12:begin wadd <=0   ;wdata <= 0  ; end //begin wadd <=32'h818  ;wdata <= {16'b0, pRES2, pRES1}  ; end 
13:begin wadd <=32'h004  ;wdata <= 32'h00000001  ; end 
default:begin wadd <=32'h008  ;wdata <= 32'h00000001 ; end 
endcase  
   
endmodule

//test bench
module axi_sim;
parameter CLK_PERIOD = 10; //仿真周期ns
parameter RST_CYCLE = 20; //复位周期数
parameter RST_TIME = RST_CYCLE * CLK_PERIOD; 

reg sim_clk;
reg sim_rstn;

initial
begin
    sim_clk = 0;
    sim_rstn = 0;
    #RST_TIME sim_rstn = 1;
end
    
always #(CLK_PERIOD/2) sim_clk = ~sim_clk;

reg s_axi_awready;
reg s_axi_wready;
reg s_axi_bvalid;
reg [1:0] s_axi_bresp;

wire [31:0] s_axi_awaddr;
wire s_axi_awvalid;
wire [31:0] s_axi_wdata;
wire s_axi_wvalid;
wire s_axi_bready;
task virtual_rx;
begin
    while(!s_axi_awvalid)
        @(posedge sim_clk);
    #1 s_axi_awready = 1;
    @(posedge sim_clk);#1 s_axi_awready = 0;
    while(s_axi_awvalid)
        @(posedge sim_clk);
    @(posedge sim_clk);#1 s_axi_wready = 1;
    @(posedge sim_clk);#1 s_axi_wready = 0;s_axi_bvalid = 1;s_axi_bresp = 2'b11;
    @(posedge sim_clk); 
    @(posedge sim_clk); #1 s_axi_bvalid = 0;s_axi_bresp = 0;//2个周期  
end    
endtask
//always@(posedge sim_clk)



integer i;
initial 
begin
    s_axi_awready=0;    
    s_axi_wready =0;
    s_axi_bvalid =0;
    s_axi_bresp  =0;
    #RST_TIME
    for(i=0;i<14;i=i+1)
    begin
        virtual_rx;    //task
    end
    
end
     
/***********************************************************/
axi_write axi_write_u (
.s_axi_aclk(sim_clk ),
.s_axi_aresetn(sim_rstn ),
.s_axi_awready(s_axi_awready ),
.s_axi_wready(s_axi_wready ),
.s_axi_bvalid(s_axi_bvalid ),
.s_axi_bresp(s_axi_bresp ),
.s_axi_awaddr(s_axi_awaddr),
.s_axi_awvalid(s_axi_awvalid),
.s_axi_wdata(s_axi_wdata),
.s_axi_wvalid(s_axi_wvalid),
.s_axi_bready(s_axi_bready)
 );
 
endmodule

读时序的可综合程序

module axi_read(
  input s_axi_aclk,
  input s_axi_aresetn,
  input s_axi_arready,
  input s_axi_rvalid,
  input [31:0] s_axi_rdata,
  input [1:0]s_axi_rresp,
  output reg [31:0] s_axi_araddr,
  output reg s_axi_arvalid,
  output reg s_axi_rready
    );

localparam [2:0] IDLER=     3'b001;
localparam [2:0] DRIVER=    3'b010;
localparam [2:0] RDATA=     3'b100;
localparam READ_NUM=        14;

reg [4:0] curr_rs,next_rs;
reg read_over;
reg [31:0] radd;
reg [9:0] r_cnt;
reg [31:0] rdata;
reg [1:0] rresp;
    
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)  curr_rs <= IDLER;
else                curr_rs <= next_rs;

always@(*)
begin
next_rs = 'dx;
case(curr_rs)
    IDLER:       if(read_over==0 && s_axi_arready==1)   next_rs = DRIVER;   else    next_rs = IDLER;  
    DRIVER:      if(s_axi_arvalid==1)                   next_rs = RDATA;    else    next_rs = DRIVER;
    RDATA:       if(s_axi_rvalid==1)                    next_rs = IDLER;    else    next_rs = RDATA;
    default:     next_rs = IDLER;    
endcase
end  

always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
begin
    s_axi_araddr  <= 0;         s_axi_arvalid <= 0;    
    s_axi_rready  <= 0;  
    rdata <= 0;                 rresp <= 0;      r_cnt <= 0;   
end
else 
case(curr_rs)                                                                 
    IDLER: 
    begin 
        rdata <= 0;rresp <= 0; 
        if(read_over==0 && s_axi_arready==1) 
        begin
            s_axi_araddr  <= radd;  s_axi_arvalid <= 1;    
            s_axi_rready  <= 0;     r_cnt <= r_cnt+1;   //控制个数
        end
        else
        begin
            s_axi_araddr  <= 0; s_axi_arvalid <= 0;    
            s_axi_rready  <= 0; r_cnt <= r_cnt;
        end
    end    
    DRIVER:
    begin
        rdata <= 0;rresp <= 0;r_cnt <= r_cnt;
        if(s_axi_arvalid==1)   
        begin
            s_axi_araddr  <= 0; s_axi_arvalid <= 0;
            s_axi_rready  <= 1;    
        end//else keep
    end       
    RDATA:  
    begin
       s_axi_araddr  <= 0; s_axi_arvalid <= 0;   r_cnt <= r_cnt;
       if(s_axi_rvalid==1) 
       begin              
            s_axi_rready  <= 0; 
            rdata <= s_axi_rdata; rresp <= s_axi_rresp;    
       end //else keep
    end  
    default: 
    begin
        s_axi_araddr  <= 0; s_axi_arvalid <= 0;
        s_axi_rready  <= 0;                    
        rdata <= 0;rresp <= 0;  r_cnt <= r_cnt;                     
    end                                                  
endcase

always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
    read_over <= 0;  
else if( r_cnt == READ_NUM)
    read_over <= 1;//else keep

always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn) 
    radd <= 0;   
else 
case( r_cnt )
0:  radd <=32'h008  ; 
1:  radd <=32'h00C  ; 
2:  radd <=32'h010  ; 
3:  radd <=32'h014  ; 
4:  radd <=32'h018  ; 
5:  radd <=32'h020  ; 
6:  radd <=32'h024  ; 
7:  radd <=32'h028  ; 
8:  radd <=32'h02C  ;
9:  radd <=32'h80C  ;
10: radd <=32'h810  ;
11: radd <=32'h814  ;
12: radd <=32'h818  ;
13: radd <=32'h004  ;
default: radd <=32'h004;
endcase 
endmodule
/
//test bench
module axi_read_sim;
parameter CLK_PERIOD = 10; //仿真周期ns
parameter RST_CYCLE = 20; //复位周期数
parameter RST_TIME = RST_CYCLE * CLK_PERIOD; 

reg sim_clk;
reg sim_rstn;

initial
begin
    sim_clk = 0;
    sim_rstn = 0;
    #RST_TIME sim_rstn = 1;
end
    
always #(CLK_PERIOD/2) sim_clk = ~sim_clk;
/*************************************************************/
wire [31:0] s_axi_araddr;
wire s_axi_arvalid;
wire s_axi_rready;
reg s_axi_arready;
reg s_axi_rvalid;
reg [31:0] s_axi_rdata;
reg [1:0] s_axi_rresp;

task virtual_rx;
input [31:0] rdata;
input [1:0] resp;
begin
    @(posedge sim_clk);#1 s_axi_arready = 1;
    while(!s_axi_arvalid)
        @(posedge sim_clk);//接收到有效的地址后,拉低
    //@(posedge sim_clk);
    #1 s_axi_arready = 0;
    #(CLK_PERIOD*4)
    #1 s_axi_rvalid = 1; 
    s_axi_rdata = rdata;
    s_axi_rresp = resp;
    while(!s_axi_rready)
        @(posedge sim_clk); //对方收到后拉低
    @(posedge sim_clk);    
    #1 s_axi_rvalid = 0; 
    s_axi_rdata = 0;
    s_axi_rresp = 0;
end    
endtask

integer i;
initial 
begin
    s_axi_arready =0;     
    s_axi_rvalid  =0;  
    s_axi_rdata   =0;
    s_axi_rresp   =0;
    #RST_TIME
    #CLK_PERIOD
    virtual_rx('h23,3);
    virtual_rx('h48,2);
    virtual_rx('h7d,1);  virtual_rx('h48,2);virtual_rx('h23,0);
    virtual_rx('h7d,1);  virtual_rx('h48,2);virtual_rx('h23,0);
    virtual_rx('h7d,1);  virtual_rx('h48,2);virtual_rx('h23,0);
    virtual_rx('h7d,1);  virtual_rx('h48,2);virtual_rx('h23,0);
    virtual_rx('h7d,1);  virtual_rx('h48,2);virtual_rx('h23,0);
end


axi_read axi_read_u (
.s_axi_aclk(sim_clk ),
.s_axi_aresetn(sim_rstn ),
.s_axi_arready(s_axi_arready ),
.s_axi_rvalid(s_axi_rvalid ),
.s_axi_rdata(s_axi_rdata ),
.s_axi_rresp(s_axi_rresp ),
.s_axi_araddr(s_axi_araddr),
.s_axi_arvalid(s_axi_arvalid),
.s_axi_rready(s_axi_rready)
 );

endmodule

注:我的testbench其中有些:#1 信号x =0;其实是为了使得非阻塞时序在判断信号x变化后动作的时间点在下一个时钟沿,若直接:信号x =0;在仿真时会在当前时钟沿动作,这不是我们期望的。

JESD的AXI配置调试心得

vivado的JESD204Bv7.2核的example design 发射端tx给出的demo_tb.v中有关于AXI4-lite的配置寄存器的细节,但注意的是寄存器0x80C和0x818不能像仿真里那样配置配置了会出大事!因为PG066手册里写到是不需要配置的:[RX only, not writeable for TX]。
我一开始没有注意就把上面的所有的寄存器放到可综合里面去了,这样仿真可以通过,且在FPGA内部用jesd_core_tx---jesdphy---jesd_core_rx内部自环下载到板子上也通过了,然后在FPGA上jesd_core_tx---jesdphy---带有jesd功能的的DAC芯片就无法成功,主要是jesd的关键信号tx_sync一直无法变高,即无法同步,然后痛苦的来了,我就各种找问题,找JESD核的配置界面是否出问题,以及SYSREF信号与参数K的关系,以及DAC芯片的配置的问题,xdc的管教约束等等,后来搞到了一个FPGA核与DA正常同步的例程,虽然可以直接把例子套到我的工程上去,但是我本着debug的心态想找出我失败的原因,一顿折腾之后终于发现了上面所说的原因,0x80c与0x818不该配置。
这前前后后已经用了接近两周的时间,问题发现了感觉也不是很复杂的问题,但你不发现就是解决不了。最后反思一下,第一是不够细心,若是早早看一下寄存器配置的就好了,第二是问题把握的不透彻,总是怀疑这怀疑那的最后找不到问题的重点,第三要与人多交流,自己一人调试是真的有点难受(事实上我们实验室就我一个人在做FPGA(哭)好在我对这个还挺感兴趣)。

最后,如果有用就点个赞吧~

 

你可能感兴趣的:(AXI4-lite总线读写Verilog可综合设计——可用于JESD204核配置)