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

news2024/9/21 19:05:37

文章目录

  • 前言
  • 一、接口转换模块设计
  • 二、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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

次世代烘焙 法线贴图 相关知识

一般将低模 高模的法线贴图实现大量细节模型画面的游戏称为次时代游戏。 次世代常用软件 低模&#xff1a;Maya、3Dmax、Topogun 。 中模&#xff1a;Maya、3Dmax 。 高模&#xff1a;Maya、3Dmax、Zbrush。 UV&#xff1a;Maya、Zbrush、Unfold3D、Uvlayout 。 烘焙&#x…

【观察】华为重构分销伙伴体系,坚持“长期主义”做大分销市场

毫无疑问&#xff0c;随着数字化转型的加速&#xff0c;当前不同类型、不同规模的企业&#xff0c;在面临数字化转型时呈现出了不同的困境和特征&#xff0c;同时对合作伙伴也提出了更高的要求&#xff0c;因此唯有通过“精耕细作”的方式才能更好地加速企业数字化转型的步伐。…

AdaSparse: 自适应稀疏网络的多场景CTR预估建模

▐ 摘要 CTR(Click-through rate)预估一直是推荐/广告领域重要技术之一。近年来&#xff0c;通过统一模型来服务多个场景的预估建模已被证明是一种有效的手段。当前多场景预估技术面临的挑战主要来自两方面&#xff1a;1&#xff09;跨场景泛化能力&#xff1a;尤其对稀疏场景&…

【分布式锁】Redisson分布式锁的使用(推荐使用)

文章目录 前言一、常见分布式锁方案对比二、分布式锁需满足四个条件三、什么是Redisson?官网和官方文档Redisson使用 四、Redisson 分布式重入锁用法Redisson 支持单点模式、主从模式、哨兵模式、集群模式自己先思考下,如果要手写一个分布式锁组件&#xff0c;怎么做&#xff…

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-1

深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践-总结-1 Java内存区域与内存溢出异常运行时数据区域程序计数器Java虚拟机栈本地方法栈Java堆方法区 OutOfMemoryError异常Java堆溢出 垃圾收集器与内存分配策略对象是否可以被回收引用计数算法可达性分析算法 Java内存区域…

力库华为机试题练习

1、两数之和 arg [2, 3, 6, 5] target 4 for i in range(len(arg)): other target - arg[i] if other in arg[i1:]: print(i, arg[i1:].index(other)i1) else: print(“输入目标数在该列表中不存在”) 2、回文数 方法一&#xff1a; class Solution: def isPalindrome(sel…

抖音小程序怎么压缩图片?教你使用抖音图片压缩助手

图片压缩是将原始图像的数据量进行减少&#xff0c;从而使其文件大小更小&#xff0c;但尽量保持原有图像质量的一种技术。通过对图片进行压缩&#xff0c;可以降低图片在传输过程中所需的带宽和存储空间&#xff0c;提高网站或应用程序的加载速度和响应速度。 此外&#xff0…

亚马逊云科技将帮助GoPlus Security,助力行业健康发展

Gartner 2022年7月发布的技术成熟度曲线分析报告显示&#xff0c;目前Web3技术已经历了第一波创新高峰期&#xff0c;正在从“创新启动阶段”向“创新泡沫阶段”过渡&#xff0c;技术体系逐步成型&#xff0c;市场热度较高&#xff0c;创业投资活跃。高速增长的背后&#xff0c…

浅谈Hutool工具类

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

最全的国内chatGPT大模型企业及产品整理

作者 | gongyouliu 编辑 | gongyouliu 自从去年11月30日openAI发布chatGPT以来&#xff0c;chatGPT引爆了新一轮科技革命。最近很多年都没有哪一项科技进步如chatGPT这般吸引全球的目光。除了媒体的大肆报道&#xff0c;国内外各个科技公司、科研机构、高等院校都在跟进&#x…

智能卡接口(ISO7816)

概述 智能卡接口&#xff08;7816&#xff09;是外部智能卡通过2 线交换8 位数据的串行同步通讯手段。芯片提供了2 个7816主机接口模块。 ⚫ 2路独立7816接口 ⚫ 具备卡时钟输出端口&#xff0c;输出频率在1MHz~5MHz之间可设 ⚫ 位传输方向可配置&#xff0c;支持MSB First或LS…

初识C++之C++中的IO流

目录 一、C语言中的输入与输入 二、流 三、C中的流 四、C中的文件IO流 1. 二进制文件 1.1 打开文件 1.2 向文件写入数据 1.3 从文件读取数据 1.4 关闭文件 1.5 综合使用 2. 文本读写 一、C语言中的输入与输入 在C语言中&#xff0c;我们最长使用的输入输出方式就是…

0基础小白简单入门使用emqx的webhook+规则实现Mysql数据持久化

EMQX (opens new window)是一款大规模可弹性伸缩的云原生分布式物联网 MQTT (opens new window)消息服务器。 作为全球最具扩展性的 MQTT 消息服务器&#xff0c;EMQX 提供了高效可靠海量物联网设备连接&#xff0c;能够高性能实时移动与处理消息和事件流数据&#xff0c;帮助…

EndNote X9 参考文献附录列表 格式调整

文章目录 1 参考文献附录列表 格式调整2 EndNote X9 插入参考文献常见问题总结3 EndNote X9 快速上手教程&#xff08;毕业论文参考文献管理器&#xff09; 1 参考文献附录列表 格式调整 注意&#xff1a;这里讲的是对齐格式&#xff0c; 文献规范格式参考EndNote X9 快速上手…

发布会彩排哪些内容?要注意哪些细节?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 相信发布会前小伙伴都会进行彩排&#xff0c;对发布会的细节&#xff0c;流程&#xff0c;各个工种如何配合进行提前的演练&#xff0c;那么发布会彩排哪些内容&#xff0c;要注意哪些细…

数据结构-图的创建与深度优先遍历DFS(邻接矩阵-动态类型)

图的创建&#xff1a; 我们先构建一个无向图&#xff1a;如图所示 根据规定&#xff0c;如果两个顶点相连&#xff0c;则两顶点的边改为1&#xff0c;否则为0&#xff0c;我们用数组指针arcs来指向标记是否有边的数组。 1.先创建结构体&#xff0c;因为都为动态所以我们都先定…

RabbitMQ详解(四):SpringBoot整合MQ

SpringBoot整合MQ 需要创建两个springboot项目&#xff0c;一个springboot_rabbitmq_producer生产者&#xff0c;一个springboot_rabbitmq_consumer消费者 fanout模式&#xff08;配置文件方式&#xff09; 定义生产者 创建生产者工程 springboot_rabbitmq_producer pom.x…

DragonflyDB 安装使用

前言 全世界最快的内存数据库 Dragonfly是一种针对现代应用程序负荷需求而构建的内存数据库&#xff0c;完全兼容Redis和Memcached的 API&#xff0c;迁移时无需修改任何代码。相比于这些传统的内存数据库&#xff0c;Dragonfly提供了其25倍的吞吐量&#xff0c;高缓存命中率和…

演化博弈模型简介

演化博弈模型简介 文章目录 演化博弈模型简介[toc]1 演化博弈思想2 演化博弈关注的问题3 复制动态中的博弈 1 演化博弈思想 传统博弈苛刻假设&#xff1a; 完全理性完全信息 演化博弈论&#xff1a;演化博弈论(Evolutionary Game Theory)把博弈理论分析和动态演化过程分析结…

【python】Pandas库用法详解!

pandas 是基于NumPy 的一种工具&#xff0c;该工具是为解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型&#xff0c;提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现&#xff0c;它是使Py…