Xilinx MIG IP之AXI接口仿真

1 MIG AXI接口简介

   按照Xilinx官方用户手册UG586的描述,AXI4接口模块将AXI4事务映射到用户接口,为DDR控制器提供行业标准的总线协议接口。AXI4的使用,在互联上和读写上交互都更加方便。

image

2 AXI 的窄传输和非对齐传输

   使用AXI4作为MIG IP数据交互接口后,除了时钟复位和一些非读写数据接口信号,剩下就是AXI4的信号,AXI4接口符合AMBA标准,使用起来也简单,这里主要说说AXI4接口的窄传输和非对齐传输。一般DDR的用户数据信号axi_wdata和axi_rdata的位宽都很大,特别在级联多片DDR的情形下,数据位宽会更大,在读写比数据位宽更小的数据或者读写地址是非对齐总线字节数的,就要涉及AXI4的窄传输和非对齐传输了。

2.1 窄传输 Narrow Transfers

   窄传输,也就是每次传输的数据比axi_wdata/axi_rdata的总线字节位宽小,实现窄传输主要利用axi_awsize/axi_arsize信号,axi_awsize信号指示突发传输时,每次地址的偏移字节数,比如axi_wdata的位宽为256bits时,那么axi_awsize的最大有效值是2^5=32Byte。这里借用ARM官方手册的图展示窄传输示意,如下两图所示,注意下面的突发类型都是INCR,第一个图展示的是axi_wdata = 4Bytes, axi_awsize = 3'd0, axi_awaddr = 0,axi_awlen = 4;第二个图展示的是axi_wdata = 8Bytes, axi_awsize = 3'd2, axi_awaddr = 4, axi_awlen = 2。

image

image

2.2 非对齐传输 Unaligned Transfers

  非对齐传输,其实是相对于axi_awsize和axi_arsize来说的,比如当axi_awsize为3'd3时,那么axi_awaddr[2:0] !=0 的写请求,就是非对齐传输。举例:设axi_wdata/axi_rdata的位宽是256bits, 一般情形下,满带宽连续大数据传输,axi_awsize和axi_arsize需要设置为3‘d5 =  32 Bytes,那么我们每次读写访问的地址需要低5位为0,保证对齐传输,有时候访问的读写地址是非对齐的情形,那么就需要将axi_awsize或者axi_arsize的突发长度降低。还是这里借用ARM官方手册的图展示非对齐示意,如下图所示,axi_awsize = 3'd2,axi_wdata = 8Byte,图中三次的区别在哪呢,其实主要看axi_awaddr和axi_wstrb的结合使用,第一张图所示,写入数据地址是对齐axi_awsize大小的;第二和第三图所示,写入数据地址是非对齐axi_awsize大小的,那么将地址向下对齐后,同时使用axi_wstrb来屏蔽非有效写入数据。

image

3 IP仿真

  配置好MIG IP后,可以在vivado直接打开MIG IP的example 例程,该例程是完整的示例的工程,有完整的仿真的工程。但也可以自己建立testbench进行仿真,当ddr初始化完成后,按照AXI读写操作即可。

  仿真的流程是先对一段地址空间写入数据,然后再读出该地址空间的数据进行比较,若比较错误,则将err_flag信号置1,同时最后使用axi_arsize来测试一下窄传输。

仿真测试代码如下:

`timescale 1ps / 1ps
`define clk_period 2000

module ddr3_axi_test_tb();

wire [15:0]      ddr3_dq            ;
wire [1:0]       ddr3_dqs_n         ;
wire [1:0]       ddr3_dqs_p         ;
wire [14:0]      ddr3_addr          ;
wire [2:0]       ddr3_ba            ;
wire             ddr3_ras_n         ;
wire             ddr3_cas_n         ;
wire             ddr3_we_n          ;
wire             ddr3_reset_n       ;
wire             ddr3_ck_p          ;
wire             ddr3_ck_n          ;
wire             ddr3_cke           ;
wire             ddr3_cs_n          ;
wire [1:0]       ddr3_dm            ;
wire             ddr3_odt           ;

