[X]关闭

米联客-XILINX-H3_CZ08_7100] FPGA_AXI总线入门连载-08 使用 fdma 读写 axi-bram

文档创建者:FPGA课程
浏览次数:169
最后更新:2024-10-14
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 3-FPGA AXI 总线入门
本帖最后由 FPGA课程 于 2024-10-14 18:21 编辑


软件版本: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 概述
基于 FDMA 可以完成很多数据读写存储类的应用,本文将展示通过 FDMA 读写 AXI-BRAM 本文实验目的:
1:掌握基于 uiFDMA3.0 的 FPGA 工程设计
2:利用 uiFDMA3.0 提供的接口,编写 BRAM 测试程序 3:对 AXI-BRAM 读写仿真和测试
2 搭建 FPGA 图形化工程
2.1 创建 Block Design 并且命名为 system
c8a7481c214943d6ac21c07818c7f2fa.jpg
如下图所示,图形化 system 就是一个代码容器,接着我们要添加一些图像化的 IP
b79677637b80452084f544fcdd6e8b99.jpg

2.2 添加图形化 FPGA IP
首先设置自定义 IP 的路径,这里读者可以把我们配套工程根路径下的 uisrc 文件夹复制到目前的工程根路径,单击 Settings 在弹出的 Settings 窗口选择 IP->Repository  设置如下路径
47073921922545e2ad3b72afa716deae.jpg
添加+号添加 IP
5729bfbc1e3847e0a29a299a98333a6a.jpg
比如输入关键词 FDMA 就可以搜索到我们米联客 uiFDMA IP(注意最新版本是 3.0 版本)
90b1f7c91bac48a0b2d83d2fb20c139a.jpg

b5ed9f732f0040ee82257d55ca13b128.jpg
用类似的方法添加必要的 IP 如下图所示:
cdba5479dd414651be5ea37b7faac5e6.jpg
2.3 完成 IP 之间的信号自动
这种简单的工程可以先让软件先自动化线,然后根据结果手动一些调整
cd8a5372874d4d6da989f510158d4229.jpg
可以看到连线结果
256667ad048a4ef7b4e9dd20ff6d06d1.jpg
2.4 调整 IP 参数
1:BRAM 参数设置
首先把 IP 的配置参数修改下,双击需要设置的IP 可以进行参数设置 FDMA 设置数据位宽 128bit  可以访问内存地址位宽 32bit  其他默认
150949d8ddc54490b7285bdb04c825a5.jpg
BRAM 设置,使用 BRAM Controller  为真双口 RAM
a71fbf7859b54166ac6bbd80662ca4cc.jpg

d509c2c8ea0d4604bc652e63b341ec59.jpg

6f93c522f56b4adbbbdf8eecb70d57c3.jpg
2:BRAM Controller 参数设置
AXI BRAM Controller 设置 axi4 协议,数据位宽 128bit  读延迟 1 个时钟
1aeaa1da4d0b4d25a2e4a0c87bb96a24.jpg
3:Clocking Wizard 参数设置
477c5b887e2a4a218bd70df8ea5e0d2c.jpg


605d4957b9954d668f00c87182894c99.jpg
4:AXI Interconnect IP 设置
双击 AXI Interconnect IP  进行设置
2993d9120653496680c858150ad8e68d.jpg
设置 AXI Interconnect IP 的性能参数,其中 Enable Register Slice  用于改善时序,Enable Data FIFO 用于增加 FIFO 大 小,增加数据传输效率
86ad5f92e029493b921d44895282013f.jpg

2.5 引出 FPGA 接口信号
分别右击下图 2 个 IP,然后选择 Make External ,把需要引出到顶层的 FPGA 信号引出
541d47aba6ae431897a06180066fefc4.jpg
为了引出时钟需先右击信号 PIN 脚断开连接,然后 Make External ,之后重新连接
18ff6b7ac58d4014a1b58d5971ee1971.jpg

6fc8381e1a0e403f9f5ef0a022e6cc16.jpg


