上篇blog中记录了DDR3 AXI4接口的IP配置详情,这一文章则是记录自己在项目工程以及学习中对于DDR3的读写测试。先讲一下大概的工程架构:产生16位的自加数写进写FIFO中,当FIFO中的数达到一次突发长度后将其全部读出写进DDR3中,再检测到DDR3中数达到1024之后全部读出写入到读FIFO中,最后在顶层的读使能信号作用下将读FIFO的数全部读出,查看写入的自加数与读出的数是否符一直,符合则实验成功。

可能有的读者最开始会疑问为什么会用到两个异步FIFO,这个自己在最开始学的时候也在想不用行不行,你不用FIFO直接写入数据再读出肯定也是可以的,但是考虑到实际项目需求以及IP核封装出的用户接口,用两个FIFO既可以解决数据位宽不同步以及突发传输的问题,并且复用性更强。比如我自己的一个项目就是数据采集相关,ADC是16位,转换出的数据存入DDR3之前先写进fifo,再读出128位的数据匹配ip 核的读写数据端口。读fifo也是同理。

  1. 工程结构

axi_test_ddr_top:顶层模块,主要为产生自加数、列化子模块、时钟生成和生成vio复位调试。

ax_ddr_top:封装好的DDR使用模块,可通过修改顶层参数实现突发长度、数据位宽、地址长度等参数的修改。

axi_fifo_ctrl:将读写fifo封装进子模块,引出fifo所需要相关信号。

axi_wr:axi4接口的写模块,实现AXI4相关写时序并且将数据写进DDR3。

axi_rd:axi4接口的读模块,实现AXI4相关读时序并且将数据从DDR3读出。

mig_7series_0:例化的ip核模板。

DDR型号为MT41K256M16XX-125,单片容量4Gb(512MB)。

  1. 具体代码分析

  • axi_test_ddr_top:

首先我测试用的板卡自带的是200M的差分晶振时钟,为了方便测试,直接用锁相环生成clk_adc、clk_pci与clk_200m。clk_adc是产生自加数的时钟,也是写fifo的写时钟。clk_pci是读fifo的读时钟,读fifo的读使能采用的是当读fifo内可读个数达到900时拉高读使能信号将数据读出。

vio模块是为了产生复位信号(FPGA_RST_N)方便抓取信号时调试。