reg              sys_clk_p          ;
reg              sys_clk_n          ;

// differential iodelayctrl clk (reference clock)
reg              clk_ref_p          ;
reg              clk_ref_n          ;

wire             ui_clk             ;
wire             ui_clk_sync_rst    ;
wire             init_calib_complete;
wire  [11:0]     device_temp        ;
reg              sys_rst            ;

// user interface signals

wire             mmcm_locked        ;
reg              aresetn            ;
reg              app_sr_req         ;
reg              app_ref_req        ;
reg              app_zq_req         ;
wire             app_sr_active      ;
wire             app_ref_ack        ;
wire             app_zq_ack         ;

// axi4 interface signals

reg   [3:0]      s_axi_awid         ;
reg   [28:0]     s_axi_awaddr       ;
reg   [7:0]      s_axi_awlen        ;
reg   [2:0]      s_axi_awsize       ;
reg   [1:0]      s_axi_awburst      ;
reg   [0:0]      s_axi_awlock       ;
reg   [3:0]      s_axi_awcache      ;
reg   [2:0]      s_axi_awprot       ;
reg   [3:0]      s_axi_awqos        ;
reg              s_axi_awvalid      ;
wire             s_axi_awready      ;

reg   [127:0]    s_axi_wdata        ;
reg   [15:0]     s_axi_wstrb        ;
reg              s_axi_wlast        ;
reg              s_axi_wvalid       ;
wire             s_axi_wready       ;

reg              s_axi_bready       ;
wire  [3:0]      s_axi_bid          ;
wire  [1:0]      s_axi_bresp        ;
wire             s_axi_bvalid       ;

reg   [3:0]      s_axi_arid         ;
reg   [28:0]     s_axi_araddr       ;
reg   [7:0]      s_axi_arlen        ;
reg   [2:0]      s_axi_arsize       ;
reg   [1:0]      s_axi_arburst      ;
reg   [0:0]      s_axi_arlock       ;
reg   [3:0]      s_axi_arcache      ;
reg   [2:0]      s_axi_arprot       ;
reg   [3:0]      s_axi_arqos        ;
reg              s_axi_arvalid      ;
wire             s_axi_arready      ;

reg              s_axi_rready       ;
wire  [3:0]      s_axi_rid          ;
wire  [127:0]    s_axi_rdata        ;
wire  [1:0]      s_axi_rresp        ;
wire             s_axi_rlast        ;
wire             s_axi_rvalid       ;

reg   [127:0]    rd_data_cnt        ;
reg              rd_data_err        ;


parameter REFCLK_FREQ           = 200.0;
localparam real REFCLK_PERIOD = (1000000.0/(2*REFCLK_FREQ));//200Mhz used to ddr_ip iodelayctrl

initial     sys_clk_p = 1;
always #(`clk_period/2) sys_clk_p = ~sys_clk_p;

initial     sys_clk_n = 0;
always #(`clk_period/2) sys_clk_n = ~sys_clk_n;

initial     clk_ref_p = 1;
always #REFCLK_PERIOD clk_ref_p = ~clk_ref_p;

initial     clk_ref_n = 0;
always #REFCLK_PERIOD clk_ref_n = ~clk_ref_n;

initial begin
    sys_rst    = 0;
    aresetn = 0;
    #1000001;//ps
    sys_rst    = 1;
    aresetn = 1;
    
    #(`clk_period*1000000);
    $display("Simulation Time Overflow %0t", $time);
    $stop;
end

initial begin   
    app_sr_req  = 1'b0;
    app_ref_req = 1'b0;
    app_zq_req  = 1'b0;
end