4b32bc0be0e141bab3153c7ae39ca07a.jpg
修改后重新连接时钟

801e82f62cc0464382fad5955227fc3c.jpg
2.6 修改信号名字
修改默认的时钟名字
155488c116474593b27ffcbf0541d756.jpg


2b6246f7dc1e478296ad87e16199401e.jpg
2.7 连线最后的遗漏
软件还在提示我们有线没有连接,这种提示是建立在 XILINX 自带的 IP,软件可以识别一部分比如复位时钟等, 不是可以识别所有的信号,一般再最后我们如果看到还有这种提示说明还有未完成的连线
b2b03aa0b7b740e9bbca5e4580d94c7f.jpg
这里是 ext_reset_in 没有连接,也是接到 Clocking Wizard 的 Locked
21a0306e347d4f41b63d1448c366a209.jpg
2.8 视图优化
右击空白处,弹出菜单选择 Regenerate Layout 优化下视图

3719bcb237a442e6b520cedebcd9498f.jpg
ddb7bfdc9b4744d4a00a067654490fd7.jpg
2.9 地址分配
AXI 总线必须分配地址,设置 uiFDMA 的地址空间分配,起始地址可以任意设置,我们设置从 0x0000_0000 开始, 大小 64KB
36575152fd794cacbaecd6ac99373c27.jpg
2.10 自动校验
保存工程

ec8fe0cfd6d54b5c85d60542297d8137.jpg
单击下图中的控件可以对图形化编程进行校验
2205d1d2c4c14382a83a53beda5beecd.jpg
2.11 自动产生顶层文件
右击 system,在弹出的菜单中选择 Create HDL Wrapper
61de9679c27249328974193327de03ab.jpg
6c3d509907b145638b24e0d13030c267.jpg

