[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_PL-DDR篇连载-05uifdma_dbuf 3.0 IP 介绍

文档创建者:FPGA课程
浏览次数:281
最后更新:2024-09-10
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 2-FPGA PL DDR使用
​ 软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!



1概述
6547d43c47ab40ca8f4e2d2ed09d52a0.jpg
uifdma_dbuf IP是米联客研发用于配合FDMA完成数据传输控制的IP模块。FDMA-DBUF IP代码采用“对称设计”方法,读写代码对称,好处是代码结构清晰,读写过程一致,代码效率高,更加容易维护。本文只用到了写通道部分,读通道部分没有用到,但是我们还是介绍下。
uifdma_dbuf的信号接口包含了:
AXI-LITE接口:用于PS端获取当前的中断帧号;
FDMA_M接口:和FDMA相连接的数据接口
ud_wx接口: uifdma_dbuf写数据通路接口
ud_rx接口: uifdma_dbuf读数据通路接口
以下只介绍uifdma_dbuf 的ud写数据通道和ud读数据通道
2uifdma_dbuf 的ud信号定义
写数据接口信号定义
                        
信号名称
                        
                        
方向
                        
                        
位宽
                        
                        
功能描述
                        
                        
ud_wclk
                        
                        
input
                        
                        
1
                        
                        
写数据通路时钟
                        
                        
ud_wvs
                        
                        
input
                        
                        
1
                        
                        
写数据帧同步信号,当使能视频功能,每个ud_wvs的上升沿进行帧同步,否则该值设置为1
                        
                        
ud_wde
                        
                        
input
                        
                        
1
                        
                        
写数据数据有效,高电平有效
                        
                        
ud_wdata
                        
                        
input
                        
                        
32~128
                        
                        
写数据
                        
                        
ud_wfull
                        
                        
output
                        
                        
1
                        
                        
写数据FIFO满,当FIFO满,继续写入会导致数据溢出
                        
                        
wbuf_sync_o
                        
                        
output
                        
                        
7
                        
                        
写数据帧同步输出,代表了正在操作的缓存号
                        
                        
wbuf_sync_i
                        
                        
input
                        
                        
7
                        
                        
写数据帧同步输入, 帧同步关系到内部缓存地址切换
                        
                        
fdma_wbuf
                        
                        
output
                        
                        
7
                        
                        
当fdma_wirq中断产生后,代表了当前已经写到DDR完成的帧缓存号
                        
                        
fdma_wirq
                        
                        
output
                        
                        
1
                        
                        
写数据完成中断
                        
读数据接口信号定义
                        
信号名称
                        
                        
方向
                        
                        
位宽
                        
                        
功能描述
                        
                        
ud_rclk
                        
                        
input
                        
                        
1
                        
                        
读数据通路时钟
                        
                        
ud_rvs
                        
                        
input
                        
                        
1
                        
                        
读数据帧同步信号,当使能视频功能,每个ud_rvs的上升沿进行帧同步,否则该值设置为1
                        
                        
ud_rde
                        
                        
input
                        
                        
1
                        
                        
读数据数据有效,高电平有效
                        
                        
ud_rdata
                        
                        
input
                        
                        
32~128
                        
                        
读数据
                        
                        
ud_rempty
                        
                        
output
                        
                        
1
                        
                        
读数据FIFO空,当FIFO非空,可以读出数据,当fdma工作在非视频模式下,可以用该信号去使能ud_rde
                        
                        
rbuf_sync_o
                        
                        
output
                        
                        
7
                        
                        
读数据帧同步输出 ,代表了正在操作的缓存号
                        
                        
rbuf_sync_i
                        
                        
input
                        
                        
7
                        
                        
读数据帧同步输入, 帧同步关系到内部缓存地址切换
                        
                        
fdma_rbuf
                        
                        
output
                        
                        
7
                        
                        
当fdma_rirq中断产生后,代表了当前已经读到DDR完成的帧缓存号
                        
                        
fdma_rirq
                        
                        
output
                        
                        
                        
                        
读数据完成中断
                        
3FDMA-DBUF IP代码分析
FDMA-DBUF IP代码采用“对称设计”方法,读写代码对称,好处是代码结构清晰,读写过程一致,代码效率高,更加容易维护。
1:FDMA-DBUF写状态机
为了配合AXI-FDMA IP发送数据到PS,我们写了一个uifdmadbuf ip,通过这个IP把用户编写的数据时序,转为AXI-FMDA接口数据流。该IP支持视频格式的帧同步,每一帧都进行同步,也支持没有帧同步的数据流方式传输。

704ecb0d1e1f43e8b88b01e793d448db.jpg
2:FDMA的写时序波形图
fc7c8a1add874721aa98f4f907a873b2.jpg
fdma_wready设置为1,当fdma_wbusy=0的时候代表FDMA的总线非忙,可以进行一次新的FDMA传输,这个时候可以设置fdma_wreq=1,同时设置fdma burst的起始地址和fdma_wsize本次需要传输的数据大小(以bytes为单位)。当fdma_wvalid=1的时候需要给出有效的数据,写入AXI总线。当最后一个数写完后,fdma_wvalid和fdma_wbusy变为0。
AXI4总线最大的burst lenth是256,而经过封装后,用户接口的fdma_size可以任意大小的,fdma ip内部代码控制每次AXI4总线的Burst长度,这样极大简化了AXI4总线协议的使用。
3:FDMA-DBUF写状态机
读数据的过程和写数据的过程是对称的,状态机如下:
464876c0323a42998efd87746090dc17.jpg
为了配合AXI-FDMA IP发送数据到PS,我们写了一个uifdmadbuf ip,通过这个IP把用户编写的数据时序,转为AXI-FMDA接口数据流。该IP支持视频格式的帧同步,每一帧都进行同步,也支持没有帧同步的数据流方式传输。
4:FDMA的读时序波形图
0c173908a0d64e4e948a5483ad993fe2.jpg
fdma_rready设置为1,当fdma_rbusy=0的时候代表FDMA的总线非忙,可以进行一次新的FDMA传输,这个时候可以设置fdma_rreq=1,同时设置fdma burst的起始地址和fdma_rsize本次需要传输的数据大小(以bytes为单位)。当fdma_rvalid=1的时候需要给出有效的数据,写入AXI总线。当最后一个数写完后,fdma_rvalid和fdma_rbusy变为0。
同样对于AXI4总线的读操作,AXI4总线最大的burst lenth是256,而经过封装后,用户接口的fdma_size可以任意大小的,fdma ip内部代码控制每次AXI4总线的Burst长度,这样极大简化了AXI4总线协议的使用。
4源码分析
米联客自定义IP路径在配套的soc_prj/uisrc/03_ip路径下,如下图所示:
658142cac4064b1eb2c59bdc3d9d4f4b.jpg
代码的层次结构如下:
f769b54c1e314e859ba1e06e531c03c4.jpg
源码部分一共有包括4个文件:
fs_cap.v该文件用于帧同步信号的抓取,采样边沿抓取方式。
uidbufirq.v文件用来保存一帧数据发送完毕后产生的中断,ps部分可以通过axi-lite接口读取中断值知道哪一个地址完成数据传输。
uidbuf.v文件是完成用户数据到FDMA接口数据转换的关键代码,同时该代码完成了中断控制,帧缓存控制。
uifdma_dbuf.v该文件是该IP的顶层文件
1:uidbuf.v
  1. /*******************************MILIANKE*******************************
  2. *Company : MiLianKe Electronic Technology Co., Ltd.
  3. *WebSite:https://www.milianke.com
  4. *TechWeb:https://www.uisrc.com
  5. *tmall-shop:https://milianke.tmall.com
  6. *jd-shop:https://milianke.jd.com
  7. *taobao-shop1: https://milianke.taobao.com
  8. *Create Date: 2021/10/15
  9. *File Name: uidbuf.v
  10. *Description:
  11. *Declaration:
  12. *The reference demo provided by Milianke is only used for learning.
  13. *We cannot ensure that the demo itself is free of bugs, so users
  14. *should be responsible for the technical problems and consequences
  15. *caused by the use of their own products.
  16. *Copyright: Copyright (c) MiLianKe
  17. *All rights reserved.
  18. *Revision: 3.1
  19. *Signal description
  20. *1) I_ input
  21. *2) O_ output
  22. *3) IO_ input output
  23. *4) S_ system internal signal
  24. *5) _n activ low
  25. *6) _dg debug signal
  26. *7) _r delay or register
  27. *8) _s state mechine
  28. *********************************************************************/

  29. /*********uidbuf(fdma data buffer controller)基于FDMA信号时序的缓存控制器***********
  30. --以下是米联客设计的uidbuf(fdma data buffer controller)基于FDMA信号时序的缓存控制器
  31. --1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨
  32. --2.读写通道独立,FIFO大小,数据位宽可以灵活设置,适合用于基于RGB时序的视频数据或者数据流传输
  33. 版本说明:
  34. --3.1 修改信号命名规则
  35. *********************************************************************/

  36. `timescale 1ns / 1ns

  37. module uidbuf#(
  38. parameter  integer                   VIDEO_ENABLE   = 1,//使能视频帧支持功能
  39. parameter  integer                   ENABLE_WRITE   = 1,//使能写通道
  40. parameter  integer                   ENABLE_READ    = 1,//使能读通道

  41. parameter  integer                   AXI_DATA_WIDTH = 128,//AXI总线数据位宽
  42. parameter  integer                   AXI_ADDR_WIDTH = 32, //AXI总线地址位宽

  43. parameter  integer                   W_BUFDEPTH     = 2048, //写通道AXI设置FIFO缓存大小
  44. parameter  integer                   W_DATAWIDTH    = 32,  //写通道AXI设置数据位宽大小
  45. parameter  [AXI_ADDR_WIDTH -1'b1: 0] W_BASEADDR     = 0, //写通道设置内存起始地址
  46. parameter  integer                   W_DSIZEBITS    = 24, //写通道设置缓存数据的增量地址大小,用于FDMA DBUF 计算帧缓存起始地址
  47. parameter  integer                   W_XSIZE        = 1920, //写通道设置X方向的数据大小,代表了每次FDMA 传输的数据长度
  48. parameter  integer                   W_XSTRIDE      = 1920, //写通道设置X方向的Stride值,主要用于图形缓存应用
  49. parameter  integer                   W_YSIZE        = 1080, //写通道设置Y方向值,代表了进行了多少次XSIZE传输
  50. parameter  integer                   W_XDIV         = 2, //写通道对X方向数据拆分为XDIV次传输,减少FIFO的使用
  51. parameter  integer                   W_BUFSIZE      = 3, //写通道设置帧缓存大小,目前最大支持128帧,可以修改参数支持更缓存数

  52. parameter  integer                   R_BUFDEPTH     = 2048, //读通道AXI设置FIFO缓存大小
  53. parameter  integer                   R_DATAWIDTH    = 32, //读通道AXI设置数据位宽大小
  54. parameter  [AXI_ADDR_WIDTH -1'b1: 0] R_BASEADDR     = 0, //读通道设置内存起始地址
  55. parameter  integer                   R_DSIZEBITS    = 24, //读通道设置缓存数据的增量地址大小,用于FDMA DBUF 计算帧缓存起始地址
  56. parameter  integer                   R_XSIZE        = 1920, //读通道设置X方向的数据大小,代表了每次FDMA 传输的数据长度
  57. parameter  integer                   R_XSTRIDE      = 1920, //读通道设置X方向的Stride值,主要用于图形缓存应用
  58. parameter  integer                   R_YSIZE        = 1080, //读通道设置Y方向值,代表了进行了多少次XSIZE传输
  59. parameter  integer                   R_XDIV         = 2, //读通道对X方向数据拆分为XDIV次传输,减少FIFO的使用
  60. parameter  integer                   R_BUFSIZE      = 3 //读通道设置帧缓存大小,目前最大支持128帧,可以修改参数支持更缓存数
  61. )
  62. (
  63. input wire                                  I_ui_clk, //和FDMA AXI总线时钟一致
  64. input wire                                  I_ui_rstn, //和FDMA AXI复位一致
  65. //sensor input -W_FIFO--------------
  66. input wire                                  I_W_wclk, //用户写数据接口时钟
  67. input wire                                  I_W_FS, //用户写数据接口同步信号,对于非视频帧一般设置为1
  68. input wire                                  I_W_wren, //用户写数据使能
  69. input wire     [W_DATAWIDTH-1'b1 : 0]       I_W_data, //用户写数据
  70. output reg     [7   :0]                     O_W_sync_cnt =0, //写通道BUF帧同步输出
  71. input  wire    [7   :0]                     I_W_buf, // 写通道BUF帧同步输入
  72. output wire                                 O_W_full,

  73. //----------fdma signals write-------      
  74. output wire    [AXI_ADDR_WIDTH-1'b1: 0]     O_fdma_waddr, //FDMA写通道地址
  75. output wire                                 O_fdma_wareq, //FDMA写通道请求
  76. output wire    [15  :0]                     O_fdma_wsize, //FDMA写通道一次FDMA的传输大小                                    
  77. input  wire                                 I_fdma_wbusy, //FDMA处于BUSY状态,AXI总线正在写操作   
  78. output wire    [AXI_DATA_WIDTH-1'b1:0]      O_fdma_wdata, //FDMA写数据
  79. input  wire                                 I_fdma_wvalid, //FDMA 写有效
  80. output wire                                 O_fdma_wready, //FDMA写准备好,用户可以写数据
  81. output reg     [7   :0]                     O_fdma_wbuf =0, //FDMA的写帧缓存号输出
  82. output wire                                 O_fdma_wirq, //FDMA一次写完成的数据传输完成后,产生中断。   
  83. //----------fdma signals read-------  
  84. input  wire                                 I_R_rclk, //用户读数据接口时钟
  85. input  wire                                 I_R_FS, //用户读数据接口同步信号,对于非视频帧一般设置1
  86. input  wire                                 I_R_rden, //用户读数据使能
  87. output wire    [R_DATAWIDTH-1'b1 : 0]       O_R_data, //用户读数据
  88. output reg     [7   :0]                     O_R_sync_cnt =0, //读通道BUF帧同步输出
  89. input  wire    [7   :0]                     I_R_buf, //写通道BUF帧同步输入
  90. output wire                                 O_R_empty,

  91. output wire    [AXI_ADDR_WIDTH-1'b1: 0]     O_fdma_raddr, // FDMA读通道地址
  92. output wire                                 O_fdma_rareq, // FDMA读通道请求
  93. output wire    [15: 0]                      O_fdma_rsize, // FDMA读通道一次FDMA的传输大小                                    
  94. input  wire                                 I_fdma_rbusy, // FDMA处于BUSY状态,AXI总线正在读操作     
  95. input  wire    [AXI_DATA_WIDTH-1'b1:0]      I_fdma_rdata, // FDMA读数据
  96. input  wire                                 I_fdma_rvalid, // FDMA 读有效
  97. output wire                                 O_fdma_rready, // FDMA读准备好,用户可以读数据
  98. output reg     [7  :0]                      O_fdma_rbuf =0, // FDMA的读帧缓存号输出
  99. output wire                                 O_fdma_rirq // FDMA一次读完成的数据传输完成后,产生中断
  100. );   

  101. // 计算Log2
  102. function integer clog2;
  103.   input integer value;
  104.   begin
  105.     for (clog2=0; value>0; clog2=clog2+1)
  106.       value = value>>1;
  107.     end
  108.   endfunction

  109. //FDMA读写状态机的状态值,一般4个状态值即可
  110. localparam S_IDLE  =  2'd0;  
  111. localparam S_RST   =  2'd1;  
  112. localparam S_DATA1 =  2'd2;   
  113. localparam S_DATA2 =  2'd3;

  114. // 通过设置通道使能,可以优化代码的利用率
  115. generate  if(ENABLE_WRITE == 1)begin : FDMA_WRITE_ENABLE

  116. localparam WFIFO_DEPTH = W_BUFDEPTH; //写通道FIFO深度
  117. localparam W_WR_DATA_COUNT_WIDTH = clog2(WFIFO_DEPTH); //计算FIFO的写通道位宽
  118. localparam W_RD_DATA_COUNT_WIDTH = clog2(WFIFO_DEPTH*W_DATAWIDTH/AXI_DATA_WIDTH);//clog2(WFIFO_DEPTH/(AXI_DATA_WIDTH/W_DATAWIDTH))+1;

  119. localparam WYBUF_SIZE           = (W_BUFSIZE - 1'b1); //写通道需要完成多少次XSIZE操作
  120. localparam WY_BURST_TIMES       = (W_YSIZE*W_XDIV); //写通道需要完成的FDMA burst 操作次数,XDIV用于把XSIZE分解多次传输
  121. localparam FDMA_WX_BURST        = (W_XSIZE*W_DATAWIDTH/AXI_DATA_WIDTH)/W_XDIV; //FDMA BURST 一次的大小
  122. localparam WX_BURST_ADDR_INC    = (W_XSIZE*(W_DATAWIDTH/8))/W_XDIV; //FDMA每次burst之后的地址增加
  123. localparam WX_LAST_ADDR_INC     = (W_XSTRIDE-W_XSIZE)*(W_DATAWIDTH/8) + WX_BURST_ADDR_INC; //根据stride值计算出来最后一次地址

  124. (*mark_debug = "true"*) (* KEEP = "TRUE" *) wire  W_wren_ri = I_W_wren;

  125. assign                                  O_fdma_wready = 1'b1;
  126. reg                                     O_fdma_wareq_r= 1'b0;
  127. reg                                     W_FIFO_Rst=0;
  128. (*mark_debug = "true"*) (* KEEP = "TRUE" *)wire                                    W_FS;
  129. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [1 :0]                              W_MS=0;
  130. reg [W_DSIZEBITS-1'b1:0]                W_addr=0;
  131. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [15:0]                              W_bcnt=0;
  132. (*mark_debug = "true"*) (* KEEP = "TRUE" *)wire[W_RD_DATA_COUNT_WIDTH-1'b1 :0]     W_rcnt;
  133. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg                                     W_REQ=0;
  134. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [5 :0]                              wirq_dly_cnt =0;
  135. reg [3 :0]                              wdiv_cnt =0;
  136. reg [7 :0]                              wrst_cnt =0;
  137. reg [7 :0]                              O_fdma_wbufn;

  138. (*mark_debug = "true"*) (* KEEP = "TRUE" *) wire wirq= O_fdma_wirq;

  139. assign O_fdma_wsize = FDMA_WX_BURST;
  140. assign O_fdma_wirq = (wirq_dly_cnt>0);

  141. assign O_fdma_waddr = W_BASEADDR + {O_fdma_wbufn,W_addr};//由于FPGA逻辑做乘法比较复杂,因此通过设置高位地址实现缓存设置

  142. reg [1:0] W_MS_r =0;
  143. always @(posedge I_ui_clk) W_MS_r <= W_MS;

  144. //每次FDMA DBUF 完成一帧数据传输后,产生中断,这个中断持续60个周期的uiclk,这里的延迟必须足够ZYNQ IP核识别到这个中断
  145. always @(posedge I_ui_clk) begin
  146.     if(I_ui_rstn == 1'b0)begin
  147.         wirq_dly_cnt <= 6'd0;
  148.         O_fdma_wbuf <=0;
  149.     end
  150.     else if((W_MS_r == S_DATA2) && (W_MS == S_IDLE))begin
  151.         wirq_dly_cnt <= 60;
  152.         O_fdma_wbuf <= O_fdma_wbufn;
  153.     end
  154.     else if(wirq_dly_cnt >0)
  155.         wirq_dly_cnt <= wirq_dly_cnt - 1'b1;
  156. end

  157. //帧同步,对于视频有效
  158. fs_cap #
  159. (
  160. .VIDEO_ENABLE(VIDEO_ENABLE)
  161. )
  162. fs_cap_W0
  163. (
  164. .I_clk(I_ui_clk),
  165. .I_rstn(I_ui_rstn),
  166. .I_vs(I_W_FS),
  167. .O_fs_cap(W_FS)
  168. );

  169. assign O_fdma_wareq = O_fdma_wareq_r;

  170. //写通道状态机,采用4个状态值描述
  171. always @(posedge I_ui_clk) begin
  172.     if(!I_ui_rstn)begin
  173.         W_MS         <= S_IDLE;
  174.         W_FIFO_Rst   <= 0;
  175.         W_addr       <= 0;
  176.         O_W_sync_cnt <= 0;
  177.         W_bcnt       <= 0;
  178.         wrst_cnt     <= 0;
  179.         wdiv_cnt     <= 0;
  180.         O_fdma_wbufn    <= 0;
  181.         O_fdma_wareq_r <= 1'd0;
  182.     end   
  183.     else begin
  184.       case(W_MS)
  185.         S_IDLE:begin
  186.           W_addr <= 0;
  187.           W_bcnt <= 0;
  188.           wrst_cnt <= 0;
  189.           wdiv_cnt <=0;
  190.           if(W_FS) begin //帧同步,对于非视频数据一般常量为1
  191.             W_MS <= S_RST;
  192.             if(O_W_sync_cnt < WYBUF_SIZE) //输出帧同步计数器
  193.                 O_W_sync_cnt <= O_W_sync_cnt + 1'b1;
  194.             else
  195.                 O_W_sync_cnt <= 0;  
  196.           end
  197.        end
  198.        S_RST:begin//帧同步,对于非视频数据直接跳过,对于视频数据,会同步每一帧,并且复位数据FIFO
  199.            O_fdma_wbufn <= I_W_buf;
  200.            wrst_cnt <= wrst_cnt + 1'b1;
  201.            if((VIDEO_ENABLE == 1) && (wrst_cnt < 40))
  202.                 W_FIFO_Rst <= 1;
  203.            else if((VIDEO_ENABLE == 1) && (wrst_cnt < 100))
  204.                 W_FIFO_Rst <= 0;
  205.            else if(O_fdma_wirq == 1'b0) begin
  206.                 W_MS <= S_DATA1;
  207.            end
  208.        end
  209.         S_DATA1:begin //发送写FDMA请求
  210.           if(I_fdma_wbusy == 1'b0 && W_REQ )begin
  211.              O_fdma_wareq_r  <= 1'b1;
  212.           end
  213.           else if(I_fdma_wbusy == 1'b1) begin
  214.              O_fdma_wareq_r  <= 1'b0;
  215.              W_MS    <= S_DATA2;
  216.           end         
  217.          end
  218.         S_DATA2:begin //写有效数据
  219.             if(I_fdma_wbusy == 1'b0)begin
  220.                 if(W_bcnt == WY_BURST_TIMES - 1'b1) //判断是否传输完毕
  221.                     W_MS <= S_IDLE;
  222.                 else begin
  223.                     if(wdiv_cnt < W_XDIV - 1'b1)begin//如果对XSIZE做了分次传输,一个XSIZE也需要XDIV次FDMA完成传输
  224.                         W_addr <= W_addr +  WX_BURST_ADDR_INC;  //计算地址增量
  225.                         wdiv_cnt <= wdiv_cnt + 1'b1;
  226.                      end
  227.                     else begin
  228.                         W_addr <= W_addr + WX_LAST_ADDR_INC; //计算最后一次地址增量,最后一次地址根据stride 计算
  229.                         wdiv_cnt <= 0;
  230.                     end
  231.                     W_bcnt <= W_bcnt + 1'b1;
  232.                     W_MS    <= S_DATA1;
  233.                 end
  234.             end
  235.          end
  236.          default: W_MS <= S_IDLE;
  237.        endcase
  238.     end
  239. end

  240. //写通道的数据FIFO,采用了原语调用xpm_fifo_async fifo,当FIFO存储的数据阈值达到一定量,一般满足一次FDMA的burst即可发出请求
  241. wire W_rbusy;
  242. always@(posedge I_ui_clk)     
  243.      W_REQ  <= (W_rcnt > FDMA_WX_BURST - 2)&&(~W_rbusy);

  244. xpm_fifo_async # (
  245.   .FIFO_MEMORY_TYPE          ("auto"),           //string; "auto", "block", or "distributed";
  246.   .ECC_MODE                  ("no_ecc"),         //string; "no_ecc" or "en_ecc";
  247.   .RELATED_CLOCKS            (0),                //positive integer; 0 or 1
  248.   .FIFO_WRITE_DEPTH          (WFIFO_DEPTH),     //positive integer
  249.   .WRITE_DATA_WIDTH          (W_DATAWIDTH),               //positive integer
  250.   .WR_DATA_COUNT_WIDTH       (W_WR_DATA_COUNT_WIDTH),               //positive integer
  251.   .PROG_FULL_THRESH          (20),               //positive integer
  252.   .FULL_RESET_VALUE          (0),                //positive integer; 0 or 1
  253.   .USE_ADV_FEATURES          ("0707"),           //string; "0000" to "1F1F";
  254.   .READ_MODE                 ("fwft"),            //string; "std" or "fwft";
  255.   .FIFO_READ_LATENCY         (0),                //positive integer;
  256.   .READ_DATA_WIDTH           (AXI_DATA_WIDTH),               //positive integer
  257.   .RD_DATA_COUNT_WIDTH       (W_RD_DATA_COUNT_WIDTH),               //positive integer
  258.   .PROG_EMPTY_THRESH         (10),               //positive integer
  259.   .DOUT_RESET_VALUE          ("0"),              //string
  260.   .CDC_SYNC_STAGES           (2),                //positive integer
  261.   .WAKEUP_TIME               (0)                 //positive integer; 0 or 2;
  262. ) xpm_fifo_W_inst (
  263.       .rst              ((I_ui_rstn == 1'b0) || (W_FIFO_Rst == 1'b1)),
  264.       .wr_clk           (I_W_wclk),
  265.       .wr_en            (I_W_wren),
  266.       .din              (I_W_data),
  267.       .full             (O_W_full),
  268.       .overflow         (),
  269.       .prog_full        (),
  270.       .wr_data_count    (),
  271.       .almost_full      (),
  272.       .wr_ack           (),
  273.       .wr_rst_busy      (),
  274.       .rd_clk           (I_ui_clk),
  275.       .rd_en            (I_fdma_wvalid),
  276.       .dout             (O_fdma_wdata),
  277.       .empty            (),
  278.       .underflow        (),
  279.       .rd_rst_busy      (W_rbusy),
  280.       .prog_empty       (),
  281.       .rd_data_count    (W_rcnt),
  282.       .almost_empty     (),
  283.       .data_valid       (),
  284.       .sleep            (1'b0),
  285.       .injectsbiterr    (1'b0),
  286.       .injectdbiterr    (1'b0),
  287.       .sbiterr          (),
  288.       .dbiterr          ()

  289. );

  290. end
  291. else begin : FDMA_WRITE_DISABLE

  292. //----------fdma signals write-------      
  293. assign O_fdma_waddr = 0;
  294. assign O_fdma_wareq = 0;
  295. assign O_fdma_wsize = 0;                                    
  296. assign O_fdma_wdata = 0;
  297. assign O_fdma_wready = 0;
  298. assign O_fdma_wirq = 0;
  299. assign O_W_full = 0;

  300. end
  301. endgenerate

  302. generate  if(ENABLE_READ == 1)begin : FDMA_READ// 通过设置通道使能,可以优化代码的利用率
  303. localparam RYBUF_SIZE           = (R_BUFSIZE - 1'b1); //读通道需要完成多少次XSIZE操作
  304. localparam RY_BURST_TIMES       = (R_YSIZE*R_XDIV); //读通道需要完成的FDMA burst 操作次数,XDIV用于把XSIZE分解多次传输
  305. localparam FDMA_RX_BURST        = (R_XSIZE*R_DATAWIDTH/AXI_DATA_WIDTH)/R_XDIV; //FDMA BURST 一次的大小
  306. localparam RX_BURST_ADDR_INC    = (R_XSIZE*(R_DATAWIDTH/8))/R_XDIV; //FDMA每次burst之后的地址增加
  307. localparam RX_LAST_ADDR_INC     = (R_XSTRIDE-R_XSIZE)*(R_DATAWIDTH/8) + RX_BURST_ADDR_INC; //根据stride值计算出来最后一次地址

  308. localparam RFIFO_DEPTH = R_BUFDEPTH*R_DATAWIDTH/AXI_DATA_WIDTH;//R_BUFDEPTH/(AXI_DATA_WIDTH/R_DATAWIDTH);
  309. localparam R_WR_DATA_COUNT_WIDTH = clog2(RFIFO_DEPTH); //读通道FIFO 输入部分深度
  310. localparam R_RD_DATA_COUNT_WIDTH = clog2(R_BUFDEPTH); //写通道FIFO输出部分深度

  311. assign                                  O_fdma_rready = 1'b1;
  312. reg                                     O_fdma_rareq_r= 1'b0;
  313. reg                                     R_FIFO_Rst=0;
  314. wire                                    R_FS;
  315. reg [1 :0]                              R_MS=0;
  316. reg [R_DSIZEBITS-1'b1:0]                R_addr=0;
  317. reg [15:0]                              R_bcnt=0;
  318. wire[R_WR_DATA_COUNT_WIDTH-1'b1 :0]     R_wcnt;
  319. reg                                     R_REQ=0;
  320. reg [5 :0]                              rirq_dly_cnt =0;
  321. reg [3 :0]                              rdiv_cnt =0;
  322. reg [7 :0]                              rrst_cnt =0;
  323. reg [7 :0]                              O_fdma_rbufn;
  324. assign O_fdma_rsize = FDMA_RX_BURST;
  325. assign O_fdma_rirq = (rirq_dly_cnt>0);

  326. assign O_fdma_raddr = R_BASEADDR + {O_fdma_rbufn,R_addr};//由于FPGA逻辑做乘法比较复杂,因此通过设置高位地址实现缓存设置

  327. reg [1:0] R_MS_r =0;
  328. always @(posedge I_ui_clk) R_MS_r <= R_MS;

  329. //每次FDMA DBUF 完成一帧数据传输后,产生中断,这个中断持续60个周期的uiclk,这里的延迟必须足够ZYNQ IP核识别到这个中断
  330. always @(posedge I_ui_clk) begin
  331.     if(I_ui_rstn == 1'b0)begin
  332.         rirq_dly_cnt <= 6'd0;
  333.         O_fdma_rbuf <=0;
  334.     end
  335.     else if((R_MS_r == S_DATA2) && (R_MS == S_IDLE))begin
  336.         rirq_dly_cnt <= 60;
  337.         O_fdma_rbuf <= O_fdma_rbufn;
  338.     end
  339.     else if(rirq_dly_cnt >0)
  340.         rirq_dly_cnt <= rirq_dly_cnt - 1'b1;
  341. end

  342. //帧同步,对于视频有效
  343. fs_cap #
  344. (
  345. .VIDEO_ENABLE(VIDEO_ENABLE)
  346. )
  347. fs_cap_R0
  348. (
  349.   .I_clk(I_ui_clk),
  350.   .I_rstn(I_ui_rstn),
  351.   .I_vs(I_R_FS),
  352.   .O_fs_cap(R_FS)
  353. );

  354. assign O_fdma_rareq = O_fdma_rareq_r;

  355. //读通道状态机,采用4个状态值描述
  356. always @(posedge I_ui_clk) begin
  357.    if(!I_ui_rstn)begin
  358.         R_MS          <= S_IDLE;
  359.         R_FIFO_Rst   <= 0;
  360.         R_addr       <= 0;
  361.         O_R_sync_cnt <= 0;
  362.         R_bcnt       <= 0;
  363.         rrst_cnt     <= 0;
  364.         rdiv_cnt      <= 0;
  365.         O_fdma_rbufn    <= 0;
  366.         O_fdma_rareq_r  <= 1'd0;
  367.     end   
  368.     else begin
  369.       case(R_MS) //帧同步,对于非视频数据一般常量为1
  370.         S_IDLE:begin
  371.           R_addr <= 0;
  372.           R_bcnt <= 0;
  373.           rrst_cnt <= 0;
  374.           rdiv_cnt <=0;
  375.           if(R_FS) begin
  376.             R_MS <= S_RST;
  377.             if(O_R_sync_cnt < RYBUF_SIZE) //输出帧同步计数器,当需要用读通道做帧同步的时候使用
  378.                 O_R_sync_cnt <= O_R_sync_cnt + 1'b1;
  379.             else
  380.                 O_R_sync_cnt <= 0;  
  381.           end
  382.        end
  383.        S_RST:begin//帧同步,对于非视频数据直接跳过,对于视频数据,会同步每一帧,并且复位数据FIFO
  384.            O_fdma_rbufn <= I_R_buf;
  385.            rrst_cnt <= rrst_cnt + 1'b1;
  386.            if((VIDEO_ENABLE == 1) && (rrst_cnt < 40))
  387.                 R_FIFO_Rst <= 1;
  388.            else if((VIDEO_ENABLE == 1) && (rrst_cnt < 100))
  389.                 R_FIFO_Rst <= 0;
  390.            else if(O_fdma_rirq == 1'b0) begin
  391.                 R_MS <= S_DATA1;
  392.            end
  393.        end
  394.        S_DATA1:begin
  395.          if(I_fdma_rbusy == 1'b0 && R_REQ)begin
  396.             O_fdma_rareq_r  <= 1'b1;  
  397.          end
  398.          else if(I_fdma_rbusy == 1'b1) begin
  399.             O_fdma_rareq_r  <= 1'b0;
  400.             R_MS    <= S_DATA2;
  401.          end         
  402.         end
  403.         S_DATA2:begin //写有效数据
  404.             if(I_fdma_rbusy == 1'b0)begin
  405.                 if(R_bcnt == RY_BURST_TIMES - 1'b1) //判断是否传输完毕
  406.                     R_MS <= S_IDLE;
  407.                 else begin
  408.                     if(rdiv_cnt < R_XDIV - 1'b1)begin//如果对XSIZE做了分次传输,一个XSIZE也需要XDIV次FDMA完成传输
  409.                         R_addr <= R_addr +  RX_BURST_ADDR_INC;  //计算地址增量
  410.                         rdiv_cnt <= rdiv_cnt + 1'b1;
  411.                      end
  412.                     else begin
  413.                         R_addr <= R_addr + RX_LAST_ADDR_INC; //计算最后一次地址增量,最后一次地址根据stride 计算
  414.                         rdiv_cnt <= 0;
  415.                     end
  416.                     R_bcnt <= R_bcnt + 1'b1;
  417.                     R_MS    <= S_DATA1;
  418.                 end
  419.             end
  420.          end
  421.          default:R_MS <= S_IDLE;
  422.       endcase
  423.    end
  424. end

  425. //读通道的数据FIFO,采用了原语调用xpm_fifo_async fifo,当FIFO存储空间有足够空余,满足一次FDMA的burst即可发出请求
  426. wire R_wbusy;
  427. always@(posedge I_ui_clk)      
  428.      R_REQ  <= (R_wcnt < FDMA_RX_BURST - 2)&&(~R_wbusy);

  429. xpm_fifo_async # (
  430.   .FIFO_MEMORY_TYPE          ("auto"),           //string; "auto", "block", or "distributed";
  431.   .ECC_MODE                  ("no_ecc"),         //string; "no_ecc" or "en_ecc";
  432.   .RELATED_CLOCKS            (0),                //positive integer; 0 or 1
  433.   .FIFO_WRITE_DEPTH          (RFIFO_DEPTH),     //positive integer
  434.   .WRITE_DATA_WIDTH          (AXI_DATA_WIDTH),               //positive integer
  435.   .WR_DATA_COUNT_WIDTH       (R_WR_DATA_COUNT_WIDTH),               //positive integer
  436.   .PROG_FULL_THRESH          (20),               //positive integer
  437.   .FULL_RESET_VALUE          (0),                //positive integer; 0 or 1
  438.   .USE_ADV_FEATURES          ("0707"),           //string; "0000" to "1F1F";
  439.   .READ_MODE                 ("fwft"),            //string; "std" or "fwft";
  440.   .FIFO_READ_LATENCY         (0),                //positive integer;
  441.   .READ_DATA_WIDTH           (R_DATAWIDTH),               //positive integer
  442.   .RD_DATA_COUNT_WIDTH       (R_RD_DATA_COUNT_WIDTH),               //positive integer
  443.   .PROG_EMPTY_THRESH         (10),               //positive integer
  444.   .DOUT_RESET_VALUE          ("0"),              //string
  445.   .CDC_SYNC_STAGES           (2),                //positive integer
  446.   .WAKEUP_TIME               (0)                 //positive integer; 0 or 2;
  447. ) xpm_fifo_R_inst (
  448.       .rst              ((I_ui_rstn == 1'b0) || (R_FIFO_Rst == 1'b1)),
  449.       .wr_clk           (I_ui_clk),
  450.       .wr_en            (I_fdma_rvalid),
  451.       .din              (I_fdma_rdata),
  452.       .full             (),
  453.       .overflow         (),
  454.       .prog_full        (),
  455.       .wr_data_count    (R_wcnt),
  456.       .almost_full      (),
  457.       .wr_ack           (),
  458.       .wr_rst_busy      (R_wbusy),
  459.       .rd_clk           (I_R_rclk),
  460.       .rd_en            (I_R_rden),
  461.       .dout             (O_R_data),
  462.       .empty            (O_R_empty),
  463.       .underflow        (),
  464.       .rd_rst_busy      (),
  465.       .prog_empty       (),
  466.       .rd_data_count    (),
  467.       .almost_empty     (),
  468.       .data_valid       (),
  469.       .sleep            (1'b0),
  470.       .injectsbiterr    (1'b0),
  471.       .injectdbiterr    (1'b0),
  472.       .sbiterr          (),
  473.       .dbiterr          ()

  474. );

  475. end
  476. else begin : FDMA_READ_DISABLE
  477.    
  478. assign O_fdma_raddr = 0;
  479. assign O_fdma_rareq = 0;
  480. assign O_fdma_rsize = 0;                                    
  481. //assign I_fdma_rdata = 0;
  482. assign O_fdma_rready = 0;
  483. assign O_fdma_rirq = 0;
  484. assign O_R_empty   = 1'b0;

  485. end
  486. endgenerate

  487. endmodule
复制代码

3:uidbufirq.v
这个部分的功能只有在PS需要获取中断后的帧缓存通道才会用到,PS通过axi-lite接口可以读取到寄存器的值。
关于更多AXI4总线相关知识可以阅读《3-2-03米联客2024版AXI4总线专题》这个章节,这个章节专讲AXI4总线,其中也详细讲解了FDMA的代码分析。
这里主要看代码的最后,根据中断信号寄存内存的中断号
  1. `timescale 1ns / 1ps
  2. /*******************************MILIANKE*******************************
  3. *Company : MiLianKe Electronic Technology Co., Ltd.
  4. *WebSite:https://www.milianke.com
  5. *TechWeb:https://www.uisrc.com
  6. *tmall-shop:https://milianke.tmall.com
  7. *jd-shop:https://milianke.jd.com
  8. *taobao-shop: https://milianke.taobao.com
  9. *Create Date: 2022/09/25
  10. *Module Name:
  11. *File Name:
  12. *Description:
  13. *The reference demo provided by Milianke is only used for learning.
  14. *We cannot ensure that the demo itself is free of bugs, so users
  15. *should be responsible for the technical problems and consequences
  16. *caused by the use of their own products.
  17. *Copyright: Copyright (c) MiLianKe
  18. *All rights reserved.
  19. *Revision: 3.1
  20. *Signal description
  21. *1) I_ input
  22. *2) O_ output
  23. *3) IO_ input output
  24. *4) S_ system internal signal
  25. *5) _n activ low
  26. *6) _dg debug signal
  27. *7) _r delay or register
  28. *8) _s state mechine
  29. *********************************************************************/

  30. module fs_cap#(
  31. parameter  integer  VIDEO_ENABLE   = 1
  32. )
  33. (
  34. input  I_clk,
  35. input  I_rstn,
  36. input  I_vs,
  37. output reg O_fs_cap
  38. );
  39.    
  40. //--------------------
  41. reg[4:0]CNT_FS   = 6'b0;
  42. reg[4:0]CNT_FS_n = 6'b0;
  43. reg     FS       = 1'b0;
  44. (* ASYNC_REG = "TRUE" *)   reg vs_i_r1;
  45. (* ASYNC_REG = "TRUE" *)   reg vs_i_r2;
  46. (* ASYNC_REG = "TRUE" *)   reg vs_i_r3;
  47. (* ASYNC_REG = "TRUE" *)   reg vs_i_r4;

  48. always@(posedge I_clk) begin
  49.       vs_i_r1 <= I_vs;
  50.       vs_i_r2 <= vs_i_r1;
  51.       vs_i_r3 <= vs_i_r2;
  52.       vs_i_r4 <= vs_i_r3;
  53. end

  54. always@(posedge I_clk) begin
  55.    if(!I_rstn)begin
  56.       O_fs_cap <= 1'd0;
  57.    end
  58.    else if(VIDEO_ENABLE == 1)begin
  59.       if({vs_i_r4,vs_i_r3} == 2'b01)begin
  60.          O_fs_cap <= 1'b1;
  61.       end
  62.       else begin
  63.          O_fs_cap <= 1'b0;
  64.       end
  65.    end
  66.    else begin
  67.          O_fs_cap <= vs_i_r4;
  68.    end
  69. end
  70.         
  71. endmodule
复制代码



您需要登录后才可以回帖 登录 | 立即注册

本版积分规则