DDR3 AXI4 IP核读写仿真实验(2)

这篇具有很好参考价值的文章主要介绍了DDR3 AXI4 IP核读写仿真实验(2)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上篇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)。

DDR3 AXI4 IP核读写仿真实验(2),fpga开发,Powered by 金山文档
  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)
 begin
    if (!FPGA_RST_N) begin
        ai_pcie_fifo_rd <= 1'b0;
    end else if(ai_pcie_fifo_rd_data_count >=13'd900) begin
        ai_pcie_fifo_rd <= 1'b1;
    end  else begin
        ai_pcie_fifo_rd <= 1'b0;
    end
end 

 always@ (posedge     clk_adc)
 begin
    if (!FPGA_RST_N) begin
        ai_fifo_data_i <= 64'd0;
        ai_fifo_wr        <= 1'd0;
    end else begin
        if(init_calib_complete & !ai_fifo_full) begin
            ai_fifo_data_i <= ai_fifo_data_i +1'b1;
            ai_fifo_wr     <= 1'b1;
        end else begin
            ai_fifo_data_i <= ai_fifo_data_i;
            ai_fifo_wr     <= 1'b0;
        end
    end
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个128bit
    parameter   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_ffff
    parameter   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,         //64M
    input       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,         //64M
    input                    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)
 begin
    if (!rst_n_i) begin
        ddr_count     <= 32'd0;
        ddr_prog_full <= 1'b0;
        ddr_cnt_full  <= 1'b0;
    end else begin
        if(ai_fifo_rd)begin                        //fifo开始读出数据到ddr3
            ddr_count <= ddr_count + 32'd8;
        end else if(M_AXI_RD_rvalid) begin         //DDR读出有效 && fifo非满 时ddr内的数据被读出
            ddr_count <= ddr_count - 32'd8;
        end else begin
            ddr_count <= ddr_count;
        end
        
        if(ddr_count >=  DDR_COUNT)begin       // 超过1024满标志信号
            ddr_prog_full <= 1'b1;
        end else begin
            ddr_prog_full <= 1'b0;
        end
        
        if(ddr_count > DDR_CNT_LIMIT)begin       // 超过 ddr_cnt_limit 满标志信号
            ddr_cnt_full <= 1'b1;
        end else begin
            ddr_cnt_full <= 1'b0;
        end
    end
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。

