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

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。


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来屏蔽非有效写入数据。

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
仿真结果如下:(init_calib_complete信号大概在84us左右置1)

由仿真结果可知,在更改axi_arsize前的rd_data_err信号一直为0,说明仿真写入与读出数据一致。
注意:
1、MIG配置中选择的DDR型号是MT41J256m16XX-125,数据位宽选择为16,所以仿真Testbench只例化了一个ddr3_module,若数据位宽选择为16的倍数,则需要例化对应倍数的ddr3_module,ddr3_module在生成IP目录下的example_design/sim下能找到。

浙公网安备 33010602011771号