`timescale 1ns / 1ps
module axi_test_ddr_top
(input               clk_200m_p,         //50M  input               clk_200m_n,         //50M
//  input                FPGA_RST_N,//ddr3相关 inout    [15:0]     ddr3_dq       ,inout    [1:0]      ddr3_dqs_n    ,inout    [1:0]      ddr3_dqs_p    ,output   [14:0]   ddr3_addr       ,       //hang地址线output    [2:0]   ddr3_ba         ,       //bank线output           ddr3_cas_n      ,       // 列使能信号,低电平有效                                                                                                            output           ddr3_ck_n       ,      //差分时钟output           ddr3_ck_p       ,      //差分时钟output           ddr3_cke        ,       //ddr3时钟使能信号output           ddr3_ras_n      ,       // 行使能信号,低电平有效output           ddr3_reset_n    ,       //复位output           ddr3_we_n       ,       //写使能output           ddr3_cs_n            ,  //片选output   [1:0]   ddr3_dm              ,  //掩码output           ddr3_odt            );    reg        [15:0]    ai_fifo_data_i                ;
reg                ai_fifo_wr                    ;
wire            ai_fifo_full                ;
reg                ai_pcie_fifo_rd              ;
wire    [15:0]    ai_pcie_fifo_data            ;
wire    [12:0]    ai_pcie_fifo_rd_data_count    ;
wire            init_calib_complete          ;
wire               locked ;
wire            clk_adc,clk_pci;wire            clk_200m  ; vio_0    u_vio_0 (.clk(clk_200m),                // input wire clk.probe_out0(FPGA_RST_N)  // output wire [0 : 0] probe_out0
);      clk_wiz_0   clk_wiz_0_inst(// Clock out ports.c0(clk_200m),     // output c0 200.c1(clk_adc),     // output clk_out3.c2(clk_pci),     // output clk_out4// Status and control signals.locked(locked),       // output locked// Clock in ports.clk_in1_p(clk_200m_p),    // input clk_in1_p.clk_in1_n(clk_200m_n));    // input clk_in1_n   always@ (posedge     clk_pci)beginif (!FPGA_RST_N) beginai_pcie_fifo_rd <= 1'b0;end else if(ai_pcie_fifo_rd_data_count >=13'd900) beginai_pcie_fifo_rd <= 1'b1;end  else beginai_pcie_fifo_rd <= 1'b0;end
end always@ (posedge     clk_adc)beginif (!FPGA_RST_N) beginai_fifo_data_i <= 64'd0;ai_fifo_wr        <= 1'd0;end else beginif(init_calib_complete & !ai_fifo_full) beginai_fifo_data_i <= ai_fifo_data_i +1'b1;ai_fifo_wr     <= 1'b1;end else beginai_fifo_data_i <= ai_fifo_data_i;ai_fifo_wr     <= 1'b0;endend
end      axi_ddr_top
#(   . DDR_WR_LEN    ( 10'd128 ),. DDR_RD_LEN    ( 10'd128 ),. DATA_WIDTH    ( 128        ),. DDR_COUNT     ( 1024        ),. DDR_ADDR_LIMIT (32'h0fff_fff0),. DDR_CNT_LIMIT  (32'h8000_0000 )
)
axi_ddr_top_inst
(. adc_clk_i                     (clk_adc   ),    . ddr3_clk_i                    (clk_200m),    . pci_clk_i                     (clk_pci),. rst_n_i                      (FPGA_RST_N),   .ai_fifo_data_i                (ai_fifo_data_i                ),.ai_fifo_wr                    (ai_fifo_wr                    ),        .ai_fifo_full                (ai_fifo_full                ),  .ai_pcie_fifo_rd              (ai_pcie_fifo_rd              ),.ai_pcie_fifo_data            (ai_pcie_fifo_data            ),.ai_pcie_fifo_rd_data_count    (ai_pcie_fifo_rd_data_count    ),    .init_calib_complete          (init_calib_complete          ),  // Output Ports - Single Bit.ddr3_cas_n      (ddr3_cas_n),   .ddr3_ck_n       (ddr3_ck_n),    .ddr3_ck_p       (ddr3_ck_p),    .ddr3_cke        (ddr3_cke),     .ddr3_cs_n       (ddr3_cs_n),    .ddr3_odt        (ddr3_odt),     .ddr3_ras_n      (ddr3_ras_n),   .ddr3_reset_n    (ddr3_reset_n), .ddr3_we_n       (ddr3_we_n),    // Output Ports - Busses.ddr3_addr   (ddr3_addr ),.ddr3_ba     (ddr3_ba), .ddr3_dm     (ddr3_dm), // InOut Ports - Single Bit// InOut Ports - Busses.ddr3_dq     (ddr3_dq     ),.ddr3_dqs_n  (ddr3_dqs_n ),.ddr3_dqs_p  (ddr3_dqs_p )
);
endmodule
  • axi_ddr_top:

此模块主要例化三个子模块,包括fifo控制模块、axi读与写模块以及ip核例化模板。

为了方便调试时控制IP核的工作,例化一个vio控制IP核的复位,方便在抓取信号时,先对IP核复位再产生数据写入fifo中。

总体顶层信号需要自己产生的就是自加数、写fifo的写使能与读fifo的读使能信号,其余相关联信号在fifo控制模块中引出。具体的可查看代码。

`timescale 1ns / 1ps
module axi_ddr_top #
(parameter   DDR_WR_LEN    =   8'd128,   //写突发长度 最大128个128bitparameter   DDR_RD_LEN    =   8'd128,   //写突发长度 最大128个128bit    parameter   DATA_WIDTH    =   128,      //读写数据位宽parameter   DDR_COUNT     =   1024,         //DDR内部存在数据个数parameter   DDR_ADDR_LIMIT =  32'h0fff_ffff ,  //2^10*2^15*2^3=2^28=268,435,456=32'h0fff_ffffparameter   DDR_CNT_LIMIT  =  32'h0fff_ffff  // 4*1024^3bit / 16bit = 268,435,456 = 32'h0fff_ffff
)
(//50m的时钟与复位信号input       ddr3_clk_i,      //200m   input       adc_clk_i,         //64Minput       pci_clk_i,        //   input        rst_n_i         , //外部复位inout  [15:0] ddr3_dq        , //数据线inout  [1:0]  ddr3_dqs_n     , //数据选取脉冲差分信号inout  [1:0]  ddr3_dqs_p     , //数据选取脉冲差分信号output [14:0] ddr3_addr      , //地址线output [2:0]  ddr3_ba        , //bank线output        ddr3_ras_n     , //行使能信号,低电平有效output        ddr3_cas_n     , //列使能信号,低电平有效output        ddr3_we_n      , //写使能信号,低电平有效output        ddr3_reset_n   , //ddr3复位output [0:0]  ddr3_ck_p      , //ddr3差分时钟output [0:0]  ddr3_ck_n      , //ddr3差分时钟output [0:0]  ddr3_cke       , //ddr3时钟使能信号output [0:0]  ddr3_cs_n      , //ddr3片选信号output [1:0]  ddr3_dm        , //ddr3掩码output [0:0]  ddr3_odt       , //odt阻抗input  [15:0]  ai_fifo_data_i,input          ai_fifo_wr     ,        output         ai_fifo_full  ,                input           ai_pcie_fifo_rd  ,output  [15:0]  ai_pcie_fifo_data,output  [12:0]    ai_pcie_fifo_rd_data_count,output          init_calib_complete  );
//wire  define//axi写通道写地址
wire [3:0] M_AXI_WR_awid;   //写地址ID,用来标志一组写信号
wire [28:0]M_AXI_WR_awaddr; //写地址,给出一次写突发传输的写地址
wire [7:0] M_AXI_WR_awlen;  //突发长度,给出突发传输的次数
wire [2:0] M_AXI_WR_awsize; //突发大小,给出每次突发传输的字节数
wire [1:0] M_AXI_WR_awburst;//突发类型
wire [0:0] M_AXI_WR_awlock; //总线锁信号,可提供操作的原子性
wire [3:0] M_AXI_WR_awcache;//内存类型,表明一次传输是怎样通过系统的
wire [2:0] M_AXI_WR_awprot; //保护类型,表明一次传输的特权级及安全等级
wire [3:0] M_AXI_WR_awqos;  //质量服务QoS
wire       M_AXI_WR_awvalid;//有效信号,表明此通道的地址控制信号有效
wire       M_AXI_WR_awready;//表明“从”可以接收地址和对应的控制信号
//axi写通道读数据
wire [DATA_WIDTH-1 :0]M_AXI_WR_wdata;  //写数据
wire [DATA_WIDTH/8-1 :0] M_AXI_WR_wstrb;  //写数据有效的字节线//用来表明哪8bits数据是有效的
wire                      M_AXI_WR_wlast;  //表明此次传输是最后一个突发传输
wire                      M_AXI_WR_wvalid; //写有效,表明此次写有效
wire                      M_AXI_WR_wready; //表明从机可以接收写数据
//axi写通道读应答
wire [3:0] M_AXI_WR_bid        ;    //写响应ID TAG
wire [1:0] M_AXI_WR_bresp    ;  //写响应,表明写传输的状态
wire       M_AXI_WR_bvalid    ; //写响应有效
wire       M_AXI_WR_bready    ; //表明主机能够接收写响应//axi读通道写地址
wire [3:0] M_AXI_RD_arid;   //读地址ID,用来标志一组写信号
wire [28:0]M_AXI_RD_araddr; //读地址,给出一次写突发传输的读地址
wire [7:0] M_AXI_RD_arlen;  //突发长度,给出突发传输的次数
wire [2:0] M_AXI_RD_arsize; //突发大小,给出每次突发传输的字节数
wire [1:0] M_AXI_RD_arburst;//突发类型
wire [0:0] M_AXI_RD_arlock; //总线锁信号,可提供操作的原子性
wire [3:0] M_AXI_RD_arcache;//内存类型,表明一次传输是怎样通过系统的
wire [2:0] M_AXI_RD_arprot; //保护类型,表明一次传输的特权级及安全等级
wire [3:0] M_AXI_RD_arqos;  //质量服务QOS
wire       M_AXI_RD_arvalid;//有效信号,表明此通道的地址控制信号有效
wire       M_AXI_RD_arready;//表明“从”可以接收地址和对应的控制信号
//axi读通道读数据
wire [3:0]                 M_AXI_RD_rid;    //读ID tag
wire [DATA_WIDTH-1:0]    M_AXI_RD_rdata;    //读数据
wire [1:0]                 M_AXI_RD_rresp;  //读响应,表明读传输的状态
wire                       M_AXI_RD_rlast;  //表明读突发的最后一次传输
wire                       M_AXI_RD_rvalid; //表明此通道信号有效
wire                       M_AXI_RD_rready; //表明主机能够接收读数据和响应信息//
wire      ui_clk      ; //800 /4 = 200m
wire      ui_rst      ;
wire    ddr3_rst_n;wire         locked ;
wire         ai_fifo_rd       ;
wire         ai_fifo_prog_full;  wire [127:0]wr_fifo_data;
wire [127:0]rd_fifo_data;
wire  [31:0]  ddr_count     ;
wire         ddr_prog_full ;
wire         ddr_cnt_full  ;
wire   [12:0]rd_data_count;
wire         ai_pcie_fifo_prom_full;
wire    [9:0] wr_fifo_rd_data_count;
/* ila_1  u_ila_1 (.clk(ddr3_clk_i), // input wire clk.probe0({ M_AXI_WR_awid,          M_AXI_WR_wdata ,  M_AXI_WR_bid    ,  M_AXI_RD_arid    , M_AXI_RD_rid     , M_AXI_WR_awaddr    ,   M_AXI_WR_wstrb ,  M_AXI_WR_bresp,  M_AXI_RD_araddr    , M_AXI_RD_rdata ,M_AXI_WR_awlen    ,                     M_AXI_WR_bvalid, M_AXI_RD_arlen    , M_AXI_RD_rresp ,M_AXI_WR_awsize    ,   M_AXI_WR_wlast ,  M_AXI_WR_bready, M_AXI_RD_arsize    , M_AXI_RD_rlast ,M_AXI_WR_awburst  ,   M_AXI_WR_wvalid,                   M_AXI_RD_arburst , M_AXI_RD_rvalid,M_AXI_WR_awlock    ,   M_AXI_WR_wready,                   M_AXI_RD_arlock    , M_AXI_RD_rready,M_AXI_WR_awcache  ,   ai_fifo_data_i,                    M_AXI_RD_arcache ,M_AXI_WR_awprot    ,   ai_fifo_wr      ,                    M_AXI_RD_arprot    ,M_AXI_WR_awqos    ,   ai_fifo_full  ,                    M_AXI_RD_arqos    ,M_AXI_WR_awvalid  ,   init_calib_complete,               M_AXI_RD_arvalid ,M_AXI_WR_awready  ,                                           M_AXI_RD_arready }) // input wire [511:0] probe0
);  vio_1   u_vio_1 (.clk(ddr3_clk_i),                // input wire clk.probe_out0(ddr3_rst_n)  // output wire [0 : 0] probe_out0
);axi_fifo_ctrl#
(.DDR_COUNT     (DDR_COUNT    ),//DDR内部存在数据个数.DDR_CNT_LIMIT (DDR_CNT_LIMIT)  // 1024^3 *8 bit / 16bit = 536870912 = 32'h2000_0000
)(. ddr3_clk_i                  (ddr3_clk_i ) ,        //200m   . adc_clk_i                   (adc_clk_i  )  ,         //64M. pci_clk_i                   (pci_clk_i  )  ,        //   .  rst_n_i                    ( rst_n_i   )  , //外部复位. ai_fifo_data_i              (ai_fifo_data_i )  ,. ai_fifo_rd                  (ai_fifo_rd     )  ,. ai_fifo_wr                  (ai_fifo_wr     )   ,        . ai_fifo_full                (ai_fifo_full   )  , . wr_fifo_data                (wr_fifo_data   )  ,. M_AXI_RD_rvalid             (M_AXI_RD_rvalid            )  ,. rd_fifo_data                (rd_fifo_data               )  ,. rd_fifo_we                  (rd_fifo_we                 )  ,. ai_pcie_fifo_rd             (ai_pcie_fifo_rd            )  ,. ai_pcie_fifo_data           (ai_pcie_fifo_data          )  ,. ai_pcie_fifo_rd_data_count  (ai_pcie_fifo_rd_data_count )  ,. ai_pcie_fifo_prom_full      (ai_pcie_fifo_prom_full     )  ,. wr_fifo_rd_data_count       (wr_fifo_rd_data_count      )    ,. ddr_count                   (ddr_count                  )  ,. ddr_prog_full               (ddr_prog_full              )  ,        . ddr_cnt_full                (ddr_cnt_full               )    );axi_wr
#(.DATA_WIDTH(DATA_WIDTH),.DDR_ADDR_LIMIT(DDR_ADDR_LIMIT)
)
u_axi_wr
(    .ARESETN      (rst_n_i         ), //axi复位.ACLK         (ui_clk          ), //axi总时钟.M_AXI_AWID   (M_AXI_WR_awid   ), //写地址ID.M_AXI_AWADDR (M_AXI_WR_awaddr ), //写地址.M_AXI_AWLEN  (M_AXI_WR_awlen  ), //突发长度 .M_AXI_AWSIZE (M_AXI_WR_awsize ), //突发大小  .M_AXI_AWBURST(M_AXI_WR_awburst), //突发类型  .M_AXI_AWLOCK (M_AXI_WR_awlock ), //总线锁信号 .M_AXI_AWCACHE(M_AXI_WR_awcache), //内存类型.M_AXI_AWPROT (M_AXI_WR_awprot ), //保护类型.M_AXI_AWQOS  (M_AXI_WR_awqos  ), //质量服务QoS     .M_AXI_AWVALID(M_AXI_WR_awvalid), //有效信号.M_AXI_AWREADY(M_AXI_WR_awready), //握手信号awready.M_AXI_WDATA (M_AXI_WR_wdata   ), //写数据.M_AXI_WSTRB (M_AXI_WR_wstrb   ), //写数据有效的字节线.M_AXI_WLAST (M_AXI_WR_wlast   ), //表明此次传输是最后一个突发传输.M_AXI_WVALID(M_AXI_WR_wvalid  ), //写有效.M_AXI_WREADY(M_AXI_WR_wready  ), //表明从机可以接收写数据                         .M_AXI_BID   (M_AXI_WR_bid     ), //写响应ID TAG.M_AXI_BRESP (M_AXI_WR_bresp   ), //写响应.M_AXI_BVALID(M_AXI_WR_bvalid  ), //写响应有效.M_AXI_BREADY(M_AXI_WR_bready  ), //表明主机能够接收写响应  .wr_len           (DDR_WR_LEN ),.wr_fifo_rd_data_count( wr_fifo_rd_data_count),.wr_fifo_data     (wr_fifo_data    ),
//  .ai_fifo_wr        (wr_fifo_re      ),.ai_fifo_prog_full(ai_fifo_prog_full),.ddr_cnt_full     (ddr_cnt_full    ),.ai_fifo_rd        (ai_fifo_rd      ),  .wr_done           (  )
);
axi_rd
#(.DATA_WIDTH(DATA_WIDTH),.DDR_ADDR_LIMIT(DDR_ADDR_LIMIT)
)
u_axi_rd
(. ARESETN      (rst_n_i),. ACLK         (ui_clk),. M_AXI_ARID   (M_AXI_RD_arid   ), //读地址ID. M_AXI_ARADDR (M_AXI_RD_araddr ), //读地址. M_AXI_ARLEN  (M_AXI_RD_arlen  ), //突发长度. M_AXI_ARSIZE (M_AXI_RD_arsize ), //突发大小. M_AXI_ARBURST(M_AXI_RD_arburst), //突发类型. M_AXI_ARLOCK (M_AXI_RD_arlock ), //总线锁信号. M_AXI_ARCACHE(M_AXI_RD_arcache), //内存类型. M_AXI_ARPROT (M_AXI_RD_arprot ), //保护类型. M_AXI_ARQOS  (M_AXI_RD_arqos  ), //质量服务QOS. M_AXI_ARVALID(M_AXI_RD_arvalid), //有效信号. M_AXI_ARREADY(M_AXI_RD_arready), //握手信号arready. M_AXI_RID   (M_AXI_RD_rid   ), //读ID tag. M_AXI_RDATA (M_AXI_RD_rdata ), //读数据. M_AXI_RRESP (M_AXI_RD_rresp ), //读响应,表明读传输的状态. M_AXI_RLAST (M_AXI_RD_rlast ), //表明读突发的最后一次传输. M_AXI_RVALID(M_AXI_RD_rvalid), //表明此通道信号有效. M_AXI_RREADY(M_AXI_RD_rready), //表明主机能够接收读数据和响应信息//  . rd_start     (         ), //读突发触发信号. rd_len       (DDR_RD_LEN), //长度
//  . rd_addr      (         ), //地址. rd_fifo_we     (rd_fifo_we     ), //连接到读fifo的写使能  O. rd_fifo_data (rd_fifo_data   ), //连接到读fifo的写数据. rd_done      ( ),  //完成一次突发. ddr_prog_full(ddr_prog_full  ),. ddr_count(ddr_count),. ai_pcie_fifo_prom_full(ai_pcie_fifo_prom_full)
);
mig_7series_0    U_mig_7series_0
(.ddr3_dq   (ddr3_dq   ),     //数据线.ddr3_dqs_n(ddr3_dqs_n),     //数据选取脉冲差分信号.ddr3_dqs_p(ddr3_dqs_p),     //数据选取脉冲差分信号.ddr3_addr (ddr3_addr ),     //地址线.ddr3_ba   (ddr3_ba   ),     //bank线.ddr3_ras_n(ddr3_ras_n),     //行使能信号,低电平有效.ddr3_cas_n(ddr3_cas_n),     //列使能信号,低电平有效.ddr3_we_n (ddr3_we_n ),     //写使能信号,低电平有效.ddr3_reset_n(ddr3_reset_n), //ddr3复位.ddr3_ck_p (ddr3_ck_p ),     //ddr3差分时钟.ddr3_ck_n (ddr3_ck_n ),     //ddr3差分时钟.ddr3_cke  (ddr3_cke  ),     //ddr3时钟使能信号.ddr3_cs_n (ddr3_cs_n ),     //ddr3片选信号.ddr3_dm   (ddr3_dm   ),     //ddr3掩码.ddr3_odt  (ddr3_odt  ),     //odt阻抗.sys_clk_i        (ddr3_clk_i),        //ip核时钟200M.sys_rst            (ddr3_rst_n),       //ip核复位.ui_clk           (ui_clk),          //用户端口时钟200M.ui_clk_sync_rst (ui_rst),         //复位.mmcm_locked        (    ),.aresetn            (ddr3_rst_n),    //异步复位.app_sr_req         ('b0),.app_ref_req        ('b0),.app_zq_req         ('b0),.app_sr_active    (),.app_ref_ack        (),.app_zq_ack        (),//axi写通道地址与控制信号.s_axi_awid    (M_AXI_WR_awid   ), //写地址ID .s_axi_awaddr  (M_AXI_WR_awaddr ), //写地址.s_axi_awlen   (M_AXI_WR_awlen  ), //突发长度.s_axi_awsize  (M_AXI_WR_awsize ), //突发大小.s_axi_awburst (M_AXI_WR_awburst), //突发类型.s_axi_awlock  (M_AXI_WR_awlock ), //总线锁信号.s_axi_awcache (M_AXI_WR_awcache), //内存类型.s_axi_awprot  (M_AXI_WR_awprot ), //保护类型.s_axi_awqos   (M_AXI_WR_awqos  ), //质量服务QoS.s_axi_awvalid (M_AXI_WR_awvalid), //有效信号.s_axi_awready (M_AXI_WR_awready), //握手信号awready//axi写通道数据.s_axi_wdata   (M_AXI_WR_wdata  ), //写数据.s_axi_wstrb   (M_AXI_WR_wstrb  ), //写数据有效的字节线.s_axi_wlast   (M_AXI_WR_wlast  ), //表明此次传输是最后一个突发传输.s_axi_wvalid  (M_AXI_WR_wvalid ), //写有效,表明此次写有效.s_axi_wready  (M_AXI_WR_wready ), //表明从机可以接收写数据//axi写通道应答                    .s_axi_bid     (M_AXI_WR_bid    ), //写响应ID TAG.s_axi_bresp   (M_AXI_WR_bresp  ), //写响应,表明写传输的状态.s_axi_bvalid  (M_AXI_WR_bvalid ), //写响应有效.s_axi_bready  (M_AXI_WR_bready ), //表明主机能够接收写响应//axi读通道地址与控制信号          .s_axi_arid    (M_AXI_RD_arid   ), //读地址ID.s_axi_araddr  (M_AXI_RD_araddr ), //读地址.s_axi_arlen   (M_AXI_RD_arlen  ), //突发长度.s_axi_arsize  (M_AXI_RD_arsize ), //突发大小.s_axi_arburst (M_AXI_RD_arburst), //突发类型.s_axi_arlock  (M_AXI_RD_arlock ), //总线锁信号.s_axi_arcache (M_AXI_RD_arcache), //内存类型.s_axi_arprot  (M_AXI_RD_arprot ), //保护类型.s_axi_arqos   (M_AXI_RD_arqos  ), //质量服务QOS.s_axi_arvalid (M_AXI_RD_arvalid), //有效信号.s_axi_arready (M_AXI_RD_arready), //握手信号arready//axi读通道数据,包括应答          .s_axi_rid     (M_AXI_RD_rid    ), //读ID tag.s_axi_rdata   (M_AXI_RD_rdata  ), //读数据.s_axi_rresp   (M_AXI_RD_rresp  ), //读响应,表明读传输的状态.s_axi_rlast   (M_AXI_RD_rlast  ), //表明读突发的最后一次传输.s_axi_rvalid  (M_AXI_RD_rvalid ), //表明此通道信号有效.s_axi_rready  (M_AXI_RD_rready ), //表明主机能够接收读数据.init_calib_complete(init_calib_complete)  //ip核初始化完成
);
endmodule
  • axi_fifo_ctrl:

这里比较重要的是代码30-58行代码,这里需要引出三个信号:ddr_count、ddr_prog_full、ddr_cnt_full。分别为ddr内部存在的数据个数、超过1024的满标志信号、超出ddr内部最大极限个数的标志信号。

顺便提一下38、40行代码:在写fifo读使能有效之后ddr_count加8与DDR读出有效后减8的原因:前文提到的AXI4总线的位宽选择的是128 ,在每次axi握手成功后写入ddr内部的数据个数应该是(128/16=8),读出也是同理。

`timescale 1ns / 1ps
module axi_fifo_ctrl#
(   parameter   DATA_WIDTH    =   128,parameter   DDR_COUNT     =   1024,         //预设DDR存1024个数parameter   DDR_CNT_LIMIT  =  32'h0fff_ffff  // 4*1024^3bit / 16bit = 268,435,456 = 32'h0fff_ffff
)(input                    ddr3_clk_i,      //200m   input                    adc_clk_i,         //64Minput                    pci_clk_i,        //   input                     rst_n_i         , //外部复位input  [15:0]               ai_fifo_data_i,input                       ai_fifo_rd    ,input                       ai_fifo_wr     ,        output                      ai_fifo_full  , output [DATA_WIDTH-1 :0 ]   wr_fifo_data  ,input                       M_AXI_RD_rvalid,input  [DATA_WIDTH-1 :0 ]   rd_fifo_data,input                       rd_fifo_we,input                       ai_pcie_fifo_rd  ,output  [15:0]              ai_pcie_fifo_data,output  [12:0]              ai_pcie_fifo_rd_data_count,output                      ai_pcie_fifo_prom_full,output  [9:0]               wr_fifo_rd_data_count,output  reg   [31:0]        ddr_count , //DDR内部存在的数据个数output  reg                 ddr_prog_full ,output  reg                 ddr_cnt_full      );    always@ (posedge ddr3_clk_i or negedge rst_n_i)beginif (!rst_n_i) beginddr_count     <= 32'd0;ddr_prog_full <= 1'b0;ddr_cnt_full  <= 1'b0;end else beginif(ai_fifo_rd)begin                        //fifo开始读出数据到ddr3ddr_count <= ddr_count + 32'd8;end else if(M_AXI_RD_rvalid) begin         //DDR读出有效 && fifo非满 时ddr内的数据被读出ddr_count <= ddr_count - 32'd8;end else beginddr_count <= ddr_count;endif(ddr_count >=  DDR_COUNT)begin       // 超过1024满标志信号ddr_prog_full <= 1'b1;end else beginddr_prog_full <= 1'b0;endif(ddr_count > DDR_CNT_LIMIT)begin       // 超过 ddr_cnt_limit 满标志信号ddr_cnt_full <= 1'b1;end else beginddr_cnt_full <= 1'b0;endend
end   data_2_ddr3_16x64_8192   adc_2_ddr3_16x64_8192 (.rst    (!rst_n_i),                      // input wire rst.wr_clk(adc_clk_i),                // input wire wr_clk.rd_clk(ddr3_clk_i ),                // input wire rd_clk.din  (ai_fifo_data_i),                      // input wire [15 : 0] din.wr_en(ai_fifo_wr),                  // input wire wr_en.rd_en(ai_fifo_rd),                  // input wire rd_en.dout(wr_fifo_data),                    // output wire [127 : 0] dout   等于app_wdf_data.almost_full    (ai_fifo_full),  // output wire almost_full .full            ( ),                    // output wire full.empty(ai_fifo_empty),                  // output wire empty.rd_data_count(wr_fifo_rd_data_count),  // output wire [9 : 0] rd_data_count.prog_full(ai_fifo_prog_full)          // output wire prog_full
);ddr3_2_pci_64x32_2048    ddr3_2_pci_64x16_8192 (.rst   (!rst_n_i),                  // input wire rst.wr_clk(ddr3_clk_i),                // input wire wr_clk.rd_clk(pci_clk_i),                // input wire rd_clk.din   (rd_fifo_data),              // input wire [127 : 0] din.wr_en (rd_fifo_we),             // input wire wr_en.rd_en (ai_pcie_fifo_rd),          // input wire rd_en.dout  (ai_pcie_fifo_data),            // output wire [15: 0] dout.full  (),                        // output wire full.almost_full( ),                  // output wire almost_full.empty( ),                      // output wire empty.rd_data_count(ai_pcie_fifo_rd_data_count),  // output wire [12 : 0] rd_data_count.prog_full(ai_pcie_fifo_prom_full)  // output wire prog_full
);
endmodule
  • axi_wr:

此为axi4的写时序模块,主要实现axi4的写时序。 主要将IP核输出的axi4写通道地址通道、写通道数据通道与写通道应答通道对应输入输出。

其中写突发触发信号设计的是当写fifo内部可读数据大于一次突发长度进入写地址等待状态((wr_fifo_rd_data_count+10'd2)>=wr_len );这里我在开始测试时写条件是当写fifo将满且ddr内部没满时进入写地址等待状态,但是由于进入到写数据循环状态还有三个状态,在这期间写fifo还在写入数据,这样最后读出的数据就不对。所以改为((wr_fifo_rd_data_count+10'd2)>=wr_len;感兴趣的读者可以试一下把将满信号(ai_fifo_prog_full)设置的小一点,只要保证fifo没有溢出数据,再接收到axi握手成功信号(ai_fifo_rd = M_AXI_WREADY & reg_wvalid) 后才能将数据正确读出并通过AXI4总线写入到DDR中。

代码注释较为详尽,状态机部分主要还是根据AXI4的读写时序图设计,具体可参考下图:主要关注AWADDR信号、ARVALID+ARREADY这一组握手信号;在每一次突发传输之前,先判断ARREADY信号,当该信号为高时,代表从机已经准备好接收新的地址信息,否则主机就不能发送地址信息给从机。

发送写数据时候主机必须在WREADY为高的时候,将WVALID信号拉高,同时将数据发送给从机,当主机发送最后一个数据时,将WLAST信号拉高一个周期,告诉从机这是最后一个数据,当从机收到WLAST信号为高时,就会将收到的数据按照指定的地址写入DDR。

当主机发送完写数据后的一个时钟周期,IP核会输出BVALID信号表示写响应有效,逻辑部分需要输出BREADY信号表明能够接收写响应。在实际代码中我直接将M_AXI_BREADY = M_AXI_BVALID,此时只需观察 Bresp信号有没有拉高。

时序部分主要是状态机控制,控制ARVALID信号的输出以及声明的一些寄存器变量,具体可查看代码注释。

AXI4的相关时序有空再写一下,网站上也有许多优秀的博主写了blog都可以参考。

突然想到还有一个关键的点没有说道:在对写地址的确定中为什么是加16(即wr_addr_reg <= wr_addr_reg + 16);首先要明确的一个点是AXI4总线只需要一个地址,最高可以突发传输256个字的数据。 每一个地址对于一个字节的数据,128位的数据位宽对于16字节,所以一组数据写入完成之后的地址增量=突发长度x16。

`timescale 1ns / 1ps
module axi_wr
#(parameter   DATA_WIDTH =128,parameter   DDR_ADDR_LIMIT  =  32'h0fff_ffff  //2^10*2^15*2^3=2^28=268,435,456=32'h0fff_ffff
)
(input                       ARESETN    , //axi复位input                       ACLK       , //axi总时钟
//axi4写通道地址通道output [3:0]               M_AXI_AWID   , //写地址ID,用来标志一组写信号output [28:0]              M_AXI_AWADDR , //写地址,给出一次写突发传输的写地址output [7:0]               M_AXI_AWLEN  , //突发长度,给出突发传输的次数  output [2:0]               M_AXI_AWSIZE , //突发大小,给出每次突发传输的字节数output [1:0]               M_AXI_AWBURST, //突发类型  output                     M_AXI_AWLOCK , //总线锁信号,可提供操作的原子性  output [3:0]               M_AXI_AWCACHE, //内存类型,表明一次传输是怎样通过系统的  output [2:0]               M_AXI_AWPROT , //保护类型,表明一次传输的特权级及安全等级  output [3:0]               M_AXI_AWQOS  , //质量服务QoS     output                     M_AXI_AWVALID, //有效信号,表明此通道的地址控制信号有效input                      M_AXI_AWREADY, //表明“从”可以接收地址和对应的控制信号
//axi4写通道数据通道output [DATA_WIDTH-1 :0]     M_AXI_WDATA  , //写数据output [DATA_WIDTH/8-1:0]    M_AXI_WSTRB  , //写数据有效的字节线output                        M_AXI_WLAST  , //表明此次传输是最后一个突发传输output                        M_AXI_WVALID , //写有效,表明此次写有效input                         M_AXI_WREADY , //表明从机可以接收写数据
//axi4写通道应答通道input [3:0]                 M_AXI_BID    , //写响应ID TAGinput [1:0]                 M_AXI_BRESP  , //写响应,表明写传输的状态input                       M_AXI_BVALID , //写响应有效output                      M_AXI_BREADY , //表明主机能够接收写响应//用户端信号input [9:0]                wr_fifo_rd_data_count, //写突发触发信号input [7:0]                wr_len       ,   //写突发长度  input [DATA_WIDTH-1:0]      wr_fifo_data , //连接到写fifo的读数据  input                       ai_fifo_prog_full,  input                       ddr_cnt_full  ,  output                      ai_fifo_rd    //连接到写fifo的读使能
);localparam S_WR_IDLE  = 3'd0;//写空闲
localparam S_WA_WAIT  = 3'd1;//写地址等待
localparam S_WA_START = 3'd2;//写地址
localparam S_WD_WAIT  = 3'd3;//写数据等待
localparam S_WD_PROC  = 3'd4;//写数据循环
localparam S_WR_WAIT  = 3'd5;//接受写应答
localparam S_WR_DONE  = 3'd6;//写结束
localparam DATA_NUM =   (DATA_WIDTH == 512) ? 6 : (DATA_WIDTH == 256) ? 5 : (DATA_WIDTH == 128) ? 4 :(DATA_WIDTH == 64)  ? 3 : 2;
//reg define
reg [2:0]   wr_state   ; //状态寄存器
reg [28:0]  reg_wr_adrs; //写地址寄存器
reg         reg_awvalid; //地址有效握手信号
reg         reg_wvalid ; //数据有效握手信号
reg         reg_w_last ; //传输最后一个数据
reg [7:0]   reg_w_len  ; //突发长度128
reg [31:0]  wr_addr_cnt; //写地址计数器
reg [31:0]  wr_data_cnt; //写数据计数器
reg [31:0]  wr_addr_reg; //写地址寄存器  //写fifo的读使能为axi握手成功  ==  M_AXI_WREADY + M_AXI_WVALID
assign    ai_fifo_rd =   M_AXI_WREADY & reg_wvalid
//只有一个主机,可随意设置
assign M_AXI_AWID         = 4'b1111;
//把地址赋予总线
assign M_AXI_AWADDR = reg_wr_adrs ;
//一次突发传输1长度   (0  至 wr_len-1'd1)
assign M_AXI_AWLEN[7:0]   = wr_len-'d1;
//表示AXI总线每个数据宽度是16字节,128位。  2^(4)=16byte
assign M_AXI_AWSIZE = DATA_NUM;
//01代表地址递增,10代表递减
assign M_AXI_AWBURST[1:0] = 2'b01;   //突发类型递增,增量突发,后续数据的地址在初始地址的基础上进行递增,递增幅度与传输宽度相同。
assign M_AXI_AWLOCK       = 1'b0;
assign M_AXI_AWCACHE[3:0] = 4'b0010;
assign M_AXI_AWPROT[2:0]  = 3'b000;
assign M_AXI_AWQOS[3:0]   = 4'b0000;
//地址握手信号AWVALID
assign M_AXI_AWVALID      = reg_awvalid;
//写完成信号的写状态完成
assign  wr_done     = (wr_state == S_WR_DONE);
//fifo数据赋予总线
assign  M_AXI_WDATA =  wr_fifo_data;
assign  M_AXI_WSTRB = 16'hFFFF;   //写数据全部有效
//写到最后一个数据
//assign  M_AXI_WLAST  =(wr_addr_cnt == reg_w_len)?  'b1:'b0;
assign  M_AXI_WLAST  =reg_w_last;
//数据握手信号WVALID
assign  M_AXI_WVALID = reg_wvalid;
//这个信号是告诉AXI我收到你的应答
assign  M_AXI_BREADY = M_AXI_BVALID;//axi写过程状态机
always @(posedge ACLK ) begin       if(!ARESETN) beginwr_state            <= S_WR_IDLE;reg_wr_adrs           <= 29'd0;reg_awvalid         <= 1'b0;  //地址reg_wvalid          <= 1'b0;  //数据reg_w_last          <= 1'b0;reg_w_len           <= 8'd0;    wr_addr_cnt         <= 32'd0;wr_data_cnt         <= 32'd0;wr_addr_reg         <= 32'd0;end else begincase(wr_state)S_WR_IDLE: begin //写空闲//  if(ai_fifo_prog_full & !ddr_cnt_full) begin //写fifo将满且ddr内部没满时进入写地址等待状态if((wr_fifo_rd_data_count+10'd2)>=wr_len )begin//fifo数据长度大于一次突发长度wr_state          <= S_WA_WAIT;reg_wr_adrs       <= wr_addr_reg ;endreg_awvalid  <= 1'b0;reg_wvalid   <= 1'b0;reg_w_len    <= 8'd0;endS_WA_WAIT  :beginwr_state     <= S_WA_START;endS_WA_START :begin          //写地址开始拉高地址有效与数据有效wr_state     <= S_WD_WAIT;//写数据等待一个时钟周期            reg_awvalid  <= 1'b1; //拉高地址有效信号            endS_WD_WAIT  :begin    reg_wvalid   <= 1'b1;//拉高数据有效信号    if(M_AXI_AWREADY)             //等待AXI写地址就绪信号将对应地址发给从机    wr_state     <= S_WD_PROC;            reg_awvalid  <= 1'b0;       //ready有效后拉低valid信号reg_w_len    <= wr_len-'d1; //127代表128个长度,0代表1个长度            endS_WD_PROC  :begin        if(M_AXI_WREADY)begin             //等待写数据就绪if(wr_addr_cnt == reg_w_len)begin //突发写入完成reg_wvalid   <= 1'b0;         //写入完成后拉低数据有效信号;此信号告诉AXI总线我正在写数据有效        wr_state     <= S_WR_WAIT;//     reg_w_last   <= 1'b1;         //表示传输最后一个数据end else  beginwr_addr_cnt <= wr_addr_cnt + 1'b1;endif(wr_addr_cnt == reg_w_len - 1)begin //突发写入完成reg_w_last   <= 1'b1;        //传输到最后一个数据时对应拉高end else beginreg_w_last   <= 1'b0;endif(wr_addr_reg == DDR_ADDR_LIMIT)begin// reg_wr_adrs <= 29'd0;wr_addr_reg <= 32'd0;end else beginwr_addr_reg <= wr_addr_reg + 16;//一组数据写入完成之后的地址增量=突发长度x16,128位等于16字节//reg_wr_adrs <= reg_wr_adrs + 8;endend else  begin//reg_wr_adrs <= reg_wr_adrs;wr_addr_reg <= wr_addr_reg;wr_addr_cnt <= wr_addr_cnt;endif(ai_fifo_rd)beginwr_data_cnt <= wr_data_cnt +1'b1;end else beginwr_data_cnt <= wr_data_cnt ;end    endS_WR_WAIT  :beginreg_w_last<='b0;            //只持续一个时钟周期//M_AXI_BVALID拉高表示写成功,然后状态机完成一次突发传输if(M_AXI_BVALID) beginwr_state          <= S_WR_DONE;endendS_WR_DONE  :beginwr_state          <= S_WR_IDLE;wr_addr_cnt<=32'd0;wr_data_cnt<=32'd0;              enddefault: begin  wr_state<= S_WR_IDLE;endendcaseend
end
endmodule
  • axi_rd:

此模块主要也是实现AXI4接口的读时序,先上时序图:

读时序相对简单,重点也是关注读地址ARADDR与握手信号ARVALID+ARREADY。简单说一下就是当地址通道上ARVALID 和ARREADY 同时为高时,地址 A 被有效的传给设备,之后设备输出的数据将出现在读数据通道上。当RREADY 和 RVALID 同时为高的时候表明有效的数据传输。RLAST 信号表示最后一个被传输的数据。

`timescale 1ns / 1ps
module axi_rd
#(parameter   DATA_WIDTH =128,parameter   DDR_ADDR_LIMIT  =  32'h0fff_ffff
)
(input                       ARESETN    , //axi复位input                       ACLK       , //axi总时钟
//axi4读通道地址通道output [3:0]               M_AXI_ARID   , //读地址ID,用来标志一组写信号output [28:0]              M_AXI_ARADDR , //读地址,给出一次写突发传输的读地址output [7:0]               M_AXI_ARLEN  , //突发长度,给出突发传输的次数output [2:0]               M_AXI_ARSIZE , //突发大小,给出每次突发传输的字节数output [1:0]               M_AXI_ARBURST, //突发类型output                        M_AXI_ARLOCK , //总线锁信号,可提供操作的原子性output [3:0]                  M_AXI_ARCACHE, //内存类型,表明一次传输是怎样通过系统的output [2:0]                  M_AXI_ARPROT , //保护类型,表明一次传输的特权级及安全等级output [3:0]                  M_AXI_ARQOS  , //质量服务QOSoutput                        M_AXI_ARVALID, //有效信号,表明此通道的地址控制信号有效input                         M_AXI_ARREADY, //表明“从”可以接收地址和对应的控制信号
//axi读通道读数据input [3:0]                M_AXI_RID    , //读ID taginput [DATA_WIDTH-1:0]       M_AXI_RDATA  , //读数据input [1:0]                   M_AXI_RRESP  , //读响应,表明读传输的状态input                         M_AXI_RLAST  , //表明读突发的最后一次传输input                         M_AXI_RVALID , //表明此通道信号有效output                        M_AXI_RREADY , //表明主机能够接收读数据和响应信息//用户端信号
//  input                      rd_start     , //读突发触发信号input [7:0]                       rd_len       , //读突发长度
//  input [31:0]                  rd_addr      , //地址  output                        rd_fifo_we      , //连接到读fifo的写使能output [DATA_WIDTH-1:0]      rd_fifo_data , //连接到读fifo的写数据        output                           rd_done  ,    //完成一次突发input        [31:0]             ddr_count,input                         ddr_prog_full,input                         ai_pcie_fifo_prom_full
);
localparam S_RD_IDLE  = 3'd0; //读空闲
localparam S_RA_WAIT  = 3'd1; //读地址等待
localparam S_RA_START = 3'd2; //读地址
localparam S_RD_WAIT  = 3'd3; //读数据等待
localparam S_RD_PROC  = 3'd4; //读数据循环
localparam S_RD_DONE  = 3'd5; //写结束
localparam DATA_NUM =   (DATA_WIDTH == 512) ? 6 : (DATA_WIDTH == 256) ? 5 : (DATA_WIDTH == 128) ? 4 :(DATA_WIDTH == 64)  ? 3 : 2;
//reg define
reg [2:0]   rd_state   ; //状态寄存器
reg [28:0]  reg_rd_adrs; //地址寄存器
reg         reg_arvalid; //地址有效握手信号
reg [7:0]   reg_rd_len  ; //突发长度最大256,实测128最佳
reg [31:0]    rd_addr_cnt;  //读地址计数
reg [31:0]    rd_data_cnt;  //读数据计数
reg [31:0]    rd_addr_reg;  //读地址寄存器
//读fifo的写使能为axi准备好
assign    rd_fifo_we =   M_AXI_RVALID ;  //ready + valid//只有一个主机,可随意设置
assign M_AXI_ARID         = 4'b1111;
//把地址赋予总线
assign M_AXI_ARADDR = reg_rd_adrs ;
//一次突发传输1长度   (0  至 wr_len-1'd1)
assign M_AXI_ARLEN[7:0]   = rd_len-'d1;
//表示AXI总线每个数据宽度是16字节,128位。  2^(4)=16byte
assign M_AXI_ARSIZE =DATA_NUM;
//01代表地址递增,10代表递减
assign  M_AXI_ARBURST[1:0] = 2'b01;//地址递增方式传输
assign  M_AXI_ARLOCK       = 1'b0;
assign  M_AXI_ARCACHE[3:0] = 4'b0011;
assign  M_AXI_ARPROT[2:0]  = 3'b000;
assign  M_AXI_ARQOS[3:0]   = 4'b0000;
//地址握手信号AWVALID
assign  M_AXI_ARVALID      = reg_arvalid;
//写完成信号的写状态完成
assign  rd_done      = (rd_state == S_RD_DONE);
//fifo数据赋予总线
assign  rd_fifo_data = M_AXI_RDATA ;
//数据握手信号RREADY
assign  M_AXI_RREADY = M_AXI_RVALID;//axi读过程状态机
always @(posedge ACLK ) beginif(!ARESETN) beginrd_state       <= S_RD_IDLE;reg_rd_adrs    <= 29'd0;rd_addr_cnt    <= 32'd0;rd_data_cnt    <= 32'd0;rd_addr_reg    <= 32'd0;reg_arvalid    <= 1'b0;  //地址
//      reg_rd_last    <= 1'b0;reg_rd_len      <= 8'd0;  rd_addr_cnt    <=32'd0;rd_data_cnt    <=32'd0;rd_addr_reg    <=32'd0;end else begincase(rd_state)S_RD_IDLE: begin //写空闲//if(rd_start)if( ddr_prog_full & !ai_pcie_fifo_prom_full) begin // ddr内部达到1024且读FIFO没满时rd_state   <= S_RA_WAIT;reg_rd_adrs<= rd_addr_reg ;endreg_arvalid  <= 1'b0;reg_rd_len    <= 8'd0;endS_RA_WAIT  :beginrd_state     <= S_RA_START;endS_RA_START :begin                  //读地址 rd_state     <= S_RD_WAIT;    //读数据等待一个时钟周期            reg_arvalid  <= 1'b1;             //拉高地址有效信号           endS_RD_WAIT  :begin    if(M_AXI_ARREADY)    //等待AXI写地址就绪信号将对应地址发给从机    rd_state     <= S_RD_PROC;            reg_arvalid  <= 1'b0;           //arready有效后拉低arvalid信号,结束写地址,进入到读取数据状态reg_rd_len   <= rd_len-'d1;     //127代表128个长度,0代表1个长度            endS_RD_PROC  :begin        if(M_AXI_RVALID & M_AXI_RLAST)begin    //等待数据有效(M_AXI_RVALID)将对应数据发给从机        rd_state     <= S_RD_DONE;end if(M_AXI_RVALID)beginif(rd_addr_reg == DDR_ADDR_LIMIT)begin//    reg_rd_adrs <= 29'd0;rd_addr_reg <= 32'd0;end else begin//    reg_rd_adrs <= reg_rd_adrs + 8;rd_addr_reg <= rd_addr_reg + 16;end            if(rd_addr_cnt == reg_rd_len)beginrd_addr_cnt<= 32'd0;end else beginrd_addr_cnt<= rd_addr_cnt + 1'b1;// 地址每次+1        endend else begin//    reg_rd_adrs <= reg_rd_adrs ;           rd_addr_reg <= rd_addr_reg;rd_addr_cnt <= rd_addr_cnt;end if(M_AXI_RVALID)beginrd_data_cnt <= rd_data_cnt + 1'b1;end else beginrd_data_cnt <= rd_data_cnt;endend  S_RD_DONE  :beginrd_state   <= S_RD_IDLE;enddefault: begin  rd_state     <= S_RD_IDLE;endendcaseend
end
endmodule
  1. 上板验证结果分析

图1为axi写模块写触发条件设置为if(ai_fifo_prog_full & !ddr_cnt_full) 时抓取的从写fifo读出的数据,可以看出数据有误。

图1

图2为抓取的写时序的封面图,可以看出在 axi握手成功后即ai_fifo_rd拉高后写fifo数据连续读出并通过axi总线写进ddr。当时忘记截取写入与读出的数据对比图了,但是大概就是这个样子,最后只要写入与读出的自加数一致则实验成功。

只有后续有机会再继续补充验证结果图了。

图2

欢迎大家一起交流,文中如有错误地方还请指出,大家一起讨论。另外本文逻辑代码有参考《升腾Pro《FPGA Verilog开发实战指南——基于Xilinx Artix7》,感谢观看!

源码我已上传我的主页,可以直接下载。

DDR3 AXI4 IP核读写仿真实验(2)相关推荐

  1. Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)

    在上一篇中Xilinx DDR3 -- MIG IP核的配置(APP接口),已经观看了Xilinx官方提供的MIG IP核读写例程仿真波形,本着学习的目的,本篇开始自己编写MIG IP核读写程序,用于 ...

  2. qq自定义diy名片代码复制_「正点原子FPGA连载」第六章自定义IP核-呼吸灯实验

    1)摘自[正点原子]领航者 ZYNQ 之嵌入式开发指南 2)实验平台:正点原子领航者ZYNQ开发板 3)平台购买地址:https://item.taobao.com/item.htm?&id= ...

  3. 异步fifo_正点原子开拓者FPGA开发板资料连载第十五章 IP核之FIFO实验

    1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...

  4. 【正点原子FPGA连载】第十五章 IP核之FIFO实验 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

    1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...

  5. 【正点原子MP157连载】 第十四章 IP核之RAM实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  6. 【正点原子FPGA连载】第十四章 IP核之RAM实验 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

    1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...

  7. FPGA中DDR3 MIG ip核使用说明

    此篇是我在学习中做的归纳与总结,其中如果存在版权或知识错误请直接联系我,欢迎留言. PS:本着知识共享的原则,此篇博客可以随意转载,但请标明出处! 目录 1.DDR3工作原理 简介: DDR基础操作步 ...

  8. 基于K7-325T的DDR3读写仿真实验

    1. IP核设置注意事项 ⭐时钟选500MHz-即DQS频率为500MHz. ⭐Phy to controller clock ratio选择4:1-此处4代表的是DQS时钟,1代表逻辑收发时钟,此处 ...

  9. IP 核之RAM实验

    目录: 1.IP核 RAM简介 2.实验1: 配置单端口 RAM 1)实验任务 2)创建工程并添加ram ip 3)编写ram_rw.v 4)编写顶层文件 5)编写激励文件 6)仿真测试 7)ILA测 ...

最新文章

  1. OpenCV 笔记(06)— Mat 结构、像素值存储方法、创建 Mat 对象各种方法、Mat 对象的运算
  2. php的封装继承多态,PHP面向对象深入理解之二(封装、继承、多态、克隆)
  3. 深入学习SAP UI5框架代码系列之三:HTML原生事件 VS UI5 Semantic事件
  4. 标准错误处理机制——error
  5. 【记录】jenkins 安装及环境配置(一)
  6. Drools 7.11 :入门程序
  7. SSH Mybatis 框架
  8. android 重新点击图标显示不出来了,android开发怎么弄成,点击图标后弹出一个消息框。主界面不显示...
  9. Normalize.css的使用及下载
  10. Linux字符设备驱动详解
  11. 非平衡电桥电阻计算_详解用非平衡电桥如何测量电阻
  12. android 程序控制开关飞行模式,android开关飞行模式的方法
  13. OSChina 周一乱弹 ——东京12G
  14. 李宏毅2022机器学习HW4解析
  15. 【科研记录】如何判断(你自己的)研究工作的价值
  16. 08-HTML5详解(二)
  17. 发现美,创造美,拥有美^_^.
  18. 类似京东淘宝历史搜索自适应长度搜索项超两行折叠功能实现
  19. CleanMyMac X真正好用的Mac电脑系统优化软件应用工具
  20. 【渝粤教育】电大中专Office办公软件 (14)作业 题库

热门文章

  1. java开发中Bean的Builder模式简析
  2. nessus的安装以及使用(带详细步骤)
  3. 关于计算机的一些的总结
  4. 线性SVM,线性可分SVM与核函数
  5. alv布局 sap_ABAP报表开发:ALV运用
  6. dp交换机命令_华为交换机配置命令解释
  7. 漫谈软件工程(1)——回顾十年软件开发经历
  8. python安装错80072ee2_更新80072EE2错误怎么办_win7报80072ee2原因是什么
  9. rrd.so更新导致rrd_fetch返回值变更,与旧版不兼容
  10. android gson解析封装,android之Gson解析json的封装