initial begin
    s_axi_awid    = 'h0;
    s_axi_awaddr  = 'h0;
    s_axi_awlen   = 'h0;
    s_axi_awsize  = 'h4;
    s_axi_awburst = 'h1;
    s_axi_awlock  = 'h0;
    s_axi_awcache = 'h0;
    s_axi_awprot  = 'h0;
    s_axi_awqos   = 'h0;
    s_axi_awvalid = 'h0;

    s_axi_wdata   = 'h0;
    s_axi_wstrb   = 'hffff;
    s_axi_wlast   = 'h0;
    s_axi_wvalid  = 'h0;

    s_axi_bready  = 'h1;

    s_axi_arid    = 'h0;
    s_axi_araddr  = 'h0;
    s_axi_arlen   = 'h0;
    s_axi_arsize  = 'h4;
    s_axi_arburst = 'h1;
    s_axi_arlock  = 'h0;
    s_axi_arcache = 'h0;
    s_axi_arprot  = 'h0;
    s_axi_arqos   = 'h0;
    s_axi_arvalid = 'h0;

    s_axi_rready  = 'h1;

    #(`clk_period*5000);
    while(~init_calib_complete) begin
        @(posedge ui_clk);
    end

    #(`clk_period*100);    
    write_task;
    
    #(`clk_period*100);    
    read_task;

end

initial begin
    rd_data_cnt = 'hdddd_0000_cccc_0000_bbbb_0000_aaaa_0000;
    rd_data_err = 'b0;
    
    #(`clk_period*2000);
    
    while(1) begin
        @(posedge ui_clk);
        rd_data_err = s_axi_rvalid && s_axi_rdata != rd_data_cnt;
        
        if(s_axi_rvalid) begin
            rd_data_cnt = {32'hdddd_0000 + rd_data_cnt[15:0] + 1, 32'hcccc_0000 + rd_data_cnt[15:0] + 1,32'hbbbb_0000 + rd_data_cnt[15:0] + 1,32'haaaa_0000 + rd_data_cnt[15:0] + 1};
        end
        else begin
            ;
        end
    end
end

task write_task;
    integer i,j;
    
    s_axi_awid    = 'h0;
    s_axi_awaddr  = 'h0;
    s_axi_awlen   = 'h0;
    s_axi_awvalid = 'h0;
    
    s_axi_wdata   = 'hdddd_0000_cccc_0000_bbbb_0000_aaaa_0000;
    s_axi_wstrb   = 'hffff;
    s_axi_wlast   = 'h0;
    s_axi_wvalid  = 'h0;

    @(posedge ui_clk);
    for(i = 0 ; i < 100 ; i = i + 1) begin
        s_axi_awvalid = 1'b1;
        s_axi_awlen   = 'd15;
        @(posedge ui_clk);
        while(~s_axi_awready) @(posedge ui_clk);
        s_axi_awvalid = 1'b0;
        s_axi_awaddr  = s_axi_awaddr + (s_axi_awlen+1)*16;// addr_unit: Byte
        s_axi_awid    = s_axi_awid + 1;
        
        s_axi_wvalid = 1'b1;
        for(j = 0; j < 16; j = j + 1) begin
            s_axi_wlast  = j == 15;
            @(posedge ui_clk);
            while(~s_axi_wready) @(posedge ui_clk);
            s_axi_wdata  = {32'hdddd_0000 + s_axi_wdata[15:0] + 1, 32'hcccc_0000 + s_axi_wdata[15:0] + 1,32'hbbbb_0000 + s_axi_wdata[15:0] + 1,32'haaaa_0000 + s_axi_wdata[15:0] + 1};
        end
        s_axi_wvalid = 1'b0;
        s_axi_wlast  = 1'b0;
    end

endtask

