zynq processing system 参数设置_【正点原子FPGA连载】第十四章基于BRAM的PS和PL的数据交互领航者 ZYNQ 之嵌入式开发指南...

1)实验平台:正点原子领航者ZYNQ开发板

2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761

3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html

4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900

5)关注正点原子公众号,获取最新资料

http://weixin.qq.com/r/hEhUTLbEdesKrfIv9x2W (二维码自动识别)

第十四章基于BRAM的PS和PL的数据交互


在ZYNQ SOC开发过程中,PL和PS之间经常需要做数据交互。对于传输速度要求较高、数据量大、地址连续的场合,可以通过AXI DMA来完成。而对于数据量较少、地址不连续、长度不规则的情况,此时AXI DMA便不再适用了。针对这种情况,可以通过BRAM来进行数据的交互。本章我们来学习下基于BRAM的PS和PL的数据交互。
本章包括以下几个部分:
1414.1简介
14.2实验任务
14.3硬件设计
14.4软件设计
14.5下载验证
14.1简介
BRAM(Block RAM)是PL部分的存储器阵列,PS和PL通过对BRAM进行读写操作,来实现数据的交互。在PL中,通过输出时钟、地址、读写控制等信号来对BRAM进行读写操作(关于BRAM的操作时序,请参考“RAM IP核实验”);而在PS中,处理器并不需要直接驱动BRAM的端口,而是通过AXI BRAM控制器来对BRAM进行读写操作。AXI BRAM控制器是集成在Vivado设计软件中的软核,可以配置成AXI4-lite接口模式或者AXI4接口模式。AXI4-Lite 接口模式的框图如图 14.1.1所示。

2c10a1ee5628feb15ec06b6784cc33e6.png

图 14.1.1 AXI4-Lite BRAM控制器框图


AXI4接口模式的BRAM控制器支持的数据位宽为32位、64位、128位、512位和1024位,而AXI4-Lite接口仅支持32位数据位宽。由图 14.1.1可知,PS通过AXI4-Lite接口访问BRAM,当使能ECC选项时,ECC允许AXI主接口检测和纠正BRAM块中的单位和双位错误。AXI BRAM控制器作为AXI总线的从接口,和AXI主接口实现互联,来对BRAM进行读写操作。针对不同的应用场合,该IP核支持单次传输和突发传输两种方式。
14.2实验任务
本章的实验任务是PS将串口接收到的数据写入BRAM,然后从BRAM中读出数据,并通过串口打印出来;与此同时,PL从BRAM中同样读出数据,并通过ILA来观察读出的数据与串口打印的数据是否一致。
14.3硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:

fa02b34466f6c265051c6ae7253c5abd.png

图 14.3.1 系统框图


在图 5.3.1中,PS端的M_AXI_GP0作为主端口,与PL端的AXI BRAM控制器IP核和PL读BRAM IP核(pl_bram_rd)通过AXI4总线进行连接。其中,AXI互联IP(AXI Interconnect)用于连接AXI存储器映射(memory-mapped)的主器件和从器件;AXI BRAM控制器作为PS端读写BRAM的IP核;PL读BRAM IP核是我们自定义的IP核,实现了PL端从BRAM中读出数据的功能,除此之外,PS端通过AXI总线来配置该IP核读取BRAM的起始地址和个数等。
由框图可知,本次实验创建的BRAM为双端口的RAM,其中一个端口连接AXI BRAM控制器,另一个连接PL读BRAM IP核。
首先创建Vivado工程,工程名为“ps_pl_bram”,然后创建Block Design设计(system.bd)并添加ZYNQ7 Processing System模块。接下来按照《“Hello World”实验》中的步骤2-7、2-8分别配置PS的UART和DDR控制器。需要特别注意的是,我们在《“Hello World”实验》的步骤2-10中,移除了PS中与PL端交互的接口信号,这些接口在我们本次实验中需要予以保留。
最后点击右下角的“OK”,本次实验ZYNQ处理系统就配置完成了。
ZYNQ7 PS配置完成后其接口如图 5.3.4所示:

5fc257cbe94409295c01da4c47c73f11.png

图 14.3.2 ZYNQ7模块接口


接下来我们要在Block Design中添加AXI BRAM Controller IP核,在Diagram窗口空白位置右击,然后选择“Add IP”。在弹出的IP目录中搜索“AXI BRAM”,最后双击搜索结果中的“AXI BRAM Controller”将其添加到设计中,如下图所示:

4d6588b35a19b0959f2ac51d690fd824.png

图 14.3.3 添加AXI GPIO IP核


添加完成后,双击AXI BRAM Controller IP核,打开其配置界面如下图所示:

114c96a1ddbc73b1f5852a1390e0e7f7.png

图 14.3.4 BRAM控制器配置页面


在图 14.3.4中,AXI Protocol(AXI 协议)选择的是AXI4,对于本次实验来说,选择AXI4或者AXI4-Lite没有影响;Data Width(数据位宽)选择32位,由于AXI4总线为字节寻址,因此在映射到BRAM地址时,需要按4字节寻址。本次实验的BRAM控制器只需要读写BRAM的一个端口,因此将BRAM的总线个数设置为1;ECC选项用于数据错误纠正与检查,这里不使能。
需要说明的是,Memory Depth(存储深度)在这里不可以设置,寻址BRAM的存储深度是在Address Editor里设置的,最后点击“OK”按钮完成设置。
接下来在Block Design中添加BRAM IP核,即Block Memory Generator IP核。在Diagram窗口空白位置右击,然后选择“Add IP”。在弹出的IP目录中搜索“Block Memory”,最后双击搜索结果中的“Block Memory Generator”将其添加到设计中,如下图所示:

889dc76275e1410c8886cdcb7a3e3c4e.png

图 14.3.5 添加Block Memory Generator IP核


添加完成后,双击Block Memory Generator IP核,打开其配置界面如下图所示:

96eff4345b361bc1beeb89694374bbfb.png

图 14.3.6 Block Memory Generator IP核配置页面


BRAM IP核支持两种模式,一种是独立模式(Stand Alone),在此模式下,可以自由配置RAM的数据深度和宽度;另一种是BRAM控制器模式(BRAM Controller),在此模式下,地址和数据默认为32位,由于本次实验添加了BRAM控制器IP核,因此BRAM模式选择BRAM控制器模式。
Memory Type(存储类型)设置为“True Dual Port RAM”,即真双口RAM。一端连接PL读BRAM IP核,另一端连接BRAM控制器。
接下来将页面切换至“Other Options”的选项,如下图所示:

e19993d13c9abcbcf9314a8113595e6f.png

图 14.3.7 Block Memory Generator IP核“Other Options”配置页面


BRAM IP核内置了一个安全电路以降低BRAM数据出现错误的概率,如果勾选使能安全电路,BRAM端口会增加rsta_busy端口和rstb_busy端口,用于表示何时可以访问BRAM。这里直接取消使能安全电路,最后点击“OK”按钮完成设置。
添加完成后Diagram窗口如下图所示:

38373edd783c0f831db38697610ba55f.png

图 14.3.8 Diagram窗口


接下来点击图 14.3.8中箭头1所指示的位置,在弹出的对话框左侧确认勾选processing_system7_0,然后点击“OK”。这一步骤会将ZYNQ7 PS模块的DDR和FIXED_IO端口引出。
然后点击图 14.3.8中箭头2所指示的位置,弹出对话框如下图所示:

e19993d13c9abcbcf9314a8113595e6f.png

图 14.3.9 自动连接


在左侧勾选“All Automation”,下面列出了会自动连接的模块及其接口。点击“OK”,工具会自动连接AXI BRAM Controller IP核的BRAM_PORTA接口和S_AXI接口。
连接完成后,在Diagram窗口空白处右击,然后选择“Regenerate Layout”对设计进行重新布局,布局后的界面如下图所示:

38373edd783c0f831db38697610ba55f.png

图 14.3.10 重新布局后的设计界面


在图 5.3.8中,箭头1指向的AXI SmartConnect是一种新型系统连接生成器,同样也是实现将一个或多个内存映射主设备连接到一个或多个内存映射从设备,一般可以作为AXI InterConnect的替代品,且具有更好的性能。箭头2指向的“Block Memory Generator”IP核,其中一个端口连接到了AXI BRAM控制器,另一个端口连接PL读BRAM IP核,这个IP核是我们自定义的IP核,接下来添加这个IP核。
自定义的IP核在例程工程目录下的ip_repo文件夹下,如图 14.3.11所示。大家可以直接拷贝这个文件夹至工程目录下,然后通过菜单栏的Tool→Setting→IP→Repository,点击“ADD”图标来添加自定义的IP核。由于本次实验的自定义IP核的部分设置和“自定义IP核-呼吸灯实验”有些区别,因此我们接下来在本次实验工程的基础上,来向大家演示创建一个新的自定义IP核。