b1c46e165b9547cfbbddf6eabc77af0f.jpg
3 编写 FDMA 的 BRAM 测试代码
刚刚以上自动产生的顶层文件,只是引出的信号接口,并不能完成对 FDMA 的控制,所以我们需要自定义一 个顶层文件,可以复制刚才产生的文件,修改,这样可以省一些编写调用接口的时间。
为了方便文件的管理,我们新建一个 fdma_bram_test.v 文件,并且复制以上代码,到这个文件夹。
ebb79268425e4c5b9de44dbb9b2f686f.jpg
右击删除刚刚自动产生的文件
c122b9c349034eb585f9012cb15ea156.jpg
添加 fdma_bram_test.v 文件
1607600ccbf948e29ae6f47fe615cca6.jpg
9e6454e2aa014efea4cdd60c2924beed.jpg
fdma_bram_test.v 文件
  1. `timescale 1ns / 1ps
  2. module fdma_bram_test(
  3. input          sysclk_p,
  4. input          sysclk_n
  5. );

  6. wire [31:0]   fdma_raddr;
  7. reg           fdma_rareq;
  8. wire          fdma_rbusy;
  9. wire [127:0]  fdma_rdata;
  10. wire [15:0]   fdma_rsize;
  11. wire          fdma_rvalid;
  12. wire [31:0]   fdma_waddr;
  13. reg           fdma_wareq;
  14. wire          fdma_wbusy;
  15. wire [127:0]  fdma_wdata;
  16. wire [15:0]   fdma_wsize;
  17. wire          fdma_wvalid;
  18. wire [0:0]    fdma_rstn;
  19. wire          ui_clk;

  20. parameter TEST_MEM_SIZE   = 32'd64*1024;//64KB
  21. parameter FDMA_BURST_LEN  = 16'd512;
  22. parameter ADDR_MEM_OFFSET = 0;
  23. parameter ADDR_INC = FDMA_BURST_LEN * 16;

  24. parameter WRITE1 = 0;
  25. parameter WRITE2 = 1;
  26. parameter WAIT   = 2;
  27. parameter READ1  = 3;
  28. parameter READ2  = 4;

  29. reg [31: 0] t_data;
  30. reg [31: 0] fdma_waddr_r;
  31. reg [2  :0] T_S = 0;
  32. wire          sysclk;

  33. //对差分时钟采用 IBUFGDS IP 核去转换
  34. IBUFGDS CLK_U(
  35. .I(sysclk_p),
  36. .IB(sysclk_n),
  37. .O(sysclk)
  38. );


  39. assign fdma_waddr = fdma_waddr_r + ADDR_MEM_OFFSET;
  40. assign fdma_raddr = fdma_waddr;

  41. assign fdma_wsize = FDMA_BURST_LEN;
  42. assign fdma_rsize = FDMA_BURST_LEN;
  43. assign fdma_wdata ={t_data,t_data,t_data,t_data};
  44.   
  45.   
  46. //delay reset
  47. reg [8:0] rst_cnt = 0;
  48. always @(posedge ui_clk)
  49.     if(rst_cnt[8] == 1'b0)
  50.          rst_cnt <= rst_cnt + 1'b1;
  51.      else
  52.          rst_cnt <= rst_cnt;

  53. always @(posedge ui_clk)begin
  54.     if(rst_cnt[8] == 1'b0)begin
  55.         T_S <=0;   
  56.         fdma_wareq  <= 1'b0;
  57.         fdma_rareq  <= 1'b0;
  58.         t_data<=0;
  59.         fdma_waddr_r <=0;      
  60.     end
  61.     else begin
  62.         case(T_S)      
  63.         WRITE1:begin
  64.             if(fdma_waddr_r==TEST_MEM_SIZE) fdma_waddr_r<=0;
  65.                 if(!fdma_wbusy)begin
  66.                     fdma_wareq  <= 1'b1;
  67.                     t_data  <= 0;
  68.                 end
  69.                 if(fdma_wareq&&fdma_wbusy)begin
  70.                     fdma_wareq  <= 1'b0;
  71.                     T_S         <= WRITE2;
  72.                 end
  73.         end
  74.         WRITE2:begin
  75.             if(!fdma_wbusy) begin
  76.                  T_S <= WAIT;
  77.                  t_data  <= 32'd0;
  78.             end
  79.             else if(fdma_wvalid) begin
  80.                 t_data <= t_data + 1'b1;
  81.             end
  82.         end
  83.         WAIT:begin//not needed
  84.             T_S <= READ1;
  85.         end
  86.         READ1:begin
  87.             if(!fdma_rbusy)begin
  88.                 fdma_rareq  <= 1'b1;
  89.                 t_data   <= 0;
  90.             end
  91.             if(fdma_rareq&&fdma_rbusy)begin
  92.                  fdma_rareq  <= 1'b0;
  93.                  T_S         <= READ2;
  94.             end
  95.         end
  96.         READ2:begin
  97.             if(!fdma_rbusy) begin
  98.                  T_S <= WRITE1;
  99.                  t_data  <= 32'd0;
  100.                  fdma_waddr_r  <= fdma_waddr_r + ADDR_INC;//128/8=16
  101.             end
  102.             else if(fdma_rvalid) begin
  103.                 t_data <= t_data + 1'b1;
  104.             end
  105.         end   
  106.         default:
  107.             T_S <= WRITE1;     
  108.         endcase
  109.     end
  110.   end
  111.   
  112. wire test_error = (fdma_rvalid && (t_data[15:0] != fdma_rdata[15:0]));

  113. ila_0 ila_dbg (
  114.         .clk(ui_clk),
  115.         .probe0({fdma_wdata[15:0],fdma_wareq,fdma_wvalid,fdma_wbusy}),
  116.         .probe1({fdma_rdata[15:0],t_data[15:0],fdma_rvalid,fdma_rbusy,T_S,test_error})
  117. );

  118.   system system_i
  119.        (.FDMA_S_0_fdma_raddr(fdma_raddr),
  120.         .FDMA_S_0_fdma_rareq(fdma_rareq),
  121.         .FDMA_S_0_fdma_rbusy(fdma_rbusy),
  122.         .FDMA_S_0_fdma_rdata(fdma_rdata),
  123.         .FDMA_S_0_fdma_rready(1'b1),
  124.         .FDMA_S_0_fdma_rsize(fdma_rsize),
  125.         .FDMA_S_0_fdma_rvalid(fdma_rvalid),
  126.         .FDMA_S_0_fdma_waddr(fdma_waddr),
  127.         .FDMA_S_0_fdma_wareq(fdma_wareq),
  128.         .FDMA_S_0_fdma_wbusy(fdma_wbusy),
  129.         .FDMA_S_0_fdma_wdata(fdma_wdata),
  130.         .FDMA_S_0_fdma_wready(1'b1),
  131.         .FDMA_S_0_fdma_wsize(fdma_wsize),
  132.         .FDMA_S_0_fdma_wvalid(fdma_wvalid),
  133.         .sysclk(sysclk),
  134.         .ui_clk(ui_clk)
  135.         );        

  136. endmodule
复制代码

4RTL 仿真
4.1 仿真 tb 文件
编写仿真 tb 文件,fdma_bram_test_tb.v ,非常简单,只需要给一个时钟
  1. `timescale 1ns / 1ps
  2. module fdma_bram_test_tb();
  3. reg sysclk_p;
  4. reg sysclk_n;
  5. fdma_bram_test fdma_bram_test_inst
  6. (
  7.     .sysclk_p(sysclk_p),
  8.     .sysclk_n(sysclk_n),
  9. );
  10. initial begin
  11.    sysclk_p  <= 1'b0;   //系统时钟设置初值
  12.    sysclk_n  <= 1'b1;
  13.      #100;
  14. end

  15. always #5 sysclk_p = ~sysclk_p;
  16. always #5 sysclk_n = ~sysclk_n;
  17. endmodule