task read_task;
    integer i;
    
    s_axi_arid    = 'h0;
    s_axi_araddr  = 'h8;
    s_axi_arlen   = 'h0;
    s_axi_arvalid = 'h0;
    s_axi_arsize  = 'h4;

    @(posedge ui_clk);    
    for(i = 0 ; i < 100 ; i = i + 1) begin
        s_axi_arvalid = 1'b1;
        s_axi_arlen   = 'd15;
        @(posedge ui_clk);
        while(~s_axi_arready) @(posedge ui_clk);
        s_axi_araddr = s_axi_araddr + (s_axi_arlen+1)*16;// addr_unit: Byte
        s_axi_arid   = s_axi_arid + 1;
    end
    s_axi_arvalid = 1'b0;
    
    #(`clk_period*400);    // UnAligned Transfer Test
    @(posedge ui_clk);
    s_axi_arsize  = 'h3;
    s_axi_araddr  = 'h4;
    s_axi_arid    = s_axi_arid + 1;
    s_axi_arvalid = 1'b1;
    s_axi_arlen   = 'd15;
    @(posedge ui_clk);
    while(~s_axi_arready) @(posedge ui_clk);
    s_axi_arvalid = 1'b0;
    
    #(`clk_period*200);    // UnAligned Transfer Test
    @(posedge ui_clk);
    s_axi_arsize  = 'h2;
    s_axi_araddr  = 'h4;
    s_axi_arid    = s_axi_arid + 1;
    s_axi_arvalid = 1'b1;
    s_axi_arlen   = 'd15;
    @(posedge ui_clk);
    while(~s_axi_arready) @(posedge ui_clk);
    s_axi_arvalid = 1'b0;
    
    #(`clk_period*200);    // UnAligned Transfer Test
    @(posedge ui_clk);
    s_axi_arsize  = 'h1;
    s_axi_araddr  = 'h4;
    s_axi_arid    = s_axi_arid + 1;
    s_axi_arvalid = 1'b1;
    s_axi_arlen   = 'd15;
    @(posedge ui_clk);
    while(~s_axi_arready) @(posedge ui_clk);
    s_axi_arvalid = 1'b0;
    
    #(`clk_period*200);    // UnAligned Transfer Test
    @(posedge ui_clk);
    s_axi_arsize  = 'h0;
    s_axi_araddr  = 'h4;
    s_axi_arid    = s_axi_arid + 1;
    s_axi_arvalid = 1'b1;
    s_axi_arlen   = 'd15;
    @(posedge ui_clk);
    while(~s_axi_arready) @(posedge ui_clk);
    s_axi_arvalid = 1'b0;

endtask