683b087b9a89104e4a6acc68d4e9e2eb.png

图 14.3.11 ip_repo文件


点击菜单栏的“Tools”,选择“Creat and Package New IP…”,如下图所示:

d3596bc53b8fe8e288f69e9eb8ee634b.png

图 14.3.12 创建一个新的自定义IP核


此时弹出创建和封装新IP核的页面,点击“NEXT”,选择“Create a new AXI4 Peripheral”,再次点击“NEXT”,进入下图所示页面。

04faf2de2b5adaecc19438d55f4dc61e.png

图 14.3.13 自定义IP核设置页面


在Name一栏的名称改为“pl_bram_rd”,IP核的路径改为工程目录下的ip_repo文件夹,即删除路径“/../”中间的一个“.”符号,其它的设置直接保持默认即可,点击“NEXT”,直到最后点击“Finish”按钮完成自定义IP核的创建。
在“IP Catalog”界面下,依次展开User Repository→AXI Peripheral→“pl_bram_rd_v1.0”,右击“pl_bram_rd_v1.0”,选择“Edit in IP Packager”,如下图所示:

48d0077824e195c3a754dec1f38d7f44.png

图 14.3.14 编辑IP核


在弹出的页面中,点击“OK”,进入编辑自定义IP核的工程界面。
打开pl_bram_rd_v1_0.v文件,在Users to add ports here和User port ends中间行添加如下代码,这些端口用于连接BRAM端口的BRAM_PORTB。

  1. 17 // Users to add ports here
  2. 18 //RAM端口
  3. 19 output wire ram_clk , //RAM时钟
  4. 20 input wire [31:0] ram_rd_data, //RAM中读出的数据
  5. 21 output wire ram_en , //RAM使能信号
  6. 22 output wire [31:0] ram_addr , //RAM地址
  7. 23 output wire [3:0] ram_we , //RAM读写控制信号
  8. 24 output wire [31:0] ram_wr_data, //RAM写数据
  9. 25 output wire ram_rst , //RAM复位信号,高电平有效
  10. 26 // User ports ends
  11. 在实例化pl_ram_rd_v1_0_S00_AXI模块的位置,添加以下代码。
  12. 57 //RAM端口
  13. 58 .ram_clk (ram_clk ),
  14. 59 .ram_rd_data (ram_rd_data),
  15. 60 .ram_en (ram_en ),
  16. 61 .ram_addr (ram_addr ),
  17. 62 .ram_we (ram_we ),
  18. 63 .ram_wr_data (ram_wr_data),
  19. 64 .ram_rst (ram_rst ),
  20. 打开pl_bram_rd_v1_0_S00_AXI.v文件,同样在Users to add ports here和User port ends中间行添加如下代码:
  21. 17 // Users to add ports here
  22. 18 //RAM端口
  23. 19 output wire ram_clk , //RAM时钟
  24. 20 input wire [31:0] ram_rd_data, //RAM中读出的数据
  25. 21 output wire ram_en , //RAM使能信号
  26. 22 output wire [31:0] ram_addr , //RAM地址
  27. 23 output wire [3:0] ram_we , //RAM读写控制信号
  28. 24 output wire [31:0] ram_wr_data, //RAM写数据
  29. 25 output wire ram_rst , //RAM复位信号,高电平有效
  30. 26 // User ports ends
  31. 在程序最后Add user logic here和User logic ends的中间行,添加如下代码:
  32. 407 // Add user logic here
  33. 408 bram_rd u_bram_rd(
  34. 409 .clk (S_AXI_ACLK),
  35. 410 .rst_n (S_AXI_ARESETN),
  36. 411 .start_rd (slv_reg0[0]),
  37. 412 .start_addr (slv_reg1),
  38. 413 .rd_len (slv_reg2),
  39. 414 //RAM端口
  40. 415 .ram_clk (ram_clk ),
  41. 416 .ram_rd_data (ram_rd_data),
  42. 417 .ram_en (ram_en ),
  43. 418 .ram_addr (ram_addr ),
  44. 419 .ram_we (ram_we ),
  45. 420 .ram_wr_data (ram_wr_data),
  46. 421 .ram_rst (ram_rst )
  47. 422 );
  48. 423 // User logic ends


