[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK高级篇连载-11PL 发数据到 PS 方案(DBUF+FDMA)

文档创建者:FPGA课程
浏览次数:91
最后更新:2024-10-08
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 2-SDK高级应用方案
本帖最后由 FPGA课程 于 2024-10-8 09:08 编辑

​ 软件版本: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概述
前文方案中,通过米联客自定义的AXI4-FDMA IP 可以把PS的DDR当作PL的DDR使用,这不仅仅可以省区PL DDR也可以节省很多PL资源。
本文方案中,通过米联客自定义的uifdmadbuf(数据缓存)IP 结合FDMA IP完成PL 数据发送到PS的应用方案
本文实验目的:
1:掌握uifdmadbuf ip的使用,以及分析uifdmadbuf的源码
2:基于米联客的uifdmadbuf和axi4-fdma完成从PL发送数据到PS DDR
2系统框图
本系统中,测试数据通过FDMA_DBUF ip 写入到AXI-FDMA IP之后通过AXI4总线发送都PS DDR.和官方的DMA IP使用方法不同的是,对于数据在DDR中的起始地址设置、DDR内存中开辟的缓存数量、数据包大小都是FDMA_DBUF中设置,FDMA_DBUF可以自主发起数据的传输,控制内存地址的切换,以及中断的产生。PS获取数据的地址,获取数据的时机都是被动完成。
3a9e5d13265e4fb58920c70fc6fbb6b4.jpg
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“3-1-01米联客2024版ZynqSocSDK入门篇”中第一个工程 “01Vitis Soc开发入门”这个实验。
本文开始不再详细描述图形化工程的搭建过程,对于VIVIADO软件工具使用可以参考前面的相关章节。
3搭建SOC系统工程
3.1Zynq IP PS部分设置
本文中的PS设置内容是新增加的配置部分,关于DDR、MIO、CPU时钟等设置请参考“3-1-01米联客2024版ZynqSocSDK入门篇”中第一个工程 “01Vitis Soc开发入门”这个实验。
1:PS复位设置
54999965f0964fee9035c769c7673dd4.jpg
2:设置 PS GT Master 接口和 HP Slave 接口
7ed3d3fe34e44f3dae42c66439ea9bfe.jpg
3:设置PL到PS的中断
Interurptsà勾选 Fabric Interrupt,勾选IRQ_F2P[15:0]。
a0c44dc8b70b4d1db9b3ed37583dc4d3.jpg
4:设置PL的时钟
勾选FCLK_CLK0,设置为100M AXI-FDMA到AXI4总线的数据时钟
勾选FCLK_CLK1,设置50M 用户数据时钟接到ui_dbuf IP
本系统需要2个时钟完成数据方案传输, FCLK_CLK0大于FCLK_CLK1是为了满足AXI Stream部分的数据吞吐能力要大于用户写数据速度。
afb6e5917c4d4eccba496d989e8225cf.jpg
5:ZYNQ IP设置完成后
40331362e8e0444db8be4e5688a6d48d.jpg
3.2添加IP
首先设置好自定义IP的路径
fbef0a5cf5ec45969be5484c6989687b.jpg
单击添加IP按钮“+”,输入如下模块IP名字的关键词,并双击添加
cb5bc32d8ab6492287ccfeab1bb6b695.jpg
其中ui_fdmabuf ip是米联客自定义IP用于和米联客fdma ip进行数据通信。用同样的方法添加axi-interconnect ip 以及 GPIO IP.
1:uifdma_dbuf参数设置
1056cbfdf2cb42aeb2e18b1a72ac71e8.jpg
2:uifdma参数设置
e29394eda28843c4a870f5d4ac637214.jpg
3:axi-gpio参数设置
e70b9d891efc46b1a3f03ed042e976f0.jpg
4723e77ffc0a402d85a8f9ea779beae3.jpg
3.3PL图形化编程
用户可以根据自己的习惯搭建以下工程,演示的方法为了让初学者看到过程,但不是效率最高,最优化的方法。
b5718dd39c264d4fa34539c13f93d3c9.jpg
3.4添加测试数据代码
新建顶层文件system_wrapper_top.v,并且添加代码,我们先看下添加好的代码和层次结构
80a01296bc74456ba6a1fa5f335f0452.jpg
可以看到system_wrapper_top.v中调用了刚才设计的图形化FPGA代码,具体源码如下:
  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: system_wrapper.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: 1.0
  19. *Signal description
  20. *1) _i input
  21. *2) _o output
  22. *3) _n activ low
  23. *4) _dg debug signal
  24. *5) _r delay or register
  25. *6) _s state mechine
  26. *********************************************************************/
  27. `timescale 1 ns / 1 ns
  28. module system_wrapper
  29. (
  30. inout [14:0]DDR_addr,
  31. inout [2:0]DDR_ba,
  32. inout DDR_cas_n,
  33. inout DDR_ck_n,
  34. inout DDR_ck_p,
  35. inout DDR_cke,
  36. inout DDR_cs_n,
  37. inout [3:0]DDR_dm,
  38. inout [31:0]DDR_dq,
  39. inout [3:0]DDR_dqs_n,
  40. inout [3:0]DDR_dqs_p,
  41. inout DDR_odt,
  42. inout DDR_ras_n,
  43. inout DDR_reset_n,
  44. inout DDR_we_n,
  45. inout FIXED_IO_ddr_vrn,
  46. inout FIXED_IO_ddr_vrp,
  47. inout [53:0]FIXED_IO_mio,
  48. inout FIXED_IO_ps_clk,
  49. inout FIXED_IO_ps_porb,
  50. inout FIXED_IO_ps_srstb
  51. );
  52. wire pl_clk;
  53. wire user_rstn;
  54. wire user_start;
  55. wire [31:0]ud_wdata_0;
  56. wire ud_wde_0;
  57. wire ud_wvs_0;
  58. wire ud_wclk_0;
  59. reg [15:0]test_data;
  60. always@(posedge pl_clk)begin
  61.         if(user_rstn == 1'b0)begin
  62.                 test_data <= 12'd0;
  63.         end
  64.         else if(ud_wde_0) begin // test_data为加计数器产生测试数据
  65.                test_data <= test_data + 1'b1;
  66.         end
  67. end
  68. assign ud_wdata_0 = {test_data,test_data};
  69. assign ud_wde_0   = user_start;
  70. assign ud_wclk_0  = pl_clk;
  71. assign ud_wvs_0   = user_start;
  72. ila_0 ila0_dg
  73. (
  74. .clk(pl_clk),
  75. .probe0({test_data[15:0],ud_wde_0,user_start,user_rstn})
  76. );
  77.   
  78. system system_i
  79. (
  80. .DDR_addr(DDR_addr),
  81. .DDR_ba(DDR_ba),
  82. .DDR_cas_n(DDR_cas_n),
  83. .DDR_ck_n(DDR_ck_n),
  84. .DDR_ck_p(DDR_ck_p),
  85. .DDR_cke(DDR_cke),
  86. .DDR_cs_n(DDR_cs_n),
  87. .DDR_dm(DDR_dm),
  88. .DDR_dq(DDR_dq),
  89. .DDR_dqs_n(DDR_dqs_n),
  90. .DDR_dqs_p(DDR_dqs_p),
  91. .DDR_odt(DDR_odt),
  92. .DDR_ras_n(DDR_ras_n),
  93. .DDR_reset_n(DDR_reset_n),
  94. .DDR_we_n(DDR_we_n),
  95. .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
  96. .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
  97. .FIXED_IO_mio(FIXED_IO_mio),
  98. .FIXED_IO_ps_clk(FIXED_IO_ps_clk),
  99. .FIXED_IO_ps_porb(FIXED_IO_ps_porb),
  100. .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),
  101. .ud_wclk_0(ud_wclk_0),
  102. .ud_wdata_0(ud_wdata_0),
  103. .ud_wde_0(ud_wde_0),
  104. .ud_wvs_0(ud_wvs_0),
  105. .user_rstn(user_rstn),
  106. .user_start(user_start),
  107. .pl_clk(pl_clk)               
  108. );
  109. endmodule
复制代码

以上代码中除了阴影部分,其他代码可以通过vivado自动产生图形化接口的顶层文件,然后复制该文件,修改而来。
3cdf9a8c155b49028111ee66a622ac9d.jpg
3.5FDMA-DBUF IP代码分析
FDMA-DBUF IP代码采用“对称设计”方法,读写代码对称,好处是代码结构清晰,读写过程一致,代码效率高,更加容易维护。本文只用到了写通道部分,读通道部分没有用到,但是我们还是介绍下。
关于更多AXI4总线相关知识可以阅读“3-2-02_axi_bus.pdf” 这个章节,这个章节专讲AXI4总线,其中也详细讲解了FDMA的代码分析。
1:FDMA-DBUF写状态机
为了配合AXI-FDMA IP发送数据到PS,我们写了一个uifdmadbuf ip,通过这个IP把用户编写的数据时序,转为AXI-FMDA接口数据流。该IP支持视频格式的帧同步,每一帧都进行同步,也支持没有帧同步的数据流方式传输。
a3f5155229324028a32e0493310c8f06.jpg
2:FDMA的写时序波形图
d7577846c23b4ceb98764d702395a004.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写状态机
读数据的过程和写数据的过程是对称的,状态机如下:
db1ded712df3462c977ebf50d0fa5756.jpg
为了配合AXI-FDMA IP发送数据到PS,我们写了一个uifdmadbuf ip,通过这个IP把用户编写的数据时序,转为AXI-FMDA接口数据流。该IP支持视频格式的帧同步,每一帧都进行同步,也支持没有帧同步的数据流方式传输。
4:FDMA的读时序波形图
70f4508f12e143b9adc0c2d9cc8c8709.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总线协议的使用。
5:源码注释
米联客自定义IP路径在配套的soc_prj/uisrc/03_ip路径下,如下图所示:
90749b89d9b64fa5ada23ff2fd6c2208.jpg
代码的层次结构如下:
7fd1030ee46441009bde08e69028a08e.jpg
源码部分一共有包括4个文件:
fs_cap.v该文件在“03PL发数据到PS方案(DMA)”一文中有描述,这里不再重复介绍其功能。
uidbufirq.v文件用来保存一帧数据发送完毕后产生的中断,ps部分可以通过axi-lite接口读取中断值知道哪一个地址完成数据传输。
uidbuf.v文件是完成用户数据到FDMA接口数据转换的关键代码,同时该代码完成了中断控制,帧缓存控制。
uifdma_dbuf.v该文件是该IP的顶层文件
5-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: 1.0
  19. *Signal description
  20. *1) _i input
  21. *2) _o output
  22. *3) _n activ low
  23. *4) _dg debug signal
  24. *5) _r delay or register
  25. *6) _s state mechine
  26. *********************************************************************/
  27. `timescale 1ns / 1ps
  28. module uidbuf#(
  29. parameter  integer                   VIDEO_ENABLE   = 1,//使能视频帧支持功能
  30. parameter  integer                   ENABLE_WRITE   = 1,//使能写通道
  31. parameter  integer                   ENABLE_READ    = 1,//使能读通道
  32. parameter  integer                   AXI_DATA_WIDTH = 128,//AXI总线数据位宽
  33. parameter  integer                   AXI_ADDR_WIDTH = 32, //AXI总线地址位宽
  34. parameter  integer                   W_BUFDEPTH     = 2048, //写通道AXI设置FIFO缓存大小
  35. parameter  integer                   W_DATAWIDTH    = 32,  //写通道AXI设置数据位宽大小
  36. parameter  [AXI_ADDR_WIDTH -1'b1: 0] W_BASEADDR     = 0, //写通道设置内存起始地址
  37. parameter  integer                   W_DSIZEBITS    = 24, //写通道设置缓存数据的增量地址大小,用于FDMA DBUF 计算帧缓存起始地址
  38. parameter  integer                   W_XSIZE        = 1920, //写通道设置X方向的数据大小,代表了每次FDMA 传输的数据长度
  39. parameter  integer                   W_XSTRIDE      = 1920, //写通道设置X方向的Stride值,主要用于图形缓存应用
  40. parameter  integer                   W_YSIZE        = 1080, //写通道设置Y方向值,代表了进行了多少次XSIZE传输
  41. parameter  integer                   W_XDIV         = 2, //写通道对X方向数据拆分为XDIV次传输,减少FIFO的使用
  42. parameter  integer                   W_BUFSIZE      = 3, //写通道设置帧缓存大小,目前最大支持128帧,可以修改参数支持更缓存数
  43. parameter  integer                   R_BUFDEPTH     = 2048, //读通道AXI设置FIFO缓存大小
  44. parameter  integer                   R_DATAWIDTH    = 32, //读通道AXI设置数据位宽大小
  45. parameter  [AXI_ADDR_WIDTH -1'b1: 0] R_BASEADDR     = 0, //读通道设置内存起始地址
  46. parameter  integer                   R_DSIZEBITS    = 24, //读通道设置缓存数据的增量地址大小,用于FDMA DBUF 计算帧缓存起始地址
  47. parameter  integer                   R_XSIZE        = 1920, //读通道设置X方向的数据大小,代表了每次FDMA 传输的数据长度
  48. parameter  integer                   R_XSTRIDE      = 1920, //读通道设置X方向的Stride值,主要用于图形缓存应用
  49. parameter  integer                   R_YSIZE        = 1080, //读通道设置Y方向值,代表了进行了多少次XSIZE传输
  50. parameter  integer                   R_XDIV         = 2, //读通道对X方向数据拆分为XDIV次传输,减少FIFO的使用
  51. parameter  integer                   R_BUFSIZE      = 3 //读通道设置帧缓存大小,目前最大支持128帧,可以修改参数支持更缓存数
  52. )
  53. (
  54. input wire                                  ui_clk, //和FDMA AXI总线时钟一致
  55. input wire                                  ui_rstn, //和FDMA AXI复位一致
  56. //sensor input -W_FIFO--------------
  57. input wire                                  W_wclk_i, //用户写数据接口时钟
  58. input wire                                  W_FS_i, //用户写数据接口同步信号,对于非视频帧一般设置为1
  59. input wire                                  W_wren_i, //用户写数据使能
  60. input wire     [W_DATAWIDTH-1'b1 : 0]       W_data_i, //用户写数据
  61. output reg     [7   :0]                     W_sync_cnt_o =0, //写通道BUF帧同步输出
  62. input  wire    [7   :0]                     W_buf_i, // 写通道BUF帧同步输入
  63. //----------fdma signals write-------      
  64. output wire    [AXI_ADDR_WIDTH-1'b1: 0]     fdma_waddr, //FDMA写通道地址
  65. output wire                                 fdma_wareq, //FDMA写通道请求
  66. output wire    [15  :0]                     fdma_wsize, //FDMA写通道一次FDMA的传输大小                                    
  67. input  wire                                 fdma_wbusy, //FDMA处于BUSY状态,AXI总线正在写操作   
  68. output wire    [AXI_DATA_WIDTH-1'b1:0]      fdma_wdata, //FDMA写数据
  69. input  wire                                 fdma_wvalid, //FDMA 写有效
  70. output wire                                 fdma_wready, //FDMA写准备好,用户可以写数据
  71. output reg     [7   :0]                     fmda_wbuf =0, //FDMA的写帧缓存号输出
  72. output wire                                 fdma_wirq, //FDMA一次写完成的数据传输完成后,产生中断。   
  73. //----------fdma signals read-------  
  74. input  wire                                 R_rclk_i, //用户读数据接口时钟
  75. input  wire                                 R_FS_i, //用户读数据接口同步信号,对于非视频帧一般设置1
  76. input  wire                                 R_rden_i, //用户读数据使能
  77. output wire    [R_DATAWIDTH-1'b1 : 0]       R_data_o, //用户读数据
  78. output reg     [7   :0]                     R_sync_cnt_o =0, //读通道BUF帧同步输出
  79. input  wire    [7   :0]                     R_buf_i, //写通道BUF帧同步输入
  80. output wire    [AXI_ADDR_WIDTH-1'b1: 0]     fdma_raddr, // FDMA读通道地址
  81. output wire                                 fdma_rareq, // FDMA读通道请求
  82. output wire    [15: 0]                      fdma_rsize, // FDMA读通道一次FDMA的传输大小                                    
  83. input  wire                                 fdma_rbusy, // FDMA处于BUSY状态,AXI总线正在读操作     
  84. input  wire    [AXI_DATA_WIDTH-1'b1:0]      fdma_rdata, // FDMA读数据
  85. input  wire                                 fdma_rvalid, // FDMA 读有效
  86. output wire                                 fdma_rready, // FDMA读准备好,用户可以写数据
  87. output reg     [7  :0]                      fmda_rbuf =0, // FDMA的读帧缓存号输出
  88. output wire                                 fdma_rirq // FDMA一次读完成的数据传输完成后,产生中断
  89. );   
  90. // 计算Log2
  91. function integer clog2;
  92.   input integer value;
  93.   begin
  94.     value = value-1;
  95.     for (clog2=0; value>0; clog2=clog2+1)
  96.       value = value>>1;
  97.     end
  98.   endfunction
  99. //FDMA读写状态机的状态值,一般4个状态值即可
  100. localparam S_IDLE  =  2'd0;  
  101. localparam S_RST   =  2'd1;  
  102. localparam S_DATA1 =  2'd2;   
  103. localparam S_DATA2 =  2'd3;
  104. // 通过设置通道使能,可以优化代码的利用率
  105. generate  if(ENABLE_WRITE == 1)begin : FDMA_WRITE_ENABLE
  106. localparam WFIFO_DEPTH = W_BUFDEPTH; //写通道FIFO深度
  107. localparam W_WR_DATA_COUNT_WIDTH = clog2(WFIFO_DEPTH)+1; //计算FIFO的写通道位宽
  108. localparam W_RD_DATA_COUNT_WIDTH = clog2(WFIFO_DEPTH*W_DATAWIDTH/AXI_DATA_WIDTH)+1;//clog2(WFIFO_DEPTH/(AXI_DATA_WIDTH/W_DATAWIDTH))+1;
  109. localparam WYBUF_SIZE           = (W_BUFSIZE - 1'b1); //写通道需要完成多少次XSIZE操作
  110. localparam WY_BURST_TIMES       = (W_YSIZE*W_XDIV); //写通道需要完成的FDMA burst 操作次数,XDIV用于把XSIZE分解多次传输
  111. localparam FDMA_WX_BURST        = (W_XSIZE*W_DATAWIDTH/AXI_DATA_WIDTH)/W_XDIV; //FDMA BURST 一次的大小
  112. localparam WX_BURST_ADDR_INC    = (W_XSIZE*(W_DATAWIDTH/8))/W_XDIV; //FDMA每次burst之后的地址增加
  113. localparam WX_LAST_ADDR_INC     = (W_XSTRIDE-W_XSIZE)*(W_DATAWIDTH/8) + WX_BURST_ADDR_INC; //根据stride值计算出来最后一次地址
  114. (*mark_debug = "true"*) (* KEEP = "TRUE" *) wire  W_wren_ri = W_wren_i;
  115. assign                                  fdma_wready = 1'b1;
  116. reg                                     fdma_wareq_r= 1'b0;
  117. reg                                     W_FIFO_Rst=0;
  118. (*mark_debug = "true"*) (* KEEP = "TRUE" *)wire                                    W_FS;
  119. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [1 :0]                              W_MS=0;
  120. reg [W_DSIZEBITS-1'b1:0]                W_addr=0;
  121. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [15:0]                              W_bcnt=0;
  122. (*mark_debug = "true"*) (* KEEP = "TRUE" *)wire[W_RD_DATA_COUNT_WIDTH-1'b1 :0]     W_rcnt;
  123. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg                                     W_REQ=0;
  124. (*mark_debug = "true"*) (* KEEP = "TRUE" *)reg [5 :0]                              wirq_dly_cnt =0;
  125. reg [3 :0]                              wdiv_cnt =0;
  126. reg [7 :0]                              wrst_cnt =0;
  127. reg [7 :0]                              fmda_wbufn;
  128. (*mark_debug = "true"*) (* KEEP = "TRUE" *) wire wirq= fdma_wirq;
  129. assign fdma_wsize = FDMA_WX_BURST;
  130. assign fdma_wirq = (wirq_dly_cnt>0);
  131. assign fdma_waddr = W_BASEADDR + {fmda_wbufn,W_addr};//由于FPGA逻辑做乘法比较复杂,因此通过设置高位地址实现缓存设置
  132. reg [1:0] W_MS_r =0;
  133. always @(posedge ui_clk) W_MS_r <= W_MS;
  134. //每次FDMA DBUF 完成一帧数据传输后,产生中断,这个中断持续60个周期的uiclk,这里的延迟必须足够ZYNQ IP核识别到这个中断
  135. always @(posedge ui_clk) begin
  136.     if(ui_rstn == 1'b0)begin
  137.         wirq_dly_cnt <= 6'd0;
  138.         fmda_wbuf <=0;
  139.     end
  140.     else if((W_MS_r == S_DATA2) && (W_MS == S_IDLE))begin
  141.         wirq_dly_cnt <= 60;
  142.         fmda_wbuf <= fmda_wbufn;
  143.     end
  144.     else if(wirq_dly_cnt >0)
  145.         wirq_dly_cnt <= wirq_dly_cnt - 1'b1;
  146. end
  147. //帧同步,对于视频有效
  148. fs_cap #
  149. (
  150. .VIDEO_ENABLE(VIDEO_ENABLE)
  151. )
  152. fs_cap_W0
  153. (
  154. .clk_i(ui_clk),
  155. .rstn_i(ui_rstn),
  156. .vs_i(W_FS_i),
  157. .fs_cap_o(W_FS)
  158. );
  159. assign fdma_wareq = fdma_wareq_r;
  160. //写通道状态机,采用4个状态值描述
  161. always @(posedge ui_clk) begin
  162.     if(!ui_rstn)begin
  163.         W_MS         <= S_IDLE;
  164.         W_FIFO_Rst   <= 0;
  165.         W_addr       <= 0;
  166.         W_sync_cnt_o <= 0;
  167.         W_bcnt       <= 0;
  168.         wrst_cnt     <= 0;
  169.         wdiv_cnt     <= 0;
  170.         fmda_wbufn    <= 0;
  171.         fdma_wareq_r <= 1'd0;
  172.     end   
  173.     else begin
  174.       case(W_MS)
  175.         S_IDLE:begin
  176.           W_addr <= 0;
  177.           W_bcnt <= 0;
  178.           wrst_cnt <= 0;
  179.           wdiv_cnt <=0;
  180.           if(W_FS) begin //帧同步,对于非视频数据一般常量为1
  181.             W_MS <= S_RST;
  182.             if(W_sync_cnt_o < WYBUF_SIZE) //输出帧同步计数器
  183.                 W_sync_cnt_o <= W_sync_cnt_o + 1'b1;
  184.             else
  185.                 W_sync_cnt_o <= 0;  
  186.           end
  187.        end
  188.        S_RST:begin//帧同步,对于非视频数据直接跳过,对于视频数据,会同步每一帧,并且复位数据FIFO
  189.            fmda_wbufn <= W_buf_i;
  190.            wrst_cnt <= wrst_cnt + 1'b1;
  191.            if((VIDEO_ENABLE == 1) && (wrst_cnt < 40))
  192.                 W_FIFO_Rst <= 1;
  193.            else if((VIDEO_ENABLE == 1) && (wrst_cnt < 100))
  194.                 W_FIFO_Rst <= 0;
  195.            else if(fdma_wirq == 1'b0) begin
  196.                 W_MS <= S_DATA1;
  197.            end
  198.        end
  199.         S_DATA1:begin //发送写FDMA请求
  200.           if(fdma_wbusy == 1'b0 && W_REQ )begin
  201.              fdma_wareq_r  <= 1'b1;
  202.           end
  203.           else if(fdma_wbusy == 1'b1) begin
  204.              fdma_wareq_r  <= 1'b0;
  205.              W_MS    <= S_DATA2;
  206.           end         
  207.          end
  208.         S_DATA2:begin //写有效数据
  209.             if(fdma_wbusy == 1'b0)begin
  210.                 if(W_bcnt == WY_BURST_TIMES - 1'b1) //判断是否传输完毕
  211.                     W_MS <= S_IDLE;
  212.                 else begin
  213.                     if(wdiv_cnt < W_XDIV - 1'b1)begin//如果对XSIZE做了分次传输,一个XSIZE也需要XDIV次FDMA完成传输
  214.                         W_addr <= W_addr +  WX_BURST_ADDR_INC;  //计算地址增量
  215.                         wdiv_cnt <= wdiv_cnt + 1'b1;
  216.                      end
  217.                     else begin
  218.                         W_addr <= W_addr + WX_LAST_ADDR_INC; //计算最后一次地址增量,最后一次地址根据stride 计算
  219.                         wdiv_cnt <= 0;
  220.                     end
  221.                     W_bcnt <= W_bcnt + 1'b1;
  222.                     W_MS    <= S_DATA1;
  223.                 end
  224.             end
  225.          end
  226.          default: W_MS <= S_IDLE;
  227.        endcase
  228.     end
  229. end
  230. //写通道的数据FIFO,采用了原语调用xpm_fifo_async fifo,当FIFO存储的数据阈值达到一定量,一般满足一次FDMA的burst即可发出请求
  231. wire W_rbusy;
  232. always@(posedge ui_clk)     
  233.      W_REQ  <= (W_rcnt > FDMA_WX_BURST - 2)&&(~W_rbusy);
  234.      
  235. xpm_fifo_async # (
  236.   .FIFO_MEMORY_TYPE          ("auto"),           //string; "auto", "block", or "distributed";
  237.   .ECC_MODE                  ("no_ecc"),         //string; "no_ecc" or "en_ecc";
  238.   .RELATED_CLOCKS            (0),                //positive integer; 0 or 1
  239.   .FIFO_WRITE_DEPTH          (WFIFO_DEPTH),     //positive integer
  240.   .WRITE_DATA_WIDTH          (W_DATAWIDTH),               //positive integer
  241.   .WR_DATA_COUNT_WIDTH       (W_WR_DATA_COUNT_WIDTH),               //positive integer
  242.   .PROG_FULL_THRESH          (20),               //positive integer
  243.   .FULL_RESET_VALUE          (0),                //positive integer; 0 or 1
  244.   .USE_ADV_FEATURES          ("0707"),           //string; "0000" to "1F1F";
  245.   .READ_MODE                 ("fwft"),            //string; "std" or "fwft";
  246.   .FIFO_READ_LATENCY         (0),                //positive integer;
  247.   .READ_DATA_WIDTH           (AXI_DATA_WIDTH),               //positive integer
  248.   .RD_DATA_COUNT_WIDTH       (W_RD_DATA_COUNT_WIDTH),               //positive integer
  249.   .PROG_EMPTY_THRESH         (10),               //positive integer
  250.   .DOUT_RESET_VALUE          ("0"),              //string
  251.   .CDC_SYNC_STAGES           (2),                //positive integer
  252.   .WAKEUP_TIME               (0)                 //positive integer; 0 or 2;
  253. ) xpm_fifo_W_inst (
  254.       .rst              ((ui_rstn == 1'b0) || (W_FIFO_Rst == 1'b1)),
  255.       .wr_clk           (W_wclk_i),
  256.       .wr_en            (W_wren_i),
  257.       .din              (W_data_i),
  258.       .full             (),
  259.       .overflow         (),
  260.       .prog_full        (),
  261.       .wr_data_count    (),
  262.       .almost_full      (),
  263.       .wr_ack           (),
  264.       .wr_rst_busy      (),
  265.       .rd_clk           (ui_clk),
  266.       .rd_en            (fdma_wvalid),
  267.       .dout             (fdma_wdata),
  268.       .empty            (),
  269.       .underflow        (),
  270.       .rd_rst_busy      (W_rbusy),
  271.       .prog_empty       (),
  272.       .rd_data_count    (W_rcnt),
  273.       .almost_empty     (),
  274.       .data_valid       (W_dvalid),
  275.       .sleep            (1'b0),
  276.       .injectsbiterr    (1'b0),
  277.       .injectdbiterr    (1'b0),
  278.       .sbiterr          (),
  279.       .dbiterr          ()
  280. );
  281. end
  282. else begin : FDMA_WRITE_DISABLE
  283. //----------fdma signals write-------      
  284. assign fdma_waddr = 0;
  285. assign fdma_wareq = 0;
  286. assign fdma_wsize = 0;                                    
  287. assign fdma_wdata = 0;
  288. assign fdma_wready = 0;
  289. assign fdma_wirq = 0;
  290. end
  291. endgenerate
  292. generate  if(ENABLE_READ == 1)begin : FDMA_READ// 通过设置通道使能,可以优化代码的利用率
  293. localparam RYBUF_SIZE           = (R_BUFSIZE - 1'b1); //读通道需要完成多少次XSIZE操作
  294. localparam RY_BURST_TIMES       = (R_YSIZE*R_XDIV); //读通道需要完成的FDMA burst 操作次数,XDIV用于把XSIZE分解多次传输
  295. localparam FDMA_RX_BURST        = (R_XSIZE*R_DATAWIDTH/AXI_DATA_WIDTH)/R_XDIV; //FDMA BURST 一次的大小
  296. localparam RX_BURST_ADDR_INC    = (R_XSIZE*(R_DATAWIDTH/8))/R_XDIV; //FDMA每次burst之后的地址增加
  297. localparam RX_LAST_ADDR_INC     = (R_XSTRIDE-R_XSIZE)*(R_DATAWIDTH/8) + RX_BURST_ADDR_INC; //根据stride值计算出来最后一次地址
  298. localparam RFIFO_DEPTH = R_BUFDEPTH*R_DATAWIDTH/AXI_DATA_WIDTH;//R_BUFDEPTH/(AXI_DATA_WIDTH/R_DATAWIDTH);
  299. localparam R_WR_DATA_COUNT_WIDTH = clog2(RFIFO_DEPTH)+1; //读通道FIFO 输入部分深度
  300. localparam R_RD_DATA_COUNT_WIDTH = clog2(R_BUFDEPTH)+1; //写通道FIFO输出部分深度
  301. assign                                  fdma_rready = 1'b1;
  302. reg                                     fdma_rareq_r= 1'b0;
  303. reg                                     R_FIFO_Rst=0;
  304. wire                                    R_FS;
  305. reg [1 :0]                              R_MS=0;
  306. reg [R_DSIZEBITS-1'b1:0]                R_addr=0;
  307. reg [15:0]                              R_bcnt=0;
  308. wire[R_WR_DATA_COUNT_WIDTH-1'b1 :0]     R_wcnt;
  309. reg                                     R_REQ=0;
  310. reg [5 :0]                              rirq_dly_cnt =0;
  311. reg [3 :0]                              rdiv_cnt =0;
  312. reg [7 :0]                              rrst_cnt =0;
  313. reg [7 :0]                              fmda_rbufn;
  314. assign fdma_rsize = FDMA_RX_BURST;
  315. assign fdma_rirq = (rirq_dly_cnt>0);
  316. assign fdma_raddr = R_BASEADDR + {fmda_rbufn,R_addr};//由于FPGA逻辑做乘法比较复杂,因此通过设置高位地址实现缓存设置
  317. reg [1:0] R_MS_r =0;
  318. always @(posedge ui_clk) R_MS_r <= R_MS;
  319. //每次FDMA DBUF 完成一帧数据传输后,产生中断,这个中断持续60个周期的uiclk,这里的延迟必须足够ZYNQ IP核识别到这个中断
  320. always @(posedge ui_clk) begin
  321.     if(ui_rstn == 1'b0)begin
  322.         rirq_dly_cnt <= 6'd0;
  323.         fmda_rbuf <=0;
  324.     end
  325.     else if((R_MS_r == S_DATA2) && (R_MS == S_IDLE))begin
  326.         rirq_dly_cnt <= 60;
  327.         fmda_rbuf <= fmda_rbufn;
  328.     end
  329.     else if(rirq_dly_cnt >0)
  330.         rirq_dly_cnt <= rirq_dly_cnt - 1'b1;
  331. end
  332. //帧同步,对于视频有效
  333. fs_cap #
  334. (
  335. .VIDEO_ENABLE(VIDEO_ENABLE)
  336. )
  337. fs_cap_R0
  338. (
  339.   .clk_i(ui_clk),
  340.   .rstn_i(ui_rstn),
  341.   .vs_i(R_FS_i),
  342.   .fs_cap_o(R_FS)
  343. );
  344. assign fdma_rareq = fdma_rareq_r;
  345. //读通道状态机,采用4个状态值描述
  346. always @(posedge ui_clk) begin
  347.    if(!ui_rstn)begin
  348.         R_MS          <= S_IDLE;
  349.         R_FIFO_Rst   <= 0;
  350.         R_addr       <= 0;
  351.         R_sync_cnt_o <= 0;
  352.         R_bcnt       <= 0;
  353.         rrst_cnt     <= 0;
  354.         rdiv_cnt      <= 0;
  355.         fmda_rbufn    <= 0;
  356.         fdma_rareq_r  <= 1'd0;
  357.     end   
  358.     else begin
  359.       case(R_MS) //帧同步,对于非视频数据一般常量为1
  360.         S_IDLE:begin
  361.           R_addr <= 0;
  362.           R_bcnt <= 0;
  363.           rrst_cnt <= 0;
  364.           rdiv_cnt <=0;
  365.           if(R_FS) begin
  366.             R_MS <= S_RST;
  367.             if(R_sync_cnt_o < RYBUF_SIZE) //输出帧同步计数器,当需要用读通道做帧同步的时候使用
  368.                 R_sync_cnt_o <= R_sync_cnt_o + 1'b1;
  369.             else
  370.                 R_sync_cnt_o <= 0;  
  371.           end
  372.        end
  373.        S_RST:begin//帧同步,对于非视频数据直接跳过,对于视频数据,会同步每一帧,并且复位数据FIFO
  374.            fmda_rbufn <= R_buf_i;
  375.            rrst_cnt <= rrst_cnt + 1'b1;
  376.            if((VIDEO_ENABLE == 1) && (rrst_cnt < 40))
  377.                 R_FIFO_Rst <= 1;
  378.            else if((VIDEO_ENABLE == 1) && (rrst_cnt < 100))
  379.                 R_FIFO_Rst <= 0;
  380.            else if(fdma_rirq == 1'b0) begin
  381.                 R_MS <= S_DATA1;
  382.            end
  383.        end
  384.        S_DATA1:begin
  385.          if(fdma_rbusy == 1'b0 && R_REQ)begin
  386.             fdma_rareq_r  <= 1'b1;  
  387.          end
  388.          else if(fdma_rbusy == 1'b1) begin
  389.             fdma_rareq_r  <= 1'b0;
  390.             R_MS    <= S_DATA2;
  391.          end         
  392.         end
  393.         S_DATA2:begin //写有效数据
  394.             if(fdma_rbusy == 1'b0)begin
  395.                 if(R_bcnt == RY_BURST_TIMES - 1'b1) //判断是否传输完毕
  396.                     R_MS <= S_IDLE;
  397.                 else begin
  398.                     if(rdiv_cnt < R_XDIV - 1'b1)begin//如果对XSIZE做了分次传输,一个XSIZE也需要XDIV次FDMA完成传输
  399.                         R_addr <= R_addr +  RX_BURST_ADDR_INC;  //计算地址增量
  400.                         rdiv_cnt <= rdiv_cnt + 1'b1;
  401.                      end
  402.                     else begin
  403.                         R_addr <= R_addr + RX_LAST_ADDR_INC; //计算最后一次地址增量,最后一次地址根据stride 计算
  404.                         rdiv_cnt <= 0;
  405.                     end
  406.                     R_bcnt <= R_bcnt + 1'b1;
  407.                     R_MS    <= S_DATA1;
  408.                 end
  409.             end
  410.          end
  411.          default:R_MS <= S_IDLE;
  412.       endcase
  413.    end
  414. end
  415. //写通道的数据FIFO,采用了原语调用xpm_fifo_async fifo,当FIFO存储的数据阈值达到一定量,一般满足一次FDMA的burst即可发出请求
  416. wire R_wbusy;
  417. always@(posedge ui_clk)      
  418.      R_REQ  <= (R_wcnt < FDMA_RX_BURST - 2)&&(~R_wbusy);
  419. xpm_fifo_async # (
  420.   .FIFO_MEMORY_TYPE          ("auto"),           //string; "auto", "block", or "distributed";
  421.   .ECC_MODE                  ("no_ecc"),         //string; "no_ecc" or "en_ecc";
  422.   .RELATED_CLOCKS            (0),                //positive integer; 0 or 1
  423.   .FIFO_WRITE_DEPTH          (RFIFO_DEPTH),     //positive integer
  424.   .WRITE_DATA_WIDTH          (AXI_DATA_WIDTH),               //positive integer
  425.   .WR_DATA_COUNT_WIDTH       (R_WR_DATA_COUNT_WIDTH),               //positive integer
  426.   .PROG_FULL_THRESH          (20),               //positive integer
  427.   .FULL_RESET_VALUE          (0),                //positive integer; 0 or 1
  428.   .USE_ADV_FEATURES          ("0707"),           //string; "0000" to "1F1F";
  429.   .READ_MODE                 ("fwft"),            //string; "std" or "fwft";
  430.   .FIFO_READ_LATENCY         (0),                //positive integer;
  431.   .READ_DATA_WIDTH           (R_DATAWIDTH),               //positive integer
  432.   .RD_DATA_COUNT_WIDTH       (R_RD_DATA_COUNT_WIDTH),               //positive integer
  433.   .PROG_EMPTY_THRESH         (10),               //positive integer
  434.   .DOUT_RESET_VALUE          ("0"),              //string
  435.   .CDC_SYNC_STAGES           (2),                //positive integer
  436.   .WAKEUP_TIME               (0)                 //positive integer; 0 or 2;
  437. ) xpm_fifo_R_inst (
  438.       .rst              ((ui_rstn == 1'b0) || (W_FIFO_Rst == 1'b1)),
  439.       .wr_clk           (ui_clk),
  440.       .wr_en            (fdma_rvalid),
  441.       .din              (fdma_rdata),
  442.       .full             (),
  443.       .overflow         (),
  444.       .prog_full        (),
  445.       .wr_data_count    (R_wcnt),
  446.       .almost_full      (),
  447.       .wr_ack           (),
  448.       .wr_rst_busy      (R_wbusy),
  449.       .rd_clk           (R_rclk_i),
  450.       .rd_en            (R_rden_i),
  451.       .dout             (R_data_o),
  452.       .empty            (),
  453.       .underflow        (),
  454.       .rd_rst_busy      (),
  455.       .prog_empty       (),
  456.       .rd_data_count    (),
  457.       .almost_empty     (),
  458.       .data_valid       (),
  459.       .sleep            (1'b0),
  460.       .injectsbiterr    (1'b0),
  461.       .injectdbiterr    (1'b0),
  462.       .sbiterr          (),
  463.       .dbiterr          ()
  464. );
  465. end
  466. else begin : FDMA_READ_DISABLE
  467.    
  468. assign fdma_raddr = 0;
  469. assign fdma_rareq = 0;
  470. assign fdma_rsize = 0;                                    
  471. assign fdma_rdata = 0;
  472. assign fdma_rready = 0;
  473. assign fdma_rirq = 0;
  474. end
  475. endgenerate
  476. endmodule
复制代码

5-2:uidbufirq.v注释
这个部分的功能只有在PS需要获取中断后的帧缓存通道才会用到,PS通过axi-lite接口可以读取到寄存器的值。
关于更多AXI4总线相关知识可以阅读“3-2-02_axi_bus.pdf” 这个章节,这个章节专讲AXI4总线,其中也详细讲解了FDMA的代码分析。
这里主要看代码的最后,根据中断信号寄存内存的中断号
  1. // 当写中断产生的时候,寄存当前的帧缓存号到fdma_wbfu_irq寄存器
  2. always @(posedge S_AXI_ACLK) fdma_wirq_r <= fdma_wirq;
  3. always @(posedge S_AXI_ACLK)begin
  4. if( S_AXI_ARESETN == 1'b0)
  5.     fdma_wbuf_irq <= 0;
  6. else if(fdma_wirq_r == 1'b0 & fdma_wirq == 1'b1)
  7.     fdma_wbuf_irq <= fdma_wbuf;
  8. end
  9. // 当读中断产生的时候,寄存当前的帧缓存号到fdma_wbfu_irq寄存器
  10. always @(posedge S_AXI_ACLK) fdma_rirq_r <= fdma_rirq;
  11. always @(posedge S_AXI_ACLK)begin
  12. if( S_AXI_ARESETN == 1'b0)
  13.     fdma_rbuf_irq <= 0;
  14. else if(fdma_rirq_r == 1'b0 & fdma_rirq == 1'b1)
  15.     fdma_rbuf_irq <= fdma_rbuf;
  16. end
  17. // User logic ends
  18. endmodule
复制代码

3.6地址空间分配
17757709c7174ea68fd8b866bd13a4f9.jpg
3.7编译并导出平台文件
1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。
2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。
3:生成Bit文件。
4:导出到硬件: FileàExport HardwareàInclude bitstream
5:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
ff4ae04dc964470bac683cb15badf08b.jpg
4搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
4.1创建SDK Platform工程
978ef1fb5e6b4b83a08bc0e79d84ba7e.jpg
4.2创建axi_fdma_pl2ps APP工程
a22c625de67248b5a6d03a9fb90b0477.jpg
5程序分析
5.1总流程图
本程序通过控制AXI-GPIO控制FPGA工程数据接口的复位,以及启动,停止。当启动FPGA数据发送后,每1MB数据都会产生一次中断,当中断到了后,我们设计了一个环形Buf用于记录哪一个缓存已经有数据了,并且做好标记。在fdma_dbuf_test()函数中,通过读取已经标记的缓存区,读取有效数据。我们这里还设计了把1MB分成16KB来处理。这种处理方式后面的demo方案中会具体应用到数据采集中。
700c7cf110194bf4bb9e018adfe2cf48.jpg
5.2环形buf设计
在本demo中采用了米联客定义的环形buf数据结构,源码和定义如下:
  1. typedef struct fdma_buf_st
  2. {
  3.         u8  record[16];//reg buffer
  4.         u8  circle_cnt;//环形buf的计数器
  5.         u8  next;//指向下一个应该处理的数据包缓存
  6.         u8  pkg_done_cnt; //记录已经接收到的包
  7.         u16 fram_cnt; //记录帧数量
  8.         u16 pkg_cnt; //记录拆包后已经处理的包
  9. }fdma_buf_st;
  10. //在中断函数中记录缓存号,这里通过读取FDMA_DBUF的寄存器获取
  11. void PS_RX_intr_Handler(void *param)
  12. {
  13.         fdma_buf.record[fdma_buf.circle_cnt]= Xil_In32((UINTPTR)FDMA_DBUF_BASE_ADDR);
  14.         fdma_buf.pkg_done_cnt++;
  15.         if(fdma_buf.circle_cnt<2)
  16.                 fdma_buf.circle_cnt ++ ;
  17.         else
  18.                 fdma_buf.circle_cnt = 0;
  19. }
复制代码

5.3GPIO的初始化
通过控制AXI-GPIO来控制复位和启动停止代码简单方便
  1. void gpio_init(void)
  2. {
  3.         XGpio_Initialize(&gpio_user_rstn, XPAR_GPIO_USER_RSTN_DEVICE_ID);
  4.         XGpio_SetDataDirection(&gpio_user_rstn, 1, 0x0);
  5.         XGpio_DiscreteWrite(&gpio_user_rstn,1,0x0);//set gpio reset=0 reset user logic
  6.         XGpio_Initialize(&gpio_user_start, XPAR_GPIO_USER_START_DEVICE_ID);
  7.         XGpio_SetDataDirection(&gpio_user_start, 1, 0x0);//set gpio user_start=0 stop transmission
  8.         XGpio_DiscreteWrite(&gpio_user_rstn,1,0x1);//set gpio reset=1 reset done
  9. }
复制代码


5.4启动传输控制
  1. void fdma_wr_set(u32 set)
  2. {
  3.         if(set==0)
  4.                 XGpio_DiscreteWrite(&gpio_user_start, 1, 0x0);//start dma
  5.         else
  6.                 XGpio_DiscreteWrite(&gpio_user_start, 1, 0x1);//start dma
  7. }
复制代码

5.5地址参数定义
该部分定义必须和fdma_dbuf 中的参数保持一致
  1. #define UIFDMA_DBUF_WBASEADDR 0x08000000
  2. #define UIFDMA_DBUF_BUFSIZE 0x100000 //2^20
  3. #define UIFDMA_DBUF_WBUF_DEPTH 2048
  4. #define UIFDMA_DBUF_WDATAWITH 32
  5. #define UIFDMA_DBUF_WXSIZE  2048
  6. #define UIFDMA_DBUF_WSTRIDE 2048
  7. #define UIFDMA_DBUF_WYSIZE  128
  8. #define RX_BUFFER0_BASE    (UIFDMA_DBUF_WBASEADDR +  UIFDMA_DBUF_BUFSIZE*0)
  9. #define RX_BUFFER1_BASE    (UIFDMA_DBUF_WBASEADDR +  UIFDMA_DBUF_BUFSIZE*1)
  10. #define RX_BUFFER2_BASE    (UIFDMA_DBUF_WBASEADDR +  UIFDMA_DBUF_BUFSIZE*2)
  11. #define FDMA_BUFSIZE       UIFDMA_DBUF_WDATAWITH/8*UIFDMA_DBUF_WXSIZE*UIFDMA_DBUF_WYSIZE
复制代码

再看下FDMA DBUF IP设置,这里只用到写通道
e14e8e79147d452b8ec6bda407f91c2c.jpg
5.6读取数据
只要fdma_buf.pkg_done_cnt>0代表中断后有缓存已经准备好了数据,下面的代表把一包数据拆分为64份,每份大小16KB.通过fdma_buf.record[fdma_buf.next]知道下一个需要读取的数据缓存地址
  1. void fdma_dbuf_test(void)
  2. {
  3.         RxBufAddr[0] =RX_BUFFER0_BASE;
  4.         RxBufAddr[1] =RX_BUFFER1_BASE;
  5.         RxBufAddr[2] =RX_BUFFER2_BASE;
  6.         while(1)
  7.         {
  8.                 if(first_trans_start==0)
  9.                 {
  10.                         first_trans_start =1;
  11.                         fdma_wr_set(1);
  12.                         fdma_buf.circle_cnt=0;
  13.                         fdma_buf.next=0;
  14.                         fdma_buf.pkg_done_cnt=0;
  15.                         fdma_buf.pkg_cnt=0;
  16.                         fdma_buf.fram_cnt=0;
  17.                 }
  18.                 if(fdma_buf.pkg_done_cnt> 0) // total_data_size divide in to 64 times as each time 16kbytes
  19.                 {
  20.                         if(fdma_buf.pkg_cnt==0)
  21.                         {
  22.                                 BufStartPtr = (u32*)(RxBufAddr[fdma_buf.record[fdma_buf.next]]);//get new buffer address
  23.                                 Xil_DCacheFlushRange((INTPTR) BufStartPtr, FDMA_BUFSIZE) ;
  24.                         }
  25.                         fdma_buf.pkg_cnt++;
  26.                         if(fdma_buf.pkg_cnt==64)
  27.                         {
  28.                                 fdma_buf.pkg_done_cnt--;
  29.                                 fdma_buf.pkg_cnt = 0;
  30.                                 fdma_buf.fram_cnt++;
  31.                                 if(fdma_buf.next<2)
  32.                                         fdma_buf.next++;
  33.                                 else
  34.                                         fdma_buf.next=0;
  35.                         }
  36.                 }
  37.         }
  38. }
复制代码

6方案演示
6.1硬件准备
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON (注意新版本的 MLK-H3-CZ08-7100FC(米联客 7X 系列),支持 JTAG 模式,对于老版本的核心板,JTAG 调试 的时候一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
以下图片中,TF 卡没有使用到
d8f2fc6b3e6b4cb4ac197015203a07e5.jpg
6.2实验结果
右击工程,选择调试
129d0a49cbd4475ca10b82a7ba540799.jpg
fe23c2acfe5b48e1af21deb916c9a6dd.jpg
添加3个缓存的起始地址,用于观察数据
8ada5bb781634259aa50f79046030bb7.jpg
设置代码调试断点
56cb7101add14e7f8edfba0d34d73c45.jpg
运行到断点处
20551e1f729f49cc99081eb57a9c3bed.jpg
通过JTAG观察到的内存数据和我们FPGA端发送的测试数据完全一致
c22e433057404a938cae5415bd55cbe6.jpg
FPGA端通过ila在线逻辑分析仪查看的测试数据
c523c5466d7a4979a1916650a7de1188.jpg
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则