基于 DDR3 的串口传图帧缓存系统设计实现(fifo2mig_axi )

文章目录

  • 前言
  • 一、接口转换模块设计
  • 二、fifo2mig_axi 模块
  • 二、接口转换模块仿真
  • 四、fifo2mig_axi_tb
  • 五、仿真展示


前言

结合串口接收模块和 tft 显示屏控制模块,设计一个基于 DDR3 的串口传图帧缓存系统。


提示:以下是本篇文章正文内容,下面案例可供参考

一、接口转换模块设计

fifo2mig_axi 模块是系统中相对比较重要的模块,涉及到与 DDR 控制器接口对接。该模块的主要是实现接口的转换,将普通的 FIFO 接口转换成 AXI 接口,用于将 FIFO 里的数据读出然后存储在 DDR 存储器以及将 DDR 存储器读出的数据存放到 FIFO 缓存。
在这里插入图片描述
AXI 接口包括 5 个通道,分为写事务和读事务。考虑模块设计实现的简单性(AXI 协议支持复杂的乱序读写操作等,这里就不做考虑),将一次完整的写事务流程规定为○1 主机向写地址通道写入地址和控制信息——>○2 写数据通道突发写入数据——>○3 收到设备的写数据响应。一次完整的读事务流程规定为○1 主机向读地址通道写入地址和控制信息——>○2 收到设备的读数据响应和读的数据。对于 DDR 控制器 mig_7series_0 模块,需要等到 init_calib_complete 为高后,才能进行
读/写操作。读/写操作不可同时进行,对读/写操作就需要有一个判断仲裁的过程,fifo2mig_axi 模块状态机设计如下图所示。
1

上电初始状态为 IDLE 状态,当 DDR 完成初始化和校准(即 init_calib_complete 变为高电平)后进入读/写仲裁状态 ARB;在该状态根据是否有读/写操作请求跳转到读/写流程的各个状态;完成一次读/写流程后,状态回到 ARB 状态进行下一次的操作。状态机采用三段式,第一二段的代码如下。

always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
 if(ui_clk_sync_rst)
 curr_state <= S_IDLE;
 else
 curr_state <= next_state;
 end
 always@(*)
 begin
 case(curr_state)
 //具体状态转移见下
 endcase
 end

用户侧逻辑的时钟和复位信号采用的 DDR 控制器 mig_7series_0 模块输出的 ui_clk 和
ui_clk_sync_rst。状态机在上电复位处于初始状态 IDLE,当 DDR 完成初始化和校准后进入
读/写仲裁状态 ARB,具体代码如下。

S_IDLE:
 begin
 if(init_calib_complete)
 next_state = S_ARB;
 else
 next_state = S_IDLE;
 end

在读/写仲裁状态 ARB,根据当前的读/写请求跳转到读/写操作流程中的地址通道的操作。这里为了设计简单化,将写操作优先级高于读优先级。