复制代码
添加 fdma_bram_test_tb.v 到工程中
e812b152a9c1498c8e0cba359160b517.jpg
a6675248ae1b41bca67de553c55539ef.jpg
4.2 仿真测试
进行 RTL 行为仿真
845d86fef9c84b9d924da805a2e7b692.jpg
5909e74a542b45ccb9e2c1a2077c8658.jpg
放大后观察数据
1ffdd364988043619006f0015a4885c8.jpg
我们也可以继续深入看 FDMA 源码里面的信号工作情况,这个读者可以自己区分析下,我们米联客计划出一 份专门讲解 AXI 总线部分的教程,里面详细分析 FDMA 的设计过程,以及信号仿真。本章节主要是侧重 FDMA 的 应用,只要会熟练应用可以了!
5 编译测试
添加 ila IP CORE  和 fpga pin 定义
0e2cd62a6757423cbc114526a594204e.jpg
ila 的设置如下
750c4dadabdb487083c13c21b343a975.jpg
4ab89b87addc4fbf975eafcad7f2440b.jpg
编译产生 bit
05f831ca95024fba9c80506afb7ade34.jpg
下载程序到开发板测试,通过在线逻辑分析仪观察
d73f356c72854287ac85a62ee7be0d10.jpg
b1466b77cf5148a88613d706303e9e34.jpg
6 程序分析
6.1 总流程图
如下图所示,本文的程序工作流程如下,包括请求写数据、FDMA收到写数据请求、写数据完成、请求读数据、FDMA收到读数据请求和读数据完成、校验。
7b14ea65efea415088c95938ce9a531b.jpg
6.2FDMA 的读写时序
以下是 fdma_test.v 中读写 fdma ip 接口的状态机,由于读写代码对称,对 fdma 的读写操作可以分为 2 步完成,分别 是:1:发送读/写请求 2:读/写有效数据。
fe63f010fa9e4f2f85d004eb32b4dcd9.jpg
1:FDMA 的写时序
70080c2232a14f2a813912c198ca44f5.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 总线协议的使用。
2:FDMA 的读时序
cff2bb0ab5de4890bc17236be61a8f3a.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 总线协议的使用。



















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

本版积分规则