这段代码例化了bram_rd模块,其中start_rd信号是开始读BRAM的开始信号,start_addr是设置BRAM的读起始地址,rd_len是设置读BRAM的个数,分别连接到AXI4-Lite总线的寄存器地址0、地址1和地址2对应的数据。
接下来在工程中创建一个新的模块,命名为“bram_rd”,位于../ps_pl_bram/ip_repo/pl_bram_rd_1.0/hdl路径下,bram_rd模块的代码如下:

  1. 1 module bram_rd(
  2. 2 input clk , //时钟信号
  3. 3 input rst_n , //复位信号
  4. 4 input start_rd , //读开始信号
  5. 5 input [31:0] start_addr , //读起始地址
  6. 6 input [31:0] rd_len , //读数据的长度
  7. 7 //RAM端口
  8. 8 output ram_clk , //RAM时钟
  9. 9 input [31:0] ram_rd_data, //RAM中读出的数据
  10. 10 output reg ram_en , //RAM使能信号
  11. 11 output reg [31:0] ram_addr , //RAM地址
  12. 12 output reg [3:0] ram_we , //RAM读写控制信号
  13. 13 output reg [31:0] ram_wr_data, //RAM写数据
  14. 14 output ram_rst //RAM复位信号,高电平有效
  15. 15 );
  16. 16
  17. 17 //reg define
  18. 18 reg [1:0] flow_cnt;
  19. 19 reg start_rd_d0;
  20. 20 reg start_rd_d1;
  21. 21
  22. 22 //wire define
  23. 23 wire pos_start_rd;
  24. 24
  25. 25 //*****************************************************
  26. 26 //** main code
  27. 27 //*****************************************************
  28. 28
  29. 29 assign ram_rst = 1'b0;
  30. 30 assign ram_clk = clk ;
  31. 31 assign pos_start_rd = ~start_rd_d1 & start_rd_d0;
  32. 32
  33. 33 //延时两拍,采start_rd信号的上升沿
  34. 34 always @(posedge clk or negedge rst_n) begin
  35. 35 if(!rst_n) begin
  36. 36 start_rd_d0 <= 1'b0;
  37. 37 start_rd_d1 <= 1'b0;
  38. 38 end
  39. 39 else begin
  40. 40 start_rd_d0 <= start_rd;
  41. 41 start_rd_d1 <= start_rd_d0;
  42. 42 end
  43. 43 end
  44. 44
  45. 45 //根据读开始信号,从RAM中读出数据
  46. 46 always @(posedge clk or negedge rst_n) begin
  47. 47 if(!rst_n) begin
  48. 48 flow_cnt <= 2'd0;
  49. 49 ram_en <= 1'b0;
  50. 50 ram_addr <= 32'd0;
  51. 51 ram_we <= 4'd0;
  52. 52 end
  53. 53 else begin
  54. 54 case(flow_cnt)
  55. 55 2'd0 : begin
  56. 56 if(pos_start_rd) begin
  57. 57 ram_en <= 1'b1;
  58. 58 ram_addr <= start_addr;
  59. 59 flow_cnt <= flow_cnt + 2'd1;
  60. 60 end
  61. 61 end
  62. 62 2'd1 : begin
  63. 63 if(ram_addr - start_addr == rd_len - 4) begin //数据读完
  64. 64 ram_en <= 1'b0;
  65. 65 flow_cnt <= flow_cnt + 2'd1;
  66. 66 end
  67. 67 else
  68. 68 ram_addr <= ram_addr + 32'd4; //地址累加4
  69. 69 end
  70. 70 2'd2 : begin
  71. 71 ram_addr <= 32'd0;
  72. 72 flow_cnt <= 2'd0;
  73. 73 end
  74. 74 endcase
  75. 75 end
  76. 76 end
  77. 77
  78. 78 endmodule


程序实现了根据输入的读开始信号(start_rd)、读起始地址(start_addr)和读数据长度(rd_len)从RAM中读出数据。
代码全部保存完成后,双击IP-XACT界面下的component.xml切换至Packaging Steps界面。点击“File Groups”一栏,随后点击界面上的“Merge changes from File Groups Wizard”;点击“Customization Parameters”一栏,随后点击界面上的“Merge changes from Customization Parameters Wizard”。
点击“Ports and Interfaces”一栏,可以看到该IP核顶层模块的端口,为了方便在Diagram界面中对自定义的IP核的BRAM端口进行连线,我们需要将BRAM相关的端口定义成总线接口的形式,方法如下。
点击“Add Bus Interfac”图标,如下图所示:

a63a051506e7d92eee5ca47bba8d7b86.png

图 14.3.15 添加总线接口


在弹出的页面中,在Name一栏,输入BRAM_PORT,然后点击Interface Definition(总线定义)右侧的“…”图标,如下图所示:

8ed0b271faf58810d810a54d8869ff31.png

图 14.3.16 选择一个总线接口


接下来在弹出页面的搜索框中输入“BRAM”,选中Advanced一栏下的“bram_rtl”,即BRAM总线接口。然后点击“OK”按钮,如下图所示:

95e13206bf5dcb721e8ce906b920b92d.png

图 14.3.17 选择“bram_ctrl”


将页面切换至“Port Mapping”选项页,左侧窗口为总线逻辑端口,右侧窗口为IP核定义的物理端口,最下面的窗口是映射端口的总结页面,如下图所示:

f22d6cf6bf8b8776d21d776a22354474.png

图 14.3.18 端口映射页面


点击左侧的“EN”端口,然后点击右侧的“ram_en”端口,此时页面上的“Map Ports”变成可以点击的按钮,点击这个按钮,此时在映射端口总结页面可以看到这两个端口映射到了一起,如下图所示:

1bb597a16249d898b610afb0ded8233b.png

图 14.3.19 映射“EN”端口和“ram_en”端口


这个步骤是将顶层模块定义的“ram_en”端口和BRAM的“EN”端口映射到一起,我们接下来将其余接口分别一一映射,如图 14.3.20和图 14.3.21所示:

59479e4d4d5b10e15482839b338ac095.png

图 14.3.20 映射端口总结页面1

164213c64d94e059dcba2728ee874049.png

图 14.3.21 映射端口总结页面2


接下来将页面切换至“Parameters”选项页,展开Auto-calculated,选中“MASTER_TYPE”,然后点击单个向右的箭头,如下图所示:

fd2c3cfeac71c87169c789f6092fe389.png

图 14.3.22 添加“MASTER_TPYE”参数


此时,“MASTER_TYPE”参数会出现在右侧Overridden目录下,最后点击“OK”按钮完成BRAM接口的封装,如下图所示:

14511e8a153f79c9cd85053509437bed.png

图 14.3.23 BRAM接口封装完成页面


接下来切换至“Review and Package”页面,点击上侧的“IP has been modified”来更新IP,最后点击“Re-Package IP”完成IP核的封装,此时IP核的封装界面会自动关闭。
返回本次实验的工程界面,在Diagram界面中添加刚刚封装的IP核。在Diagram窗口空白位置右击,然后选择“Add IP”。在弹出的IP目录中搜索“pl_bram”,最后双击搜索结果中的“pl_bram_rd_v10”将其添加到设计中,如下图所示:

6fc7fc1435287aae06f21e90f8d0542a.png

图 14.3.24 添加“pl_bram_rd”IP核


添加完成后,点击界面上方的“Run Connection Automation”,在弹出页面的左侧勾选“All Automation”,下面列出了会自动连接的模块及其接口。点击“OK”,工具会自动连接PL读BRAM IP核的BRAM_PORT接口和BRAM IP核的BRAM_PORTB接口。
连接完成后,在Diagram窗口空白处右击,然后选择“Regenerate Layout”对设计进行重新布局,布局后的界面如下图所示:

80cdcc7ea6d9f2943293f3538e05e834.png

图 14.3.25 重新布局后的设计界面


接下来切换至“Address Editor”页面,展开“processing_system7_0”下的“Data”,将范围设置成“4K”,如图 14.3.26所示。由于BRAM的数据位宽是32位,因此BRAM的存储深度为1K。

fe9f64ceec947825817ab51593f8ce35.png

图 14.3.26 分配存储空间


到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
在左侧Flow Navigator导航栏中找到SYNTHESIS,点击该选项中的“Run Synthesis”,如下图所示:

f2edf1d82bf99559ccf78c6f26fadfa0.png

图 14.3.27 点击“Run Synthesis”