S_ARB:
 begin
 if(wr_ddr3_req == 1'b1)
 next_state = S_WR_ADDR;
 else if(rd_ddr3_req == 1'b1)
 next_state = S_RD_ADDR;
 else
 next_state = S_ARB;
 end

当在 ARB 状态出现写操作请求后,进入到 AXI 写地址通道的操作状态 S_WR_ADDR,在该状态,传输写操作的地址和控制信息,当 awready 和 awvalid 同时为高时表明地址已经传输完成,进入到写操作的写数据通道的操作。

S_WR_ADDR:
 begin
 if(m_axi_awready && m_axi_awvalid)
 next_state = S_WR_DATA;
 else
 next_state = S_WR_ADDR;
 end

在对写操作的写数据通道的操作中,当主机写完最后一个数据后,进入到等待写响应的状态。

S_WR_DATA:
 begin
 if(m_axi_wready && m_axi_wvalid && m_axi_wlast)
 next_state = S_WR_RESP;
 else
 next_state = S_WR_DATA;
 end

在等待写响应状态,当主机接收到设备的写响应后,一次完整的写操作流程完成,状态回到仲裁状态进行下一次的操作,bresp 不同值表示不同的响应结果,bresp 为 2’b00 表示写数据成功,bid 需要与写地址通道传输的 awbid 一致。

S_WR_RESP:
 begin
 if(m_axi_bready && m_axi_bvalid && (m_axi_bresp == 2'b00) && (m_axi_bid == AXI_ID))
 next_state = S_ARB;
 else
 next_state = S_WR_RESP;
 end

当在 ARB 状态出现读操作请求(此时无写请求)后,进入到 AXI 读地址通道的操作状态 S_RD_ADDR,在该状态,传输读操作的地址和控制信息,当 arready 和 arvalid 同时为高
时表明地址已经传输完成,进入到读操作的读响应通道的操作。

S_RD_ADDR:
 begin
 if(m_axi_arready && m_axi_arvalid)
 next_state = S_RD_RESP;
 else
 next_state = S_RD_ADDR;
 end

在等待读响应状态,当主机接收到设备的读响应后,一次完整的读操作流程完成,状态
回到仲裁状态进行下一次的操作,bresp 不同值表示不同的响应结果,bresp 为 2’b00 表示读
数据成功,last 表示读取的最后一个数据的标识,rid 需要与读地址通道传输的 arbid 一致。

S_RD_RESP:
 begin
 if(m_axi_rready && m_axi_rvalid && m_axi_rlast && (m_axi_rresp == 2'b00) && (m_axi_rid ==
AXI_ID))
 next_state = S_ARB;
 else
 next_state = S_RD_RESP;
 end

状态机设计完成后,剩下的就是在各个状态中产生各种信号。对于 AXI 接口的一些信号,在写操作的写地址通道比较关键的是产生 awaddr 和 awvalid。其中,awaddr 除了在复位和清除时变为起始地址外,在完成一次写操作流程后,地址就需要增加一次突发写入的数据量,需要注意的是这里的地址是以字节为单位的,则每次地址增加量应该是突发写数据个数*每个数据的字节数。这里每次突发长读为 AWLEN 加 1,每个数据是 16 字节(数据位宽是128bit),所以每完成一次写操作,地址增加(m_axi_awlen + 1’b1)*16。

always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
 if(ui_clk_sync_rst)
 m_axi_awaddr <= WR_DDR_ADDR_BEGIN;
 else if(wr_addr_clr)
 m_axi_awaddr <= WR_DDR_ADDR_BEGIN;
 else if(m_axi_awaddr >= WR_DDR_ADDR_END)
 m_axi_awaddr <= WR_DDR_ADDR_BEGIN;
 else if((curr_state == S_WR_RESP) && m_axi_bready && m_axi_bvalid && (m_axi_bresp == 2'b00)
&& (m_axi_bid == AXI_ID))
 m_axi_awaddr <= m_axi_awaddr + ((m_axi_awlen + 1'b1)<<4);
 else
 m_axi_awaddr <= m_axi_awaddr;
 end

对于 awvalid 产生就相对简单些,在进入 WR_ADDR 状态到就将其输出为高,等到awready 和 awvalid 同时高的时候,就将 awvalid 输出为低,保证 awready 和 awvalid 信号只有一个时钟周期的同时高。

always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
 if(ui_clk_sync_rst)
 m_axi_awvalid <= 1'b0;
 else if((curr_state == S_WR_ADDR) && m_axi_awready && m_axi_awvalid)
 m_axi_awvalid <= 1'b0;
 else if(curr_state == S_WR_ADDR && wr_fifo_empty)
 m_axi_awvalid <= 1'b1;
 else
 m_axi_awvalid <= m_axi_awvalid;
 end

在写操作的写数据通道比较关键的是产生 wvalid 和 wlast 信号。wvalid 在进入到WR_DATA 状态就变为高电平,在发送完最后一个数据后变为低电平。(期间,在主机给出的 wvalid 与设备给出的 wready 信号同时为高的情况下,数据写入到设备)

always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
if(ui_clk_sync_rst)
 m_axi_wvalid <= 1'b0;
 else if((curr_state == S_WR_DATA) && m_axi_wready && m_axi_wvalid && m_axi_wlast)
 m_axi_wvalid <= 1'b0;
 else if(curr_state == S_WR_DATA)
 m_axi_wvalid <= 1'b1;
 else
 m_axi_wvalid <= m_axi_wvalid;
 end

wlast 信号是主机向设备传输最后一个数据的标识信号,这个信号的产生依赖于一次突发写入数据个数和当前已经传输了几个数据,主机在传输最后一个数据同时将其输出为高,在发送完最后一个数据后立马将其输出为低。这个过程首先需要对传输数据个数进行计数,当 wready 和 m_axi_wvalid 同时为高时代表传输一个数据,传输数据个数计数器代码如下。

always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
 if(ui_clk_sync_rst)
 wr_data_cnt <= 1'b0;
 else if(curr_state == S_ARB)
 wr_data_cnt <= 1'b0;
 else if(curr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid)
 wr_data_cnt <= wr_data_cnt + 1'b1;
 else
 wr_data_cnt <= wr_data_cnt;
 end

在产生 wlast 时,分两种情况,一是当突发写数据个数为 1,也就是 wlen 等于 0 时,那么传输的第一个数就是传输的最后一个数据,这种情况下,一进入到 WR_DATA 状态就将wlast 变为高电平;二是当突发写数据个数大于 1,也就是 wlen 等于等于 1 时,就在传输完倒数第二个数(即 wr_data_cnt 为 m_axi_awlen -1’b1)后将 wlast 变为高电平。当最后一个数据传输完成(m_axi_wready、m_axi_wvalid 和 m_axi_wlast 同时为高电平)后将 wlast 变为低,具体代码如下。

always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
 if(ui_clk_sync_rst)
 m_axi_wlast <= 1'b0;
 else if(curr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid && m_axi_wlast)
 m_axi_wlast <= 1'b0;
 else if(curr_state == S_WR_DATA && m_axi_awlen == 8'd0)
 m_axi_wlast <= 1'b1;
 else if(curr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid && (wr_data_cnt ==
m_axi_awlen -1'b1))
 m_axi_wlast <= 1'b1;
 else
     m_axi_wlast <= m_axi_wlast;
 end

在读操作的写地址通道信号的产生上与写操作类似。比较关键的是产生 araddr 和 arvalid;
产生过程与 awaddr 和 awvalid 类似。awaddr 除了在复位和清除时变为起始地址外,在完成
一次读操作流程后,地址就需要增加一次突发写入的数据量。即每完成一次读操作,地址增
加(m_axi_arlen + 1’b1)*16,计算与写操作一样。具体代码如下。

//m_axi_araddr
 always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
 if(ui_clk_sync_rst)
 m_axi_araddr <= RD_DDR_ADDR_BEGIN;
 else if(rd_addr_clr)
 m_axi_araddr <= RD_DDR_ADDR_BEGIN;
 else if(m_axi_araddr >= RD_DDR_ADDR_END)
 m_axi_araddr <= RD_DDR_ADDR_BEGIN;
 else if((curr_state == S_RD_RESP) && m_axi_rready && m_axi_rvalid && m_axi_rlast &&
(m_axi_rresp == 2'b00) && (m_axi_rid == AXI_ID))
 m_axi_araddr <= m_axi_araddr + ((m_axi_awlen + 1'b1)<<4);
 else
 m_axi_araddr <= m_axi_araddr;
 end
 //m_axi_arvalid
 always@(posedge ui_clk or posedge ui_clk_sync_rst)
 begin
 if(ui_clk_sync_rst)
 m_axi_arvalid <= 1'b0;
 else if((curr_state == S_RD_ADDR) && m_axi_arready && m_axi_arvalid)
 m_axi_arvalid <= 1'b0;
 else if(curr_state == S_RD_ADDR)
 m_axi_arvalid <= 1'b1;
 else
 m_axi_arvalid <= m_axi_arvalid;
 end

对于 AXI 接口信号,除了上述信号外,还有一些信号相对比较简单,基本就是给固定值就可以,这里直接给出代码。

 assign m_axi_awid = AXI_ID ; //output [3:0] m_axi_awid 
 assign m_axi_awsize = 3'b100 ; //output [2:0] m_axi_awsize 
 assign m_axi_awburst = 2'b01 ; //output [1:0] m_axi_awburst
 assign m_axi_awlock = 1'b0 ; //output [0:0] m_axi_awlock 
 assign m_axi_awcache = 4'b0000 ; //output [3:0] m_axi_awcache
 assign m_axi_awprot = 3'b000 ; //output [2:0] m_axi_awprot
 assign m_axi_awqos = 4'b0000 ; //output [3:0] m_axi_awqos 
 assign m_axi_awlen = AXI_LEN ;
 
 assign m_axi_wstrb = 16'hffff ; //output [15:0] m_axi_wstrb 
 assign m_axi_wdata = wr_fifo_rddata;
 assign m_axi_bready = 1'b1 ; //output m_axi_bready
 assign m_axi_arid = AXI_ID ; //output [3:0] m_axi_arid 
 assign m_axi_arsize = 3'b100 ; //output [2:0] m_axi_arsize 
 assign m_axi_arburst = 2'b01 ; //output [1:0] m_axi_arburst
 assign m_axi_arlock = 1'b0 ; //output [0:0] m_axi_arlock 
 assign m_axi_arcache = 4'b0000 ; //output [3:0] m_axi_arcache
 assign m_axi_arprot = 3'b000 ; //output [2:0] m_axi_arprot 
 assign m_axi_arqos = 4'b0000 ; //output [3:0] m_axi_arqos
 assign m_axi_arlen = AXI_LEN ;
 
 assign m_axi_rready = ~rd_fifo_alfull; //output m_axi_rready





关于 AXI 接口的信号设计完后,剩下的就是与读写 FIFO 之间的接口,包括读写使能和读写数据。

 assign wr_fifo_rdreq = m_axi_wvalid && m_axi_wready;
 assign rd_fifo_wrreq = m_axi_rvalid && m_axi_rready;
 assign rd_fifo_wrdata = m_axi_rdata;

控制状态机条状的读写 DDR 请求信号是根据当前 FIFO 中数据量进行判断产生,当写FIFO 中的数据量超过一个阈值(这里阈值使用 WLEN,)就产生写 DDR 请求;当读 FIFO中的数据量低于一个阈值(这里阈值使用 RLEN,也可以设置为其他值)就产生读 DDR 请求,信号产生需要满足在 DDR 初始化完成之后并且当前 FIFO 不处于复位。

assign wr_ddr3_req = (init_calib_complete == 1'b1) && (wr_fifo_rst_busy == 1'b0) &&
(wr_fifo_rd_cnt >= m_axi_awlen);
 assign rd_ddr3_req = (init_calib_complete == 1'b1) && (rd_fifo_rst_busy == 1'b0) && (rd_fifo_wr_cnt <
m_axi_arlen);

至此关于 fifo2mig_axi 模块的设计就完成,上述设计的完整代码可参考 fifo2mig_axi 模块。

二、fifo2mig_axi 模块

代码如下(示例):

/
// Module Name   : fifo2mig_axi
// Description   : fifo接口到MIG IP AXI接口的转换模块
// Revision:vivado  2018.3
// designer: 小王在努力...
// 
/

module fifo2mig_axi
#(
  parameter WR_DDR_ADDR_BEGIN = 0      ,
  parameter WR_DDR_ADDR_END   = 200    ,
  parameter RD_DDR_ADDR_BEGIN = 0      ,
  parameter RD_DDR_ADDR_END   = 200    ,
  
  parameter AXI_ID            = 4'b0000,
  parameter AXI_LEN           = 8'd31    //burst length = 32
)
(
  //FIFO Interface ports
  input               wr_addr_clr         ,//sync ui_clk(rd_ddr3_fifo中的rst)
  output              wr_fifo_rdreq       ,//wr_ddr3_fifo(rd_en)
  input     [127:0]   wr_fifo_rddata      ,//wr_ddr3_fifo(dout)
  input               wr_fifo_empty       ,
  input     [8:0]     wr_fifo_rd_cnt      ,
  input               wr_fifo_rst_busy    ,//wr_ddr3_fifo(wrfifo_wr_rst_busy | wrfifo_rd_rst_busy)

  input               rd_addr_clr         ,//rst
  output              rd_fifo_wrreq       ,//rd_ddr3_fifo(wr_en)
  output    [127:0]   rd_fifo_wrdata      ,
  input               rd_fifo_alfull      ,//rd_ddr3_fifo.full    
  input     [8:0]     rd_fifo_wr_cnt      ,
  input               rd_fifo_rst_busy    ,

//以下是MIG IP的接口
  // Application interface ports
  input               ui_clk              ,
  input               ui_clk_sync_rst     ,
  input               mmcm_locked         ,
  input               init_calib_complete ,
  // Slave Interface Write Address Ports
  output    [3:0]     m_axi_awid          ,
  output reg[27:0]    m_axi_awaddr        ,
  output    [7:0]     m_axi_awlen         ,
  output    [2:0]     m_axi_awsize        ,
  output    [1:0]     m_axi_awburst       ,
  output    [0:0]     m_axi_awlock        ,
  output    [3:0]     m_axi_awcache       ,
  output    [2:0]     m_axi_awprot        ,
  output    [3:0]     m_axi_awqos         ,
  output reg          m_axi_awvalid       ,
  input               m_axi_awready       ,
  // Slave Interface Write Data Ports
  output    [127:0]   m_axi_wdata         ,
  output    [15:0]    m_axi_wstrb         ,
  output reg          m_axi_wlast         ,
  output reg          m_axi_wvalid        ,
  input               m_axi_wready        ,
  // Slave Interface Write Response Ports
  input     [3:0]     m_axi_bid           ,
  input     [1:0]     m_axi_bresp         ,
  input               m_axi_bvalid        ,
  output              m_axi_bready        ,
  // Slave Interface Read Address Ports
  output    [3:0]     m_axi_arid          ,
  output reg[27:0]    m_axi_araddr        ,
  output    [7:0]     m_axi_arlen         ,
  output    [2:0]     m_axi_arsize        ,
  output    [1:0]     m_axi_arburst       ,
  output    [0:0]     m_axi_arlock        ,
  output    [3:0]     m_axi_arcache       ,
  output    [2:0]     m_axi_arprot        ,
  output    [3:0]     m_axi_arqos         ,
  output reg          m_axi_arvalid       ,
  input               m_axi_arready       ,
  // Slave Interface Read Data Ports
  input     [3:0]     m_axi_rid           ,
  input     [127:0]   m_axi_rdata         ,
  input     [1:0]     m_axi_rresp         ,
  input               m_axi_rlast         ,
  input               m_axi_rvalid        ,
  output              m_axi_rready        
);


  //------------------------------------------
  //状态机参数
  localparam S_IDLE    = 7'b0000001,
             S_ARB     = 7'b0000010,
             S_WR_ADDR = 7'b0000100,
             S_WR_DATA = 7'b0001000,
             S_WR_RESP = 7'b0010000,
             S_RD_ADDR = 7'b0100000,
             S_RD_RESP = 7'b1000000;
  //------------------------------------------



  wire[7:0]wr_req_cnt_thresh;
  wire[7:0]rd_req_cnt_thresh;
  wire     wr_ddr3_req ;
  wire     rd_ddr3_req ;
  reg [6:0]curr_state  ;
  reg [6:0]next_state  ;
  reg      wr_rd_poll  ;  //0:allow wr  1:allow rd
  reg [7:0]wr_data_cnt ;

  assign m_axi_awid    = AXI_ID   ; //output [3:0]      m_axi_awid 

  //每写一个数据为2^m_axi_awsize个字节即此处为16字节128位  
  assign m_axi_awsize  = 3'b100   ; //output [2:0]      m_axi_awsize 

  //------------------------------------------
      //m_axi_awburst    label                                  meaning
      //    2'b00        FIXED       每次传输的地址是固定的,即为 S_AXI_AWADDR.该类型主要用于读或者清空 FIFO.
      //    2'b01        INCR        每次传输的地址等于上一次传输的地址加上传输的大小(即S_AXI_AWSIZE信号所给出的传输的字节数)                   
  
  assign m_axi_awburst = 2'b01    ; //output [1:0]      m_axi_awburst
  //-------------------------------------------

  //-------------------------------------------
  assign m_axi_awlock  = 1'b0     ; //output [0:0]      m_axi_awlock 
  assign m_axi_awcache = 4'b0000  ; //output [3:0]      m_axi_awcache
  assign m_axi_awprot  = 3'b000   ; //output [2:0]      m_axi_awprot 
  assign m_axi_awqos   = 4'b0000  ; //output [3:0]      m_axi_awqos   
      //上述接口为本地接口维护命令信号,这几个信号可以不用使用,输入信号直接给 0,输出信号不连接其他信号.
  //--------------------------------------------- 

  assign m_axi_awlen   = AXI_LEN  ;



  //wstrb 等于 16'hffff,表示写入的 16 个字节全部有效.
  assign m_axi_wstrb   = 16'hffff ; //output [15:0]     m_axi_wstrb 

  //ddr写数据128位由写fifo的读数据128直接给
  assign m_axi_wdata   = wr_fifo_rddata;
  

  //接受写响应就绪.该信号表示主机已经能够接受响应信息. 1 = 主机就绪 0 = 主机未就绪
  assign m_axi_bready  = 1'b1     ; //output            m_axi_bready

  //rid 需要与读地址通道传输的 arbid 一致
  assign m_axi_arid    = AXI_ID   ; //output [3:0]      m_axi_arid  


  //每读一个数据为2^m_axi_arsize个字节即此处为16字节128位   
  assign m_axi_arsize  = 3'b100   ; //output [2:0]      m_axi_arsize 

  //每次传输的地址等于上一次传输的地址加上传输的大小(即S_AXI_ARSIZE信号所给出的传输的字节数) 
  assign m_axi_arburst = 2'b01    ; //output [1:0]      m_axi_arburst
  

  //---------------------------------------------
  assign m_axi_arlock  = 1'b0     ; //output [0:0]      m_axi_arlock 
  assign m_axi_arcache = 4'b0000  ; //output [3:0]      m_axi_arcache
  assign m_axi_arprot  = 3'b000   ; //output [2:0]      m_axi_arprot 
  assign m_axi_arqos   = 4'b0000  ; //output [3:0]      m_axi_arqos
    //上述接口为本地接口维护命令信号,这几个信号可以不用使用,输入信号直接给 0,输出信号不连接其他信号.
  //--------------------------------------------- 

  //数据突发长度即一次写入的数据为m_axi_arlen+1个 字节数为(m_axi_arlen+1)*2^m_axi_arsize
  assign m_axi_arlen   = AXI_LEN  ;


  //rd_fifo_alfull = rd_ddr3_fifo.full
  assign m_axi_rready  = ~rd_fifo_alfull; //output            m_axi_rready




  //wr_fifo_rdreq = wr_ddr3_fifo(rd_en)
  assign wr_fifo_rdreq  = m_axi_wvalid && m_axi_wready;
  //rd_fifo_wrreq =rd_ddr3_fifo(wr_en)
  assign rd_fifo_wrreq  = m_axi_rvalid && m_axi_rready;
  

  //读fifo写入数据由ddr3读数据直接给入
  assign rd_fifo_wrdata = m_axi_rdata;  



  assign wr_req_cnt_thresh = (m_axi_awlen == 1'b0)? 1'b0 : AXI_LEN-1'b1;
  assign rd_req_cnt_thresh = AXI_LEN;

  
  assign wr_ddr3_req = (wr_fifo_rst_busy == 1'b0) && (wr_fifo_rd_cnt >= wr_req_cnt_thresh) ? 1'b1:1'b0;
  assign rd_ddr3_req = (rd_fifo_rst_busy == 1'b0) && (rd_fifo_wr_cnt <= rd_req_cnt_thresh) ? 1'b1:1'b0;






  //m_axi_awaddr
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      m_axi_awaddr <= WR_DDR_ADDR_BEGIN;    //WR_DDR_ADDR_BEGIN = 0;
    else if(wr_addr_clr)                    //rd_ddr3_fifo中的rst
      m_axi_awaddr <= WR_DDR_ADDR_BEGIN;
    else if(m_axi_awaddr >= WR_DDR_ADDR_END)
      m_axi_awaddr <= WR_DDR_ADDR_BEGIN;
    else if((curr_state == S_WR_RESP) && m_axi_bready && m_axi_bvalid && (m_axi_bresp == 2'b00) && (m_axi_bid == AXI_ID))
      m_axi_awaddr <= m_axi_awaddr + ((m_axi_awlen + 1'b1)<<4);
    else
      m_axi_awaddr <= m_axi_awaddr;
  end

  //m_axi_awvalid
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      m_axi_awvalid <= 1'b0;
    else if((curr_state == S_WR_ADDR) && m_axi_awready && m_axi_awvalid)
      m_axi_awvalid <= 1'b0;
    else if(curr_state == S_WR_ADDR)
      m_axi_awvalid <= 1'b1;
    else
      m_axi_awvalid <= m_axi_awvalid;
  end

  //m_axi_wvalid
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      m_axi_wvalid <= 1'b0;
    else if((curr_state == S_WR_DATA) && m_axi_wready && m_axi_wvalid && m_axi_wlast)
      m_axi_wvalid <= 1'b0;
    else if(curr_state == S_WR_DATA)
      m_axi_wvalid <= 1'b1;
    else
      m_axi_wvalid <= m_axi_wvalid;
  end

  //wr_data_cnt
  //用于计数传输的数据,控制m_axi_wlast的产生
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      wr_data_cnt <= 1'b0;
    else if(curr_state == S_ARB)
      wr_data_cnt <= 1'b0;
    else if(curr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid)
      wr_data_cnt <= wr_data_cnt + 1'b1;
    else
      wr_data_cnt <= wr_data_cnt;
  end

  //m_axi_wlast
  //--------------------
        //在产生 wlast 时,分两种情况,一是当突发写数据个数为 1,也就是 wlen 等于 0 时,
        //那么传输的第一个数就是传输的最后一个数据,这种情况下,一进入到 WR_DATA 状态就将
        //wlast 变为高电平;二是当突发写数据个数大于 1,也就是 wlen 等于等于 1 时,就在传输完
        //倒数第二个数(即 wr_data_cnt 为 m_axi_awlen -1'b1)后将 wlast 变为高电平.当最后一个
        //数据传输完成(m_axi_wready、m_axi_wvalid 和 m_axi_wlast 同时为高电平)后将 wlast 变为低
//--------------------
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      m_axi_wlast <= 1'b0;
    else if(curr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid && m_axi_wlast)
      m_axi_wlast <= 1'b0;
    else if(curr_state == S_WR_DATA && m_axi_awlen == 8'd0)
      m_axi_wlast <= 1'b1;
    else if(curr_state == S_WR_DATA && m_axi_wready && m_axi_wvalid && (wr_data_cnt == m_axi_awlen -1'b1))
      m_axi_wlast <= 1'b1;
    else
      m_axi_wlast <= m_axi_wlast;
  end

  //m_axi_araddr
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      m_axi_araddr <= RD_DDR_ADDR_BEGIN;
    else if(rd_addr_clr)
      m_axi_araddr <= RD_DDR_ADDR_BEGIN;
    else if(m_axi_araddr >= RD_DDR_ADDR_END)
      m_axi_araddr <= RD_DDR_ADDR_BEGIN;
    else if((curr_state == S_RD_RESP) && m_axi_rready && m_axi_rvalid && m_axi_rlast && (m_axi_rresp == 2'b00) && (m_axi_rid == AXI_ID))
      m_axi_araddr <= m_axi_araddr + ((m_axi_awlen + 1'b1)<<4);
    else
      m_axi_araddr <= m_axi_araddr;
  end

  //m_axi_arvalid
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      m_axi_arvalid <= 1'b0;
    else if((curr_state == S_RD_ADDR) && m_axi_arready && m_axi_arvalid)
      m_axi_arvalid <= 1'b0;
    else if(curr_state == S_RD_ADDR)
      m_axi_arvalid <= 1'b1;
    else
      m_axi_arvalid <= m_axi_arvalid;
  end

  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      wr_rd_poll <= 1'b0;
    else if(curr_state == S_ARB)
      wr_rd_poll <= ~wr_rd_poll;
    else
      wr_rd_poll <= wr_rd_poll;
  end

  //**********************************
  //state machine
  //**********************************
  always@(posedge ui_clk or posedge ui_clk_sync_rst)
  begin
    if(ui_clk_sync_rst)
      curr_state <= S_IDLE;
    else
      curr_state <= next_state;
  end

  always@(*)
  begin
    case(curr_state)
      S_IDLE:
      begin
        if(mmcm_locked && init_calib_complete)
          next_state = S_ARB;
        else
          next_state = S_IDLE;
      end
      
      //-------------
              //wr_rst_busy 和 rd_rst_busy 两个信
              //号输出,这两个信号分别表示的是写/读时钟域复位忙信号(为 1 表示忙,处于复位中,为 0
              //表示复位完),所以每次给一个异步复位信号对 FIFO 进行复位时,需要等到 wr_rst_busy 从
              //1 变为 0 后才能对 FIFO 进行写数据操作(在 FIFO 非满情况下,这个是任何写操作时候都
              //需要满足的),在 wr_rst_busy 为 1 时进行读是不允许的;同样的要等到 rd_rst_busy 从 1 变
              //为 0 后才能对 FIFO 进行读操作,在 rd_rst_busy 为 1 时进行读是不允许的
      //--------------
              //assign wr_ddr3_req = (wr_fifo_rst_busy == 1'b0) && (wr_fifo_rd_cnt >= wr_req_cnt_thresh) ? 1'b1:1'b0;
              
              //wr_fifo_rst_busy = wrfifo_wr_rst_busy | wrfifo_rd_rst_busy
              //wr_fifo_rd_cnt = {3'd0,wrfifo_rd_cnt}
              //wrfifo_rd_cnt = wr_ddr3_fifo.rd_data_count
              //wr_req_cnt_thresh = (m_axi_awlen == 1'b0)? 1'b0 : AXI_LEN-1'b1;
              //AXI_LEN = m_axi_arlen
      //-------------
              //rd_ddr3_req = (rd_fifo_rst_busy == 1'b0) && (rd_fifo_wr_cnt <= rd_req_cnt_thresh) ? 1'b1:1'b0;


              //rd_fifo_rst_busy = rdfifo_wr_rst_busy | rdfifo_rd_rst_busy
              //rd_fifo_wr_cnt = {3'd0,rdfifo_wr_cnt}
              //rdfifo_wr_cnt = rd_ddr3_fifo.wr_data_count
              //rd_req_cnt_thresh = AXI_LEN; 
      //--------------
      S_ARB:
      begin
        if((wr_ddr3_req == 1'b1) && (wr_rd_poll == 1'b0))
          next_state = S_WR_ADDR;
        else if((rd_ddr3_req == 1'b1) && (wr_rd_poll == 1'b1))
          next_state = S_RD_ADDR;
        else
          next_state = S_ARB;
      end

      S_WR_ADDR:
      begin
        if(m_axi_awready && m_axi_awvalid)
          next_state = S_WR_DATA;
        else
          next_state = S_WR_ADDR;
      end

      S_WR_DATA:
      begin
        if(m_axi_wready && m_axi_wvalid && m_axi_wlast)
          next_state = S_WR_RESP;
        else
          next_state = S_WR_DATA;
      end
      


      //m_axi_arid    = AXI_ID
      //m_axi_awid    = AXI_ID
      S_WR_RESP:
      begin
        if(m_axi_bready && m_axi_bvalid && (m_axi_bresp == 2'b00) && (m_axi_bid == AXI_ID))
          next_state = S_ARB;
        else if(m_axi_bready && m_axi_bvalid)
          next_state = S_IDLE;
        else
          next_state = S_WR_RESP;
      end

      S_RD_ADDR:
      begin
        if(m_axi_arready && m_axi_arvalid)
          next_state = S_RD_RESP;
        else
          next_state = S_RD_ADDR;
      end

      S_RD_RESP:
      begin
        if(m_axi_rready && m_axi_rvalid && m_axi_rlast && (m_axi_rresp == 2'b00) && (m_axi_rid == AXI_ID))
          next_state = S_ARB;
        else if(m_axi_rready && m_axi_rvalid && m_axi_rlast)
          next_state = S_IDLE;
        else
          next_state = S_RD_RESP;
      end

      default: next_state = S_IDLE;
    endcase
  end

endmodule

二、接口转换模块仿真

将上面 fifo2mig_axi 模块设计内容进行分析和综合直至没有错误以及警告。新建名称为
fifo2mig_axi_tb 的仿真文件。为了使仿真更加贴近我们的系统设计,仿真 testbench 设计的结
构框图如下。

1
这里仿真文件中除了需要例化 fifo2mig_axi 模块,还需要分别例化 mig_7series_0 模块、
wr_ddr3_fifo 模块、rd_ddr3_fifo 模块以及 DDR3 仿真模型。其中 3 个 IP 在前面工程建立后
就已经创建好。DDR3 的仿真模型可以在 DDR 控制器 Example Design 工程中 Source 窗口中
Simulation Sources 下找到 ddr3_model.sv 和其对应的参数配置文件 ddr3_model_parameters.vh,到工程目录下对应文件目录下找到这两个文件并复制到本节的工程中并进行添加。
2
除此之外,只需产生时钟和复位、数据产生和数据读取的激励。为了简单处理,仿真时
钟除了 DDR 控制器时钟采用产生 200MHz 时钟 sys_clk_i,其他模块时钟就统一使用 DDR
控制器输出的供用户侧使用的 ui_clk 时钟。200MHz 时钟 sys_clk_i 的时钟周期 5ns,即每
2.5ns翻转一次,时钟产生代码如下。注意这里仿真的时间单位和精度分别使用1ns和100ps,
即使用`timescale 1ns/100ps。

initial sys_clk_i = 1'b1;
always #2.5 sys_clk_i = ~sys_clk_i;

数据产生的激励设计上将其封装成任务 task 形式,方便调用。具体代码如下。该任务的
具体功能是在调用这个任务和给定的输入参数 data_begin 和 wr_data_cnt 时,向 wr_ddr3_fifo
中写入以起始数据 data_begin 开始递增的 wr_data_cnt 个数据。

task wr_data;
 input [15:0]data_begin;
 input [15:0]wr_data_cnt;
 begin
 wrfifo_wren = 1'b0;
 wrfifo_din = data_begin;
 @(posedge ui_clk);
 #1 wrfifo_wren = 1'b1;
 repeat(wr_data_cnt)
 begin 
 @(posedge ui_clk);
 wrfifo_din = wrfifo_din + 1'b1;
 end
 #1 wrfifo_wren = 1'b0;
 end
endtask

数据读取的激励设计上采用与数据产生类似的方式,封装成任务 task 形式,具体代码
如下。任务的具体功能是在调用这个任务和给定的输入参数 rd_data_cnt 时,向 rd_ddr3_fifo
中读出个 rd_data_cnt 数据。

task rd_data;
 input [15:0]rd_data_cnt;
 begin
 rdfifo_rden = 1'b0;
 @(posedge ui_clk);
 #1 rdfifo_rden = 1'b1;
 repeat(rd_data_cnt)
 begin
 @(posedge ui_clk);
 end
 #1 rdfifo_rden = 1'b0;
 end
 endtask

仿真设计上整体流程是,先产生 DDR 控制器的复位以及 FIFO 的复位。等 DDR 控制器
内部锁相环锁定后,延时 200ns 后对 FIFO 解复位。等待 DDR 初始化校准完成后,延时 200n
往 wr_ddr3_fifo 写入 1024 个数据,数据写完后,对 rd_ddr3_fifo 进行复位,清空里面的缓
存,等 FIFO 复位结束一段时间后,开始对 rd_ddr3_fifo 进行读取数据,读取 1024 个数据。
具体代码如下。

initial begin
 sys_rst = 1'b0;
 aresetn = 1'b0;
 wrfifo_clr = 1'b1;
 wrfifo_wren = 1'b0;
 wrfifo_din = 8'd0;
 rdfifo_clr = 1'b1;
 rdfifo_rden = 1'b0;
 #201;
 sys_rst = 1'b1;
 aresetn = 1'b1;
 @(posedge mmcm_locked);
 #200;
 wrfifo_clr = 1'b0;
 rdfifo_clr = 1'b0;
 @(posedge init_calib_complete);
 #200;
 
 wr_data(16'd100,16'd1024);
 #2000;
 rdfifo_clr = 1'b1;
 #20;
 rdfifo_clr = 1'b0;
 #2000;
 rd_data(16'd1024);
 
 #5000;
 $stop;
 end

至此关于 fifo2mig_axi _tb模块的设计就完成,上述设计的完整代码可参考 fifo2mig_axi_tb 模块。

四、fifo2mig_axi_tb

/
// Company       : 武汉芯路恒科技有限公司
//                 http://xiaomeige.taobao.com
// Web           : http://www.corecourse.cn
// 
// Create Date   : 2019/05/01 00:00:00
// Module Name   : fifo2mig_axi_tb
// Description   : fifo2mig_axi模块仿真文件
// 
// Dependencies  : 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
/

`timescale 1ns/100ps

module fifo2mig_axi_tb;

  reg           aresetn;
  reg           sys_clk_i;
  reg           sys_rst;
  //wr_fifo Interface
  reg           wrfifo_clr;
  reg  [15:0]   wrfifo_din;
  reg           wrfifo_wren;
  wire          wrfifo_rden;
  wire [127:0]  wrfifo_dout;
  wire [5 : 0]  wrfifo_rd_cnt;
  wire          wrfifo_empty;
  wire          wrfifo_wr_rst_busy;
  wire          wrfifo_rd_rst_busy;
  //rd_fifo Interface
  reg           rdfifo_clr;
  wire          rdfifo_wren;
  wire [127:0]  rdfifo_din;
  reg           rdfifo_rden;
  wire [15 :0]  rdfifo_dout;
  wire [5 : 0]  rdfifo_wr_cnt;
  wire          rdfifo_full;
  wire          rdfifo_wr_rst_busy;
  wire          rdfifo_rd_rst_busy;
  //mig Interface
  wire          ui_clk;
  wire          ui_clk_sync_rst;
  wire          mmcm_locked;
  wire          init_calib_complete;

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

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

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

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

  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;
  wire          s_axi_rready;

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



  //200M时钟输入
  initial sys_clk_i = 1'b1;
  always #2.5 sys_clk_i = ~sys_clk_i;

  initial begin
    sys_rst = 1'b0;
    aresetn = 1'b0;
    wrfifo_clr  = 1'b1;
    wrfifo_wren = 1'b0;
    wrfifo_din  = 8'd0;
    rdfifo_clr  = 1'b1;
    rdfifo_rden = 1'b0;
    #201;
    sys_rst = 1'b1;
    aresetn = 1'b1;
    @(posedge mmcm_locked);
    #200;
    wrfifo_clr  = 1'b0;
    rdfifo_clr  = 1'b0;
    @(posedge init_calib_complete);
    #200;

    wr_data(16'd100,16'd1024);
    #2000;
    rdfifo_clr  = 1'b1;
    #20;
    rdfifo_clr  = 1'b0;
    #2000;
    rd_data(16'd1024);

    #5000;
    $stop;
  end

  task wr_data;
    input [15:0]data_begin;
    input [15:0]wr_data_cnt;
    begin
      wrfifo_wren = 1'b0;
      wrfifo_din  = data_begin;
      @(posedge ui_clk);
      #1 wrfifo_wren = 1'b1;
      repeat(wr_data_cnt)
      begin        
        @(posedge ui_clk);
        wrfifo_din = wrfifo_din + 1'b1;
      end
      #1 wrfifo_wren = 1'b0;
    end
  endtask

  task rd_data;
    input [15:0]rd_data_cnt;
    begin
      rdfifo_rden = 1'b0;
      @(posedge ui_clk);
      #1 rdfifo_rden = 1'b1;
      repeat(rd_data_cnt)
      begin
        @(posedge ui_clk);
      end
      #1 rdfifo_rden = 1'b0;
    end
  endtask

  wr_ddr3_fifo wr_ddr3_fifo
  (
    .rst           (wrfifo_clr         ), // input  wire rst
    .wr_clk        (ui_clk             ), // input  wire wr_clk
    .rd_clk        (ui_clk             ), // input  wire rd_clk
    .din           (wrfifo_din         ), // input  wire [15 : 0] din
    .wr_en         (wrfifo_wren        ), // input  wire wr_en
    .rd_en         (wrfifo_rden        ), // input  wire rd_en 
    .dout          (wrfifo_dout        ), // output wire [127 : 0] dout
    .full          (                   ), // output wire full
    .empty         (wrfifo_empty       ), // output wire empty
    .rd_data_count (wrfifo_rd_cnt      ), // output wire [5 : 0] rd_data_count
    .wr_data_count (                   ), // output wire [8 : 0] wr_data_count
    .wr_rst_busy   (wrfifo_wr_rst_busy ), // output wire wr_rst_busy
    .rd_rst_busy   (wrfifo_rd_rst_busy )  // output wire rd_rst_busy
  );

  rd_ddr3_fifo rd_ddr3_fifo
  (
    .rst           (rdfifo_clr         ), // input  wire rst
    .wr_clk        (ui_clk             ), // input  wire wr_clk
    .rd_clk        (ui_clk             ), // input  wire rd_clk
    .din           (rdfifo_din         ), // input  wire [127 : 0] din
    .wr_en         (rdfifo_wren        ), // input  wire wr_en
    .rd_en         (rdfifo_rden        ), // input  wire rd_en
    .dout          (rdfifo_dout        ), // output wire [15 : 0] dout
    .full          (rdfifo_full        ), // output wire full
    .empty         (                   ), // output wire empty
    .rd_data_count (                   ), // output wire [8 : 0] rd_data_count
    .wr_data_count (rdfifo_wr_cnt      ), // output wire [5 : 0] wr_data_count
    .wr_rst_busy   (rdfifo_wr_rst_busy ), // output wire wr_rst_busy
    .rd_rst_busy   (rdfifo_rd_rst_busy )  // output wire rd_rst_busy
  );

  assign wr_addr_clr = wrfifo_clr;
  assign rd_addr_clr = rdfifo_clr;

  fifo2mig_axi
  #(
    .WR_DDR_ADDR_BEGIN (0         ),
    .WR_DDR_ADDR_END   (1024*2    ),
    .RD_DDR_ADDR_BEGIN (0         ),
    .RD_DDR_ADDR_END   (1024*2    ),

    .AXI_ID            (4'b0000   ),
    .AXI_LEN           (8'd31     )  //axi burst length = 32
  )fifo2mig_axi
  (
    //FIFO Interface ports
    .wr_addr_clr         (wr_addr_clr         ), //1:clear sync ui_clk
    .wr_fifo_rdreq       (wrfifo_rden         ),
    .wr_fifo_rddata      (wrfifo_dout         ),
    .wr_fifo_empty       (wrfifo_empty        ),
    .wr_fifo_rd_cnt      ({3'd0,wrfifo_rd_cnt}),
    .wr_fifo_rst_busy    (wrfifo_wr_rst_busy | wrfifo_rd_rst_busy),

    .rd_addr_clr         (rd_addr_clr         ), //1:clear sync ui_clk
    .rd_fifo_wrreq       (rdfifo_wren         ),
    .rd_fifo_wrdata      (rdfifo_din          ),
    .rd_fifo_alfull      (rdfifo_full         ),
    .rd_fifo_wr_cnt      ({3'd0,rdfifo_wr_cnt}),
    .rd_fifo_rst_busy    (rdfifo_wr_rst_busy | rdfifo_rd_rst_busy),  
    // Application interface ports
    .ui_clk              (ui_clk              ),
    .ui_clk_sync_rst     (ui_clk_sync_rst     ),
    .mmcm_locked         (mmcm_locked         ),
    .init_calib_complete (init_calib_complete ),
    // Slave Interface Write Address Ports
    .m_axi_awid          (s_axi_awid          ),
    .m_axi_awaddr        (s_axi_awaddr        ),
    .m_axi_awlen         (s_axi_awlen         ),
    .m_axi_awsize        (s_axi_awsize        ),
    .m_axi_awburst       (s_axi_awburst       ),
    .m_axi_awlock        (s_axi_awlock        ),
    .m_axi_awcache       (s_axi_awcache       ),
    .m_axi_awprot        (s_axi_awprot        ),
    .m_axi_awqos         (s_axi_awqos         ),
    .m_axi_awvalid       (s_axi_awvalid       ),
    .m_axi_awready       (s_axi_awready       ),
    // Slave Interface Write Data Ports
    .m_axi_wdata         (s_axi_wdata         ),
    .m_axi_wstrb         (s_axi_wstrb         ),
    .m_axi_wlast         (s_axi_wlast         ),
    .m_axi_wvalid        (s_axi_wvalid        ),
    .m_axi_wready        (s_axi_wready        ),
    // Slave Interface Write Response Ports
    .m_axi_bid           (s_axi_bid           ),
    .m_axi_bresp         (s_axi_bresp         ),
    .m_axi_bvalid        (s_axi_bvalid        ),
    .m_axi_bready        (s_axi_bready        ),
    // Slave Interface Read Address Ports
    .m_axi_arid          (s_axi_arid          ),
    .m_axi_araddr        (s_axi_araddr        ),
    .m_axi_arlen         (s_axi_arlen         ),
    .m_axi_arsize        (s_axi_arsize        ),
    .m_axi_arburst       (s_axi_arburst       ),
    .m_axi_arlock        (s_axi_arlock        ),
    .m_axi_arcache       (s_axi_arcache       ),
    .m_axi_arprot        (s_axi_arprot        ),
    .m_axi_arqos         (s_axi_arqos         ),
    .m_axi_arvalid       (s_axi_arvalid       ),
    .m_axi_arready       (s_axi_arready       ),
    // Slave Interface Read Data Ports
    .m_axi_rid           (s_axi_rid           ),
    .m_axi_rdata         (s_axi_rdata         ),
    .m_axi_rresp         (s_axi_rresp         ),
    .m_axi_rlast         (s_axi_rlast         ),
    .m_axi_rvalid        (s_axi_rvalid        ),
    .m_axi_rready        (s_axi_rready        )
  );

  mig_7series_0 u_mig_7series_0 (
    // Memory interface ports
    .ddr3_addr           (ddr3_addr           ),  // output [13:0]   ddr3_addr
    .ddr3_ba             (ddr3_ba             ),  // output [2:0]    ddr3_ba
    .ddr3_cas_n          (ddr3_cas_n          ),  // output          ddr3_cas_n
    .ddr3_ck_n           (ddr3_ck_n           ),  // output [0:0]    ddr3_ck_n
    .ddr3_ck_p           (ddr3_ck_p           ),  // output [0:0]    ddr3_ck_p
    .ddr3_cke            (ddr3_cke            ),  // output [0:0]    ddr3_cke
    .ddr3_ras_n          (ddr3_ras_n          ),  // output          ddr3_ras_n
    .ddr3_reset_n        (ddr3_reset_n        ),  // output          ddr3_reset_n
    .ddr3_we_n           (ddr3_we_n           ),  // output          ddr3_we_n
    .ddr3_dq             (ddr3_dq             ),  // inout [15:0]    ddr3_dq
    .ddr3_dqs_n          (ddr3_dqs_n          ),  // inout [1:0]     ddr3_dqs_n
    .ddr3_dqs_p          (ddr3_dqs_p          ),  // inout [1:0]     ddr3_dqs_p
    .init_calib_complete (init_calib_complete ),  // output          init_calib_complete
    .ddr3_cs_n           (ddr3_cs_n           ),  // output [0:0]    ddr3_cs_n
    .ddr3_dm             (ddr3_dm             ),  // output [1:0]    ddr3_dm
    .ddr3_odt            (ddr3_odt            ),  // output [0:0]    ddr3_odt
    // Application interface ports
    .ui_clk              (ui_clk              ),  // output          ui_clk
    .ui_clk_sync_rst     (ui_clk_sync_rst     ),  // output          ui_clk_sync_rst
    .mmcm_locked         (mmcm_locked         ),  // output          mmcm_locked
    .aresetn             (aresetn             ),  // input           aresetn
    .app_sr_req          (1'b0                ),  // input           app_sr_req
    .app_ref_req         (1'b0                ),  // input           app_ref_req
    .app_zq_req          (1'b0                ),  // input           app_zq_req
    .app_sr_active       (                    ),  // output          app_sr_active
    .app_ref_ack         (                    ),  // output          app_ref_ack
    .app_zq_ack          (                    ),  // output          app_zq_ack
    // Slave Interface Write Address Ports               
    .s_axi_awid          (s_axi_awid          ),  // input [3:0]     s_axi_awid
    .s_axi_awaddr        (s_axi_awaddr        ),  // input [27:0]    s_axi_awaddr
    .s_axi_awlen         (s_axi_awlen         ),  // input [7:0]     s_axi_awlen
    .s_axi_awsize        (s_axi_awsize        ),  // input [2:0]     s_axi_awsize
    .s_axi_awburst       (s_axi_awburst       ),  // input [1:0]     s_axi_awburst
    .s_axi_awlock        (s_axi_awlock        ),  // input [0:0]     s_axi_awlock
    .s_axi_awcache       (s_axi_awcache       ),  // input [3:0]     s_axi_awcache
    .s_axi_awprot        (s_axi_awprot        ),  // input [2:0]     s_axi_awprot
    .s_axi_awqos         (s_axi_awqos         ),  // input [3:0]     s_axi_awqos
    .s_axi_awvalid       (s_axi_awvalid       ),  // input           s_axi_awvalid
    .s_axi_awready       (s_axi_awready       ),  // output          s_axi_awready
    // Slave Interface Write Data Ports
    .s_axi_wdata         (s_axi_wdata         ),  // input [127:0]   s_axi_wdata
    .s_axi_wstrb         (s_axi_wstrb         ),  // input [15:0]    s_axi_wstrb
    .s_axi_wlast         (s_axi_wlast         ),  // input           s_axi_wlast
    .s_axi_wvalid        (s_axi_wvalid        ),  // input           s_axi_wvalid
    .s_axi_wready        (s_axi_wready        ),  // output          s_axi_wready
    // Slave Interface Write Response Ports              
    .s_axi_bid           (s_axi_bid           ),  // output [3:0]    s_axi_bid
    .s_axi_bresp         (s_axi_bresp         ),  // output [1:0]    s_axi_bresp
    .s_axi_bvalid        (s_axi_bvalid        ),  // output          s_axi_bvalid
    .s_axi_bready        (s_axi_bready        ),  // input           s_axi_bready
    // Slave Interface Read Address Ports                
    .s_axi_arid          (s_axi_arid          ),  // input [3:0]     s_axi_arid
    .s_axi_araddr        (s_axi_araddr        ),  // input [27:0]    s_axi_araddr
    .s_axi_arlen         (s_axi_arlen         ),  // input [7:0]     s_axi_arlen
    .s_axi_arsize        (s_axi_arsize        ),  // input [2:0]     s_axi_arsize
    .s_axi_arburst       (s_axi_arburst       ),  // input [1:0]     s_axi_arburst
    .s_axi_arlock        (s_axi_arlock        ),  // input [0:0]     s_axi_arlock
    .s_axi_arcache       (s_axi_arcache       ),  // input [3:0]     s_axi_arcache
    .s_axi_arprot        (s_axi_arprot        ),  // input [2:0]     s_axi_arprot
    .s_axi_arqos         (s_axi_arqos         ),  // input [3:0]     s_axi_arqos
    .s_axi_arvalid       (s_axi_arvalid       ),  // input           s_axi_arvalid
    .s_axi_arready       (s_axi_arready       ),  // output          s_axi_arready
    // Slave Interface Read Data Ports
    .s_axi_rid           (s_axi_rid           ),  // output [3:0]    s_axi_rid
    .s_axi_rdata         (s_axi_rdata         ),  // output [127:0]  s_axi_rdata
    .s_axi_rresp         (s_axi_rresp         ),  // output [1:0]    s_axi_rresp
    .s_axi_rlast         (s_axi_rlast         ),  // output          s_axi_rlast
    .s_axi_rvalid        (s_axi_rvalid        ),  // output          s_axi_rvalid
    .s_axi_rready        (s_axi_rready        ),  // input           s_axi_rready
    // System Clock Ports
    .sys_clk_i           (sys_clk_i           ),
    .sys_rst             (sys_rst             )   // input           sys_rst
  );

  ddr3_model 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

五、仿真展示

可以看到如下图所示的波形。
1
在初始化校准完成信号变高后,就立马产生了一次读 DDR 操作,同时读出数据写入到
rd_ddr3_fifo,这个时候从 DDR 读出的数据是无效的,因为在此之前还没有往 DDR 里存入
数据。这就是为什么在仿真上在往 DDR 存储数据后,读数据之前先对 rd_ddr3_fifo 进行一
次复位清空缓存的操作,同时对把 DDR 的地址也复位到起始地址。这样可保证读取的数据
都是从 DDR 起始地址开始,同时保证读之前读 FIFO 没有无效的缓存数据。在串口传图系
统上使用也是采用仿真类似的方式进行的,为了达到显示屏显示图像的帧同步(不出现显示
偏移问题),会在显示每帧数据前将读 FIFO 模块 rd_ddr3_fifo 进行一次复位清空缓存和读
DDR 操纵的地址复位到起始。保证每次屏幕显示图像数据都是从 DDR 内存中起始地址开
始读出的数据。
之后在往写 FIFO 写入 1024 个数据(位宽为 16bit)过程中,AXI 总线上产生了 4 次突
发写,每次突发写是 32 个数据(位宽为 128bit),这个与设置的突发写长度和预期的突发写
次数(写 FIFO 数据个数FIFO 写数据位宽/AXI 数据位宽/AXI 一次突发写数据个数 =
1024
16/128/32 =4,根据这个公式,写 FIFO 数据个数不是随意设置,否则会出现不能满足
写入数据正好是 AXI 突发写数据个数的整数被)一致。
在这里插入图片描述
对第一次突发写 DDR 操作部分 AXI 写事务波形放大来看。
在这里插入图片描述
从波形图可以看到一次 AXI 写数据操作流程如下:
(1)在地址通道写入接下来要写入数据的起始地址和写突发长度等信息,在 awready
和 awvalid 同时为高时,这些信息被传输给 DDR 控制器;
(2)在写数据通道写入指定突发长度的数据,当 wready 和 wvalid 同时为高时表示写
入数据被传输给 DDR 控制器,在写最后一个数据时,wlast 变为高,写完最后一个 wlast 变
为低;
(3)在写响应通道等待设备的写响应,当 bready 和 bvalid 同时为高时表示响应的到来。
一次操作过程中,数据写入流程与我们设计预期是一致的。同时可以对写入的数据波形
放大了看,写入的每个数据是由写 FIFO 写入 8 个 16bit 数据拼接而成的 128bit 数据,具体
拼接过程的位宽转换是由写 FIFO 完成。可以通过类似方法去对其他几次的写操作波形进行
观察,从而验证设计的正确性。
将 1024 个数据(位宽为 16bit)写完之后,对读 FIFO 进行一次清零,清零的作用前面
已经说过。等待一段时间后,对读 FIFO 进行读数据操作,读取前面写入的 1024 个数据。读
数据过程中,AXI 总线上就会产生突发读的操作。
在这里插入图片描述
对红色圈中第一次突发读 DDR 操作部分 AXI 读事务波形放大来看。
(1)在地址通道写入接下来要读取数据的起始地址和读突发长度等信息,在 arready 和
arvalid 同时为高时,这些信息被传输给 DDR 控制器;
(2)在读响应通道,当 rready 和 rvalid 同时为高时表示读出的有效数据,当 rlast 变为
高时表示读出的最后一个数据。图中波形 rdata 有部分地方数据是未知,这些地方并非是有
效数据地方,可不用关心。
从整体上看,写 FIFO 中写入数据是 100~1123 的 1024 个连续递增数据,波形与仿真代
码是一致的,下图是整个写入数据和写入第一个数据与最后一个数据的波形图。
在这里插入图片描述
在这里插入图片描述
读 FIFO 中读出的数据同样也是 100~1123 的 1024 个连续递增数据,注意读 FIFO 的输
出模式是 First Word Fall Through。
在这里插入图片描述
从写入数据与最后读出数据的一致性说明 fifo2mig_axi 模块功能是正常的。

【附件:】链接:https://pan.baidu.com/s/1UhXO3PCYuJyESSTmCPrVrw?pwd=8dr7
提取码:8dr7

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/19666.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电影《银河护卫队3》观后感

上周看了电影《银河护卫队3》&#xff0c;本部电影&#xff0c;主要是围绕着主角团队中的一个队员展开叙事的&#xff0c;在团队中&#xff0c;这名队员叫“火箭”&#xff0c;是一只经过基因改造过的浣熊。 当初进行改造的团队&#xff0c;是一家拥有基因改造技术的团队&…

程序员:面试造飞机,入职拧螺丝?真难···

刚开始工作的时候&#xff0c;我也想不通这个问题&#xff0c;甚至很鄙视这种现象。后面当了面试官&#xff0c;做到了公司中层管理&#xff0c;也会站在公司以及行业角度去重新思考这个问题。 为什么这种现象会越来越普遍呢&#xff1f;尤其在 IT 行业愈加明显。 面试看的是…

命令firewalld和firewall-cmd用法

firewalld命令跟firewall-cmd 1.启动firewalld服务 systemctl start firewalld.service2.关闭firewalld服务 systemctl stop firewalld.service3.重启firewalld服务 systemctl restart firewalld.service4.查看firewalld状态 systemctl status firewalld.service5.开机自启…

chatgpt-4它的未来是什么?该如何应用起来?

在当今快节奏的数字通信世界中&#xff0c;ChatGPT已成为一个强大的在线聊天平台&#xff0c;改变了人们互动和沟通的方式。凭借其先进的AI功能、用户友好的界面和创新技术&#xff0c;ChatGPT已成为个人和企业的热门选择。 然而&#xff0c;ChatGPT的未来有望更加激动人心和具…

Java多线程synchronized Lock volatile,看完这一篇就够了

线程安全问题 一、对线程安全的理解&#xff08;实际上是内存安全&#xff09;二、线程同步的实现方法synchronized实现同步Lock实现同步volatile实现同步JUC的Callable实现同步 三、守护线程四、ThreadLocal原理和使用场景五、Thread类的常用方法&#xff0c;以及线程的状态六…

junit如何在多模块项目中使用

文章目录 前言一、最简单的单元测试二、springboot多模块测试单元1.问题2.解决 总结 前言 相信后端的小伙伴对于junit测试应该不陌生,当我们写好了一些功能之后,由于不太放心是否会出现问题,我们会选择自测; 第一种 通过类似postman之类的,直接走接口测试第二种 由于构造数据…

Spring Boot集成ShardingSphere实现数据加密及数据脱敏 | Spring Cloud 48

一、前言 通过以下系列章节&#xff1a; Spring Boot集成ShardingSphere实现数据分片&#xff08;一&#xff09; | Spring Cloud 40 Spring Boot集成ShardingSphere实现数据分片&#xff08;二&#xff09; | Spring Cloud 41 Spring Boot集成ShardingSphere实现数据分片&…

SoringBoot——pom文件:starter

先来看一看&#xff1a; 这次我们来介绍SpringBoot的pom文件的另一个好玩的地方&#xff1a;starter。 starter的中文含义是启动器&#xff0c;所以有时候我们在Maven仓库找依赖的时候&#xff0c;如果开启了自动翻译就会经常会看见一个奇怪的词叫做某某弹簧启动器&#xff0…

tomcat集群下的session共享和负载均衡(memcache实现)

环境 操作系统&#xff1a;windows tomcat1&#xff1a;Apache Tomcat/7.0.52&#xff08;8085&#xff09; tomcat2&#xff1a;Apache Tomcat/7.0.52&#xff08;8086&#xff09; jdk&#xff1a;1.8.0_251 nginx&#xff1a;nginx-1.20.1&#xff08;8070&#xff09; memc…

day4-字符设备驱动基础上_基础框架

大纲&#xff1a; 设备分类 申请和注销设备号 函数指针复习 注册字符设备 字符设备驱动框架解析 读操作实现 写操作实现 ioctl操作实现 printk 多个次设备的支持 一、Linux内核对设备的分类 linux的文件种类&#xff1a; -&#xff1a;普通文件 (文件内容、文件名、…

【终极解决方案】IDEA maven 项目修改代码不生效。

【终极解决方案】IDEA maven 项目修改代码不生效。 文章目录 【终极解决方案】IDEA maven 项目修改代码不生效。1、项目问题描述2、可能的解决方案3、分析原因4、解决方案5、参考文献 1、项目问题描述 遇到一个非常奇怪的问题&#xff0c;修改了一个基于maven搭建的SSM项目&am…

c++ this指针

this指针介绍&#xff1a; c中成员变量和成员函数分开存储&#xff0c;每一个非静态成员函数只会有一个实例&#xff0c;多个同类型对象共用这一个成员函数。那么代码怎么区分哪个对象调用自己呢&#xff1f;this指针由此应运而生。 c通过提供对象指针&#xff0c;this指针。…

3D点云的基本操作(基于PCL编程)

知识储备 右手系 右手&#xff0c;拇指&#xff0c;食指&#xff0c;中指&#xff0c;分别是x,y,z的正方向。左手系则同理。 旋转矩阵 本质&#xff1a;两个坐标系之间的旋转关系。 用途&#xff1a;旋转点云。 原理&#xff1a;设传感器的坐标系为O1X1Y1Z1&#xff0c;设…

高分辨率光学遥感图像水体分类综述2022.03

本文是Water body classification from high-resolution optical remote sensing imagery: Achievements and perspectives的学习笔记。 相关资源被作者整理到&#xff1a;这里 文章目录 Introduction基本知识 挑战和机遇挑战1. 有限的光谱信息和小场景覆盖2. 形状、大小和分布…

【Python入门】Python循环语句(while循环的基础语法)

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…

Spring_jdbcTemplate基本使用

文章目录 一、导入spring-jdbc和spring-tx坐标二、创建数据库表和实体在applicationContext.xml中配置连接池和JdbcTemplate在test数据库中创建account表 三、创建JdbcTemplate对象四、执行数据库操作 一、导入spring-jdbc和spring-tx坐标 <dependency><groupId>o…

Gateway

Gateway Nacos配置管理 同一配置管理 在配置管理界面点击&#xff0b; 然后填写配置信息 配置获取步骤&#xff1a; 引入Nacos的配置管理客户端 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-…

【源码解析】SpringBoot整合AOP原理解析

AOP介绍 AOP&#xff08;Aspect Oriented Programming&#xff09;是基于切面编程的&#xff0c;可无侵入的在原本功能的切面层添加自定义代码&#xff0c;一般用于日志收集、权限认证等场景。 AOP基本概念 通知&#xff08;Advice&#xff09;: AOP 框架中的增强处理。通知…

力扣sql中等篇练习(十八)

力扣sql中等篇练习(十八) 1 银行账户概要 1.1 题目内容 1.1.1 基本题目信息1 1.1.2 基本题目信息2 1.1.3 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below SELECT u.user_id,u.user_name,u.creditIFNULL(t1.c1,0) credit,case when u.creditIFNULL…

浅谈Hutool工具类

一、Hutool简介 Hutool是一个Java工具类库&#xff0c;它封装了很多常用的Java工具类&#xff0c;如加密解密、文件操作、日期时间处理、Http客户端等。它的目标是让Java开发变得更加简单、高效。 二、Hutool的特点 高效&#xff1a;提供了很多高效的工具类和方法。 简单&…