DDR3 AXI4 IP核读写仿真实验(2),fpga开发,Powered by 金山文档
`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 TAG
  input [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) begin
      wr_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 begin
      case(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 ;
          end
          reg_awvalid  <= 1'b0;
          reg_wvalid   <= 1'b0;
          reg_w_len    <= 8'd0;
        end
        S_WA_WAIT  :begin
          wr_state     <= S_WA_START;
        end
        S_WA_START :begin          //写地址开始拉高地址有效与数据有效
          wr_state     <= S_WD_WAIT;//写数据等待一个时钟周期            
          reg_awvalid  <= 1'b1; //拉高地址有效信号            
        end
        S_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个长度            
        end
        S_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  begin
                 wr_addr_cnt <= wr_addr_cnt + 1'b1;
              end
              if(wr_addr_cnt == reg_w_len - 1)begin //突发写入完成
                 reg_w_last   <= 1'b1;        //传输到最后一个数据时对应拉高
              end else begin
                 reg_w_last   <= 1'b0;
              end
              if(wr_addr_reg == DDR_ADDR_LIMIT)begin
                // reg_wr_adrs <= 29'd0;
                 wr_addr_reg <= 32'd0;
              end else begin
                 wr_addr_reg <= wr_addr_reg + 16;//一组数据写入完成之后的地址增量=突发长度x16,128位等于16字节
                //reg_wr_adrs <= reg_wr_adrs + 8;
              end
         end else  begin
                //reg_wr_adrs <= reg_wr_adrs;
                wr_addr_reg <= wr_addr_reg;
                wr_addr_cnt <= wr_addr_cnt;
         end
              if(ai_fifo_rd)begin
                   wr_data_cnt <= wr_data_cnt +1'b1;
              end else begin
                   wr_data_cnt <= wr_data_cnt ;
              end    
           
        end
        S_WR_WAIT  :begin
          reg_w_last<='b0;            //只持续一个时钟周期
          //M_AXI_BVALID拉高表示写成功,然后状态机完成一次突发传输
          if(M_AXI_BVALID) begin
              wr_state          <= S_WR_DONE;
          end
        end
        S_WR_DONE  :begin
              wr_state          <= S_WR_IDLE;
              wr_addr_cnt<=32'd0;
              wr_data_cnt<=32'd0;              
        end
        default: begin  
              wr_state<= S_WR_IDLE;
        end
      endcase
    end
end
endmodule
  • axi_rd:

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

DDR3 AXI4 IP核读写仿真实验(2),fpga开发,Powered by 金山文档

读时序相对简单,重点也是关注读地址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  , //质量服务QOS
  output                        M_AXI_ARVALID, //有效信号,表明此通道的地址控制信号有效
  input                         M_AXI_ARREADY, //表明“从”可以接收地址和对应的控制信号                                            
//axi读通道读数据
  input [3:0]                M_AXI_RID    , //读ID tag
  input [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 ) begin
    if(!ARESETN) begin
      rd_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 begin
      case(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 ;
          end
          reg_arvalid  <= 1'b0;
          reg_rd_len    <= 8'd0;
        end
        S_RA_WAIT  :begin
          rd_state     <= S_RA_START;
        end
        S_RA_START :begin                  //读地址 
          rd_state     <= S_RD_WAIT;    //读数据等待一个时钟周期            
          reg_arvalid  <= 1'b1;             //拉高地址有效信号           
        end
        S_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个长度            
        end
        S_RD_PROC  :begin        
            if(M_AXI_RVALID & M_AXI_RLAST)begin    //等待数据有效(M_AXI_RVALID)将对应数据发给从机        
            rd_state     <= S_RD_DONE;
            end 
            
           if(M_AXI_RVALID)begin
                if(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)begin
                    rd_addr_cnt<= 32'd0;
                end else begin
                    rd_addr_cnt<= rd_addr_cnt + 1'b1;// 地址每次+1        
                end
           end 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)begin
              rd_data_cnt <= rd_data_cnt + 1'b1;
              end else begin
              rd_data_cnt <= rd_data_cnt;
              end
        end  
        S_RD_DONE  :begin
              rd_state   <= S_RD_IDLE;
        end
        default: begin  
              rd_state     <= S_RD_IDLE;
        end
      endcase
    end
end
endmodule

  1. 上板验证结果分析

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

DDR3 AXI4 IP核读写仿真实验(2),fpga开发,Powered by 金山文档

图1 

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

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

DDR3 AXI4 IP核读写仿真实验(2),fpga开发,Powered by 金山文档

图2

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

         源码我已上传我的主页,可以直接下载。文章来源地址https://www.toymoban.com/news/detail-635894.html

到了这里,关于DDR3 AXI4 IP核读写仿真实验(2)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于紫光同创 FPGA 的 DDR3 读写实验

    此篇为专栏 《紫光同创FPGA开发笔记》 的第二篇,记录我的学习FPGA的一些开发过程和心得感悟,刚接触FPGA的朋友们可以先去此专栏置顶 《FPGA零基础入门学习路线》来做最基础的扫盲。 本篇内容基于笔者实际开发过程和正点原子资料撰写,将会详细讲解此 FPGA 实验的全流程

    2024年01月20日
    浏览(42)
  • ZYNQ AXI4总线访问DDR3实现图像数据乒乓存储与显示

    目录 前言 一、添加端口 二、添加局部变量 三、例化读写FIFO 四、内部变量修改,设置一次读写进行多少次突发操作 五、写地址 六、读地址 七、状态机 1.写状态机 2.读状态机 总结 在Altera FPGA进行图像处理时,我们采用的存储芯片为SDRAM,当时参照正点原子的例程是封装SDR

    2024年02月02日
    浏览(42)
  • Xilinx FPGA DDR3设计(三)DDR3 IP核详解及读写测试

    引言 :本文我们介绍下Xilinx DDR3 IP核的重要架构、IP核信号管脚定义、读写操作时序、IP核详细配置以及简单的读写测试。 7系列FPGA DDR接口解决方案如图1所示。 图1、7系列FPGA DDR3解决方案 1.1 用户FPGA逻辑(User FPGA Logic) 如图1中①所示,用户FPGA逻辑块是任何需要连接到外部

    2024年02月06日
    浏览(41)
  • 【两周学会FPGA】从0到1学习紫光同创FPGA开发|盘古PGL22G开发板学习之DDR3 IP简单读写测试(六)

    本原创教程由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处 适用于板卡型号: 紫光同创PGL22G开发平台(盘古22K) 一:盘古22K开发板(紫光同创PGL22G开发平台)简介 盘古22K开发板是基于紫光同创Logos系列PGL22G芯片设计的一款FPGA开发板,全

    2024年01月23日
    浏览(55)
  • 【Xilinx FPGA】DDR3 MIG IP 仿真

    Memory Interface Generator (MIG 7 Series)是 Xilinx 为 7 系列器件提供的 Memory 控制器 IP,使用该 IP 可以很方便地进行 DDR3 的读写操作。本文主要记录 Xilinx DDR3 MIG IP 的仿真过程,包括 IP 配置和 DDR3 读写仿真两部分内容。 目录 1 MIG IP 配置 2 DDR3 读写仿真         在 Vivado 开发平台 IP C

    2024年02月09日
    浏览(37)
  • fpga 通过axi master读写PS侧DDR的仿真和上板测试

           FPGA和ARM数据交互是ZYNQ系统中非常重要的内容。PS提供了供FPGA读写的AXI-HP接口用于两者的高速通信和数据交互。一般的,我们会采用AXI DMA的方式去传输数据,DMA代码基本是是C编写,对于FPGA开发者来说不利于维护和debug。本文提供一种手写AXI_MASTER接口用于PL 向DDR指定位

    2024年04月12日
    浏览(28)
  • FPGA通过PCIe读写DDR4仿真IP核

    环境:Vivado 17.4 根据个人所需选择器件库,创建好空的工程文件夹。 添加第一个IP:utility buffer 双击模块进入配置,选择差分时钟; 第二个IP,直接搜索DMA ,双击添加; 添加之后同样双击模块,进入配置:  配置完成。  第三个IP:AXI Interconnect,双击模块进入配置,将主从接

    2023年04月19日
    浏览(34)
  • 【FPGA】MIG DDR3读写逻辑测试

            笔者在之前通过microblaze软核的方式实现了DDR3芯片的读写测试,当时对于Xilinx MIG DDR控制器的理解还比较肤浅。还是想通过控制用户接口时序的方式来读写DDR,扩展和加深自己对DDR的理解。 MIG IP核配置请看我的前一篇文章 【FPGA测试】Microblaze测试DDR读写_microblaze

    2024年01月22日
    浏览(33)
  • 紫光同创 FPGA 开发跳坑指南(四)—— DDR3 控制器 IP 的使用

    DDR3 是一种大容量的存储器件,采用了预取技术和双边沿采样技术,以实现高速数据存储与读取,在视频处理中可以用来缓存 1 帧或多帧图像。 目录 一、紫光 DDR3 IP 的安装 二、紫光 DDR3 IP 的配置 三、DDR3 IP 的使用 3.1 DDR3 写操作 3.2 DDR3 读操作         在 Pango Design Suit 中,选

    2024年01月25日
    浏览(40)
  • 【FPGA】AXI4-Lite总线读写BRAM

    AXI协议基础知识 。这篇博客比较详细地介绍了AXI总线,并且罗列了所有的通道和端口,写代码的时候可以方便地进行查表。 AXI总线,AXI_BRAM读写仿真测试 。 这篇文章为代码的书写提供大致的思路,比如状态机和时序的控制问题,可以参考。 双向握手机制的实质是: 数据接

    2024年02月15日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包