生成综合文件后,点击“Set Up Debug”来添加信号至ILA界面,如下图所示:

7658b30847d0ee66ba2287e5b45b55de.png

图 14.3.28 点击“Set Up Debug”


依次添加blk_mem_gen_0模块实例化U0模块下的addrb、doutb和enb信号,这三个信号分别对应BRAM PORTB端口的地址、读出的数据和RAM使能信号,如下图所示:

9c76e48622e1339a36c89cb1ea875b11.png

图 14.3.29 添加待观察信号


点击Next按钮,接下来设置采样的深度,采样深度设置的值越大,采集到的数据越多。

550413eb314a389fa816593d31d4a0f9.png

图 14.3.30 ILA设置选项


点击Next按钮,最后点击Finish按钮完成信号的添加,按快捷键“Ctrl + S”保存Synthesized Design。
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成Bitstream之后,在菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中,勾选“Include bitstream”。然后在菜单栏选择File > Launch SDK,启动SDK软件。
14.4软件设计
在SDK软件中新建一个BSP工程和一个空的应用工程,应用工程名为“ps_pl_bram”。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码。代码的主体部分如下所示:

  1. 1 #include "xil_printf.h"
  2. 2 #include "stdio.h"
  3. 3 #include "pl_bram_rd.h"
  4. 4 #include "xbram.h"
  5. 5
  6. 6 #define PL_BRAM_BASE XPAR_PL_BRAM_RD_0_S00_AXI_BASEADDR //PL_RAM_RD基地址
  7. 7 #define PL_BRAM_START PL_BRAM_RD_S00_AXI_SLV_REG0_OFFSET //RAM读开始寄存器地址
  8. 8 #define PL_BRAM_START_ADDR PL_BRAM_RD_S00_AXI_SLV_REG1_OFFSET //RAM起始寄存器地址
  9. 9 #define PL_BRAM_LEN PL_BRAM_RD_S00_AXI_SLV_REG2_OFFSET //PL读RAM的深度
  10. 10
  11. 11 #define START_ADDR 0 //RAM起始地址 范围:0~1023
  12. 12 #define BRAM_DATA_BYTE 4 //BRAM数据字节个数
  13. 13
  14. 14 char ch_data[1024]; //写入BRAM的字符数组
  15. 15 int ch_data_len; //写入BRAM的字符个数
  16. 16
  17. 17 //函数声明
  18. 18 void str_wr_bram();
  19. 19 void str_rd_bram();
  20. 20
  21. 21 //main函数
  22. 22 int main()
  23. 23 {
  24. 24 while(1)
  25. 25 {
  26. 26 printf("Please enter data to read and write BRAMn") ;
  27. 27 scanf("%1024s", ch_data); //用户输入字符串
  28. 28 ch_data_len = strlen(ch_data); //计算字符串的长度
  29. 29
  30. 30 str_wr_bram(); //将用户输入的字符串写入BRAM中
  31. 31 str_rd_bram(); //从BRAM中读出数据
  32. 32 }
  33. 33 }
  34. 34
  35. 35 //将字符串写入BRAM
  36. 36 void str_wr_bram()
  37. 37 {
  38. 38 int i=0,wr_cnt = 0;
  39. 39 //每次循环向BRAM中写入1个字符
  40. 40 for(i = BRAM_DATA_BYTE*START_ADDR ; i < BRAM_DATA_BYTE*(START_ADDR + ch_data_len) ;
  41. 41 i += BRAM_DATA_BYTE){
  42. 42 XBram_WriteReg(XPAR_BRAM_0_BASEADDR,i,ch_data[wr_cnt]) ;
  43. 43 wr_cnt++;
  44. 44 }
  45. 45 //设置BRAM写入的字符串长度
  46. 46 PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_LEN , BRAM_DATA_BYTE*ch_data_len) ;
  47. 47 //设置BRAM的起始地址
  48. 48 PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_START_ADDR, BRAM_DATA_BYTE*START_ADDR) ;
  49. 49 //拉高BRAM开始信号
  50. 50 PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_START , 1) ;
  51. 51 //拉低BRAM开始信号
  52. 52 PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_START , 0) ;
  53. 53 }
  54. 54
  55. 55 //从BRAM中读出数据
  56. 56 void str_rd_bram()
  57. 57 {
  58. 58 int read_data=0,i=0;
  59. 59 //循环从BRAM中读出数据
  60. 60 for(i = BRAM_DATA_BYTE*START_ADDR ; i < BRAM_DATA_BYTE*(START_ADDR + ch_data_len) ;
  61. 61 i += BRAM_DATA_BYTE){
  62. 62 read_data = XBram_ReadReg(XPAR_BRAM_0_BASEADDR,i) ;
  63. 63 printf("BRAM address is %dt,Read data is %cn",i/BRAM_DATA_BYTE ,read_data) ;
  64. 64 }
  65. 65 }