mig_ip_core u_mig_ip_core(
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr),
    .ddr3_ba                        (ddr3_ba),
    .ddr3_cas_n                     (ddr3_cas_n),
    .ddr3_ck_n                      (ddr3_ck_n),
    .ddr3_ck_p                      (ddr3_ck_p),
    .ddr3_cke                       (ddr3_cke),
    .ddr3_ras_n                     (ddr3_ras_n),
    .ddr3_reset_n                   (ddr3_reset_n),
    .ddr3_we_n                      (ddr3_we_n),
    .ddr3_dq                        (ddr3_dq),
    .ddr3_dqs_n                     (ddr3_dqs_n),
    .ddr3_dqs_p                     (ddr3_dqs_p),
    .ddr3_cs_n                      (ddr3_cs_n),
    .ddr3_dm                        (ddr3_dm),
    .ddr3_odt                       (ddr3_odt),

    // Application interface ports
    .ui_clk                         (ui_clk),
    .ui_clk_sync_rst                (ui_clk_sync_rst),
    .mmcm_locked                    (mmcm_locked),
    .aresetn                        (aresetn),
    .app_sr_req                     (app_sr_req),
    .app_ref_req                    (app_ref_req),
    .app_zq_req                     (app_zq_req),
    .app_sr_active                  (app_sr_active),
    .app_ref_ack                    (app_ref_ack),
    .app_zq_ack                     (app_zq_ack),

    // Slave Interface Write Address Ports
    .s_axi_awid                     (s_axi_awid),
    .s_axi_awaddr                   (s_axi_awaddr),
    .s_axi_awlen                    (s_axi_awlen),
    .s_axi_awsize                   (s_axi_awsize),
    .s_axi_awburst                  (s_axi_awburst),
    .s_axi_awlock                   (s_axi_awlock),
    .s_axi_awcache                  (s_axi_awcache),
    .s_axi_awprot                   (s_axi_awprot),
    .s_axi_awqos                    (s_axi_awqos),
    .s_axi_awvalid                  (s_axi_awvalid),
    .s_axi_awready                  (s_axi_awready),

    // Slave Interface Write Data Ports
    .s_axi_wdata                    (s_axi_wdata),
    .s_axi_wstrb                    (s_axi_wstrb),
    .s_axi_wlast                    (s_axi_wlast),
    .s_axi_wvalid                   (s_axi_wvalid),
    .s_axi_wready                   (s_axi_wready),

    // Slave Interface Write Response Ports
    .s_axi_bid                      (s_axi_bid),
    .s_axi_bresp                    (s_axi_bresp),
    .s_axi_bvalid                   (s_axi_bvalid),
    .s_axi_bready                   (s_axi_bready),

    // Slave Interface Read Address Ports
    .s_axi_arid                     (s_axi_arid),
    .s_axi_araddr                   (s_axi_araddr),
    .s_axi_arlen                    (s_axi_arlen),
    .s_axi_arsize                   (s_axi_arsize),
    .s_axi_arburst                  (s_axi_arburst),
    .s_axi_arlock                   (s_axi_arlock),
    .s_axi_arcache                  (s_axi_arcache),
    .s_axi_arprot                   (s_axi_arprot),
    .s_axi_arqos                    (s_axi_arqos),
    .s_axi_arvalid                  (s_axi_arvalid),
    .s_axi_arready                  (s_axi_arready),

    // Slave Interface Read Data Ports
    .s_axi_rid                      (s_axi_rid),
    .s_axi_rdata                    (s_axi_rdata),
    .s_axi_rresp                    (s_axi_rresp),
    .s_axi_rlast                    (s_axi_rlast),
    .s_axi_rvalid                   (s_axi_rvalid),
    .s_axi_rready                   (s_axi_rready),

    // System Clock Ports
    .sys_clk_p                      (sys_clk_p),
    .sys_clk_n                      (sys_clk_n),

    // Reference Clock Ports
    .clk_ref_p                      (clk_ref_p),
    .clk_ref_n                      (clk_ref_n),

    .init_calib_complete            (init_calib_complete),
    .device_temp                    (device_temp),
    .sys_rst                        (sys_rst)
);

ddr3_model u_ddr3_model(
    .rst_n                 (ddr3_reset_n        ),
    .ck                    (ddr3_ck_p           ),
    .ck_n                  (ddr3_ck_n           ),
    .cke                   (ddr3_cke            ),
    .cs_n                  (ddr3_cs_n           ),
    .ras_n                 (ddr3_ras_n          ),
    .cas_n                 (ddr3_cas_n          ),
    .we_n                  (ddr3_we_n           ),
    .dm_tdqs               (ddr3_dm             ),
    .ba                    (ddr3_ba             ),
    .addr                  (ddr3_addr           ),
    .dq                    (ddr3_dq             ),
    .dqs                   (ddr3_dqs_p          ),
    .dqs_n                 (ddr3_dqs_n          ),
    .tdqs_n                (                    ),
    .odt                   (ddr3_odt            )
);

endmodule
View Code

仿真结果如下:(init_calib_complete信号大概在84us左右置1)

image

由仿真结果可知,在更改axi_arsize前的rd_data_err信号一直为0,说明仿真写入与读出数据一致。

  注意:

  1、MIG配置中选择的DDR型号是MT41J256m16XX-125,数据位宽选择为16,所以仿真Testbench只例化了一个ddr3_module,若数据位宽选择为16的倍数,则需要例化对应倍数的ddr3_module,ddr3_module在生成IP目录下的example_design/sim下能找到。

4 参考链接

Zynq-7000 All Programmable SoC and 7 Series Devices Memory Interface Solutions v4.1, User Guide (UG586)

AMBA AXI and ACE Protocol Specification Version E

posted @ 2025-11-18 20:11  KD_one  阅读(18)  评论(0)    收藏  举报