主函数实现了通过串口接收用户数据,然后将用户数据写入BRAM,并从BRAM中读出数据的功能。主函数包含一个无限循环while语句,程序首先打印一串字符串来提示用户开始输入数据,然后通过scanf函数来接收串口的数据,存放在字符串数组ch_data中。这里的“%1024s”是指单次最大接收1024个字符,这是因为BRAM的深度为1024,防止写入BRAM的数据个数超出BRAM的深度。
然后计算接收到的数据个数,并调用自己编写的str_wr_bram函数将数据写入BRAM。数据写完后,调用自己编写的str_rd_bram()函数从BRAM中读出数据。
接下来在程序的第35至53行,将接收到的数据写入BRAM中,并配置PL端开始从BRAM中读取数据。在写BRAM的过程中,通过XBram_WriteReg()函数将接收到的数据按照起始地址,依次写入BRAM中。在数据写入完成后,通过AXI总线,配置PL端读取BRAM的数据个数和起始地址,并驱动PL读开始信号输出一个脉冲信号。
在程序第55至65行PS端读取BRAM中的数据。通过XBram_ReadReg()函数按照BRAM的起始地址,依次从BRAM中读出数据,并通过串口打印出来。
程序设计完成后,按快捷键Ctrl+S保存main.c文件,工具会自动进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
14.5下载验证
首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将USB UART接口与电脑连接,用于串口通信。最后连接开发板的电源,并打开电源开关。
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的BIT文件,来对PL进行配置。最后下载软件程序,下载完成后,在下方的SDK Terminal中可以看到应用程序打印的信息“Please enter data to read and write BRAM”。实验结果如下图所示:

2e49ed22da22d6b87a016883f328509c.png

图 14.5.1 下载验证


此时返回到Vivado软件,在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,展开“Open Hardware Manager”,点击该选项中的“Open Target”,选择“Auto Connect”,如下图所示:
此时会打开ILA观察波形的界面。我们接下来设置触发条件,点击“Trigger Setup”界面下的“+”图标,选择enb信号,将Value的值设置为“R”,如下图所示:

16fbc1bc419203ce4ec7c94e2f85eff0.png

图 14.5.2 添加触发条件


点击工具栏的Run Trigger for this ILA core,此时界面上出现了等待触发的状态,如下图所示:

6d5fe74331075b6904f41bcea424947b.png

图 14.5.3 等待触发


接下来打开SDK的软件界面,在SDK Terminal窗口中输入http://www.openedv.com,并点击“Sned”按钮。此时可以看到串口中打印的信息,如图 14.5.4和图 14.5.5所示:

634dfd8df1583c41f5fd8491cc6a163a.png

图 14.5.4 发送数据界面

8db079a863dcc84f6c0c1992b0298442.png

图 14.5.5 窗口打印信息界面


由上图可知,PS写入BRAM的数据和从BRAM中读出的数据一致。接下来我们在ILA界面观察PL从BRAM中读出的数据是否正确。
返回到Vivado界面,可以看到ILA逻辑分析仪的界面已经显示触发信号的波形。对波形进行放大,将doutb的显示格式改成ASCII,如图 14.5.6和图 14.5.7所示:

2a7ff6ca26241e63aa8b1b40efc492ae.png

图 14.5.6 切换显示的进制

53c882d943d4ccb2725b44204ac9990b.png

图 14.5.7 ILA波形显示界面


由上图可知,在enb拉高期间,doutb输出的数据分别是“www.openedv.com”,和串口助手发送的数据和打印出的数据一致,说明本次实验验证成功。
在这里需要注意的是,如果大家使用其它串口调试助手的时候,需要勾选“发送新行”,否则PS的串口接收程序会一直等待接收串口的数据,串口调试助手的界面设置如下:

545c9c93059a2b15813ffb9b5bf2e709.png

图 14.5.8 串口助手设置界面

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值