目录

一、实验简介

二、vivado部分处理

三、SDK编程

四、实验测试

五、总结


一、实验简介

ZYNQ系列嵌入式FPGA可以使PS将数据写入PL部分BRAM,PL可以将数据读取后再重新写入BRAM,PS将数据读出后再传走,这样可以使PL部分专心处理数据,不用考虑相对缓慢的数据接收与发送部分,并且可以实现数据的缓存。这也是数据量较小时的PS与PL的数据交互方法,当数据量达到BRAM的存储最大值后,就需要使用DDR来进行交互了。

实验目的:通过自定义IP核,将PS写入BRAM的数据加一后重新写入BRAM,再与第一次写入的数据比较。

二、vivado部分处理

首先创建IP核:Tools->Creat and Package New IP,选择创建AXI4 Peripheral:

在 Name 一栏的名称改为“ pl_bram_plus1”, IP 核的路径改为工程目录下的 ip_repo 文件夹,即删除路径“ /../”中间的一个“ .”符号,其它的设置直接保持默认即可,点击“ NEXT”,直到最后点击“ Finish”按钮完成自定义 IP 核的创建。

到这里IP核的框架已经搭建好了,接下来要去进行细节定义。首先打开IP Catalog,依次展开 User Repository→AXI Peripheral→“ pl_bram_plus1_v1.0”,右键选择“ Edit in IP Packager”。

打开 pl_bram_plus1_v1_0.v 文件,在 Users to add ports here 和 User port ends 中间行添加如下代码,这些端口用于连接 BRAM 端口的 BRAM_PORTB。

output wire ram_clk ,                  //RAM 时钟
input wire [31:0] ram_rd_data,         //RAM 中读出的数据
output wire ram_en ,                   //RAM 使能信号
output wire [31:0] ram_addr ,          //RAM 地址
output wire [3:0] ram_we ,             //RAM 读写控制信号
output wire [31:0] ram_wr_data,        //RAM 写数据
output wire ram_rst ,                  //RAM 复位信号,高电平有

在实例化 pl_ram_plus1_v1_0_S00_AXI 模块的位置,添加以下代码。这里需要注意一下例化时的逗号问题,如果添加在最后,需要在之前的最后一个信号后添加逗号。

.ram_clk (ram_clk ),
.ram_rd_data (ram_rd_data),
.ram_en (ram_en ),
.ram_addr (ram_addr ),
.ram_we (ram_we ),
.ram_wr_data (ram_wr_data),
.ram_rst (ram_rst )

打开 pl_bram_plus1_v1_0_S00_AXI.v 文件,同样在 Users to add ports here 和 User port ends 中间行添加如下代码:

output wire ram_clk ,                //RAM 时钟
input wire [31:0] ram_rd_data,       //RAM 中读出的数据
output wire ram_en ,                 //RAM 使能信号
output wire [31:0] ram_addr ,        //RAM 地址
output wire [3:0] ram_we ,           //RAM 读写控制信号
output wire [31:0] ram_wr_data,      //RAM 写数据
output wire ram_rst ,                //RAM 复位信号,高电平有

在程序最后 Add user logic here 和 User logic ends 的中间行,添加如下代码:

bram_ip_plus1 inst_bram_ip_plus1(.clk (S_AXI_ACLK),.rst_n (S_AXI_ARESETN),.start_rd (slv_reg0[0]),.start_addr (slv_reg1),.rd_len (slv_reg2),//RAM 端口.ram_clk (ram_clk ),.ram_rd_data (ram_rd_data),.ram_en (ram_en ),.ram_addr (ram_addr ),.ram_we (ram_we ),.ram_wr_data (ram_wr_data),.ram_rst (ram_rst ));

接下来在工程中创建一个新的模块,命名为“ bram_rd”,位于../BRAMIP/ip_repo/pl_bram_plus1_1.0/hdl路径下, bram_ip_plus1 模块的代码如下:

module bram_ip_plus1(input clk , //时钟信号input rst_n , //复位信号input start_rd , //读开始信号input [31:0] start_addr , //读起始地址input [31:0] rd_len , //读数据的长度//RAM 端口output ram_clk , //RAM 时钟input [31:0] ram_rd_data, //RAM 中读出的数据output reg ram_en , //RAM 使能信号output reg [31:0] ram_addr , //RAM 地址output reg [3:0] ram_we , //RAM 读写控制信号output reg [31:0] ram_wr_data, //RAM 写数据output ram_rst //RAM 复位信号,高电平有效);//reg definereg [1:0] flow_cnt;reg start_rd_d0;reg start_rd_d1;//wire definewire pos_start_rd;//*****************************************************//** main code//*****************************************************assign ram_rst = 1'b0;assign ram_clk = clk ;assign pos_start_rd = ~start_rd_d1 & start_rd_d0;//延时两拍,采 start_rd 信号的上升沿always @(posedge clk or negedge rst_n) beginif(!rst_n) beginstart_rd_d0 <= 1'b0;start_rd_d1 <= 1'b0;endelse beginstart_rd_d0 <= start_rd;start_rd_d1 <= start_rd_d0;endend//根据读开始信号,从 RAM 中读出数据always @(posedge clk or negedge rst_n) beginif(!rst_n) beginflow_cnt <= 2'd0;ram_en <= 1'b0;ram_addr <= 32'd0;ram_we <= 4'd0;endelse begincase(flow_cnt)2'd0 : beginif(pos_start_rd) beginram_en <= 1'b1;ram_addr <= start_addr;flow_cnt <= flow_cnt + 2'd1;endend2'd1 : beginram_wr_data <= ram_rd_data + 1;flow_cnt <= flow_cnt + 2'd1;ram_we <= 4'b1111;end 2'd2:beginif(ram_addr - start_addr == rd_len - 4) begin //数据读完flow_cnt <= flow_cnt + 2'd1;endelse beginram_addr <= ram_addr + 32'd4; //地址累加 4ram_we <= 4'b0000;flow_cnt <= flow_cnt - 2'd1;end      end2'd3 : beginram_addr <= 32'd0;flow_cnt <= 2'd0;ram_en <= 1'b0;endendcaseendendendmodule

代码都添加完毕后,进行IP封装。

双击 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”图标,如下图所示:

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

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

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

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

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

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

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

接下来切换至“Review and Package”页面,点击上侧的“ IP has been modified”来更新 IP,最后点击“ Re-Package IP”完成 IP 核的封装,此时 IP 核的封装界面会自动关闭。

如果不嫌麻烦,可以在进行最后的封装前,进行一下综合,检查电路是否有问题,是否有信号被优化没了。不综合大多数情况下没问题,偶尔会出现问题。我建议可以不要嫌麻烦,综合后再进行封装。

退出后点击右侧的IP Catalog,在user repository 下面的AXI处右键添加IP。添加之后就可以在BD中调用了。

新建Block Design,依次添加ARM处理器(需要添加uart),BRAM generator和BRAM control,然后点击自动连线,接下来添加自定义IP,再次点击自动连线,最终的框图如下:

接下来走流程,生成output和顶层,然后综合实现布局布线生成比特流,成功后export hardware,launch SDK。然后去SDK中编程。

三、SDK编程

SDK里先建立工程,我们建立一个空的或者HELLO WORLD!工程都可以,我选择建立空工程,然后在src里新建main.c,main.c的内容如下:

#include "xil_printf.h"
#include "stdio.h"
#include "pl_bram_plus1.h"
#include "xbram.h"
#include "xparameters.h"#define PL_BRAM_BASE XPAR_PL_BRAM_PLUS1_0_S00_AXI_BASEADDR //PL_RAM_RD 基地址
#define PL_BRAM_START PL_BRAM_PLUS1_S00_AXI_SLV_REG0_OFFSET //RAM 读开始寄存器地址
#define PL_BRAM_START_ADDR PL_BRAM_PLUS1_S00_AXI_SLV_REG1_OFFSET //RAM 起始寄存器地址
#define PL_BRAM_LEN PL_BRAM_PLUS1_S00_AXI_SLV_REG2_OFFSET //PL 读 RAM 的深度#define START_ADDR 0 //RAM 起始地址 范围:0~1023
#define BRAM_DATA_BYTE 4 //BRAM 数据字节个数int data[1024]; //写入 BRAM 的字符数组
int data_len; //写入 BRAM 的字符个数//main 函数int main(){int i=0,wr_cnt = 0;int read_data=0;for(int k=0;k<1024;k++){data[k] = k;}data_len = 1024;//将用户输入的字符串写入 BRAM 中//每次循环向 BRAM 中写入 1 个字符for(i = BRAM_DATA_BYTE*START_ADDR ; i < BRAM_DATA_BYTE*(START_ADDR + data_len) ;i += BRAM_DATA_BYTE){XBram_WriteReg(XPAR_BRAM_0_BASEADDR,i,data[wr_cnt]) ;wr_cnt++;}//设置 BRAM 写入的字符串长度PL_BRAM_PLUS1_mWriteReg(PL_BRAM_BASE, PL_BRAM_LEN , BRAM_DATA_BYTE*data_len) ;//设置 BRAM 的起始地址PL_BRAM_PLUS1_mWriteReg(PL_BRAM_BASE, PL_BRAM_START_ADDR, BRAM_DATA_BYTE*START_ADDR) ;//拉高 BRAM 开始信号PL_BRAM_PLUS1_mWriteReg(PL_BRAM_BASE, PL_BRAM_START , 1) ;//拉低 BRAM 开始信号PL_BRAM_PLUS1_mWriteReg(PL_BRAM_BASE, PL_BRAM_START , 0) ;//从 BRAM 中读出数据i = 0;//循环从 BRAM 中读出数据for(i = BRAM_DATA_BYTE*START_ADDR ; i < BRAM_DATA_BYTE*(START_ADDR + data_len) ;i += BRAM_DATA_BYTE){read_data = XBram_ReadReg(XPAR_BRAM_0_BASEADDR,i) ;printf("BRAM address is %d\t,Read data is %d\n",i/BRAM_DATA_BYTE ,read_data) ;}}

四、实验测试

设置run configurations,连接uart后,下载到开发板:

从SDK程序可以看到,我们写入的是data数组的内容,就是0-1023,我们读出来的数据如下图所示:

可以看到,实验结果完全正确。

五、总结

本实验通过PS部分将数据写入FPGA的BRAM里,通过自定义IP实现PL部分对BRAM里的数据进行读取后,加一再重新写入BRAM里,PS再重新读取,进行对比。从截图可看到,试验成功。

通过这样的流程,以后我们可以直接在定义的IP里实现更加复杂的功能。

如果绝的自定义IP操作繁琐,可以留言跟我要ip_repo。

ZYNQ学习之路(三):自定义IP实现PL处理PS写入BRAM的数据相关推荐

  1. ZYNQ学习之路17.自定义SDSoC硬件平台

    前言 在前面的学习中,我们已经学会了使用Vivado及SDK开发环境,熟悉了硬件开发与Linux软件驱动之间的联系及开发流程.本系列教程我们学习SDSoc的开发,在SDSoc IDE中,Xilinx为 ...

  2. ZYNQ学习之路13.创建PetaLinux工程

    在前面的学习中,我们知道如何根据PetaLinux BSP设计去创建一个工程,现在,我们结合Vivado设计我们自己PetaLinux系统. 开发环境:Ubuntu16 64bit, PetaLinu ...

  3. Redis——学习之路三(初识redis config配置)

    我们先看看config 默认情况下系统是怎么配置的.在命令行中输入 config get *(如图) 默认情况下有61配置信息,每一个命令占两行,第一行为配置名称信息,第二行为配置的具体信息. 我们就 ...

  4. typescript学习之路(三) —— ts定义类的方法(包含es5以及es6的定义类)

    提起类,不得不说一下,强类型编程语言,如php,java,c++等都有类的概念.而js作为一门弱类型语言,是没有类这个概念的,虽然也能模拟类的实现,但总归不是类.so,ts也只是模拟类而已,使得更贴切 ...

  5. ZYNQ学习之路4.ZYNQ通过GP口读取PL内部RAM数据

    实验环境:window 7 64 bit, vivado 2017.1, ZTURN board. 参考手册:Xilinx Distributed Memory Generator 在ZYNQ开发中, ...

  6. 【网络基础学习笔记】三、IP 网络层;

    文章目录 三.IP 层(网络层 - 路由器 - 广域网) 3.1 IP 报文首部结构 3.2 IP地址/网段/子网掩码/广播域 3.2.1 IP 地址分类 3.2.1 子网掩码 3.2.2 广播域 3 ...

  7. 【ZYNQ Ultrascale+ MPSOC FPGA教程】第三十二章 PL读写PS端DDR数据

    本原创教程由芯驿电子科技(上海)有限公司(ALINX)创作,版权归本公司所有,如需转载,需授权并注明出处. 适用于板卡型号: AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4E ...

  8. 【Zynq UltraScale+ MPSoC】基于LWIP模板的udp通信与测试(二):PL与PS基于BRAM的UDP通信

    文章目录 前言 一.PL端准备 二.PS端程序 1.BD文件和模块连接 2.PS端的程序: 三.测试与总结 前言 承接上一篇文章 一.PL端准备 这部分主要是加入BRAM来向PS端传输数据,主要是需要 ...

  9. ZYNQ学习之路3. 定制AXI IP核

    ZYNQ最大的优点就是硬核A9处理器与FPGA的结合,处理器可以扩展出任何使用者想要的外设(数字逻辑外设),FPGA与处理器通过AXI高速总线进行连接,提供了处理器到FPGA的高速带宽(ZYNQ700 ...

最新文章

  1. AI一分钟 | 网信办暂停快手、火山小视频算法推荐功能;无需人类司机,加州将允许自动驾驶汽车接送乘客
  2. 面对疫情,飞书线上办公室,让我们换一种方式重聚!
  3. break后面的语句还执行吗_流程控制语句
  4. 如何“主动出击”提升网站的收录速度?
  5. ubuntu python3.5安装_ubuntu16.04升级Python3.5到Python3.7的方法步骤
  6. 在PLSQL中,存储过程的输出参数(varchar2)最大支持多大
  7. linux分区 挂盘,linux分区与格式化磁盘挂载磁盘与自动挂载详细教程
  8. htt://3g.hn_根据我对“询问HN:谁在招聘?”的分析,开发人员技能发展趋势
  9. mysql 单标递归_MySql8 WITH RECURSIVE递归查询父子集的方法
  10. b - 数据结构实验之查找二:平衡二叉树_文件系统的灵魂数据结构 B树
  11. mysql 字符,索引
  12. python基础元组(五)
  13. 对中级Linux用户有用的20个命令
  14. MFC中子类调用父类成员
  15. 【转】和菜鸟一起学linux之DBUS基础学习记录
  16. 自学通过CISSP备考心得
  17. 前端web设计师_Web设计师的时尚Web设计主题
  18. Android Material-dialogs的使用(kotlin)
  19. 高级语言程序设计(c语言描述) 陆黎明 朱媛媛 练习答案,高级语言程序设计(c语言描述) 陆黎明 朱媛媛 练习答案...
  20. git rebase命令实际操作记录

热门文章

  1. php引用中国联通M2M接口
  2. UltraEdit激活版
  3. SpringMvc中关于@ResponseBody和HashMap的用法
  4. CSS3 3D旋转立方体
  5. 关键词SEO排名优化的对策与流程
  6. 春夏时节,淘宝店铺如何快速打造爆款?
  7. 360百科词条怎么创建(教你100%通过审核方法)
  8. 房地产—客户关系管理系统CRM|软件开发|软件外包|程序二次开发
  9. ladp3 获取属性_Ldap3 库使用方法(三)
  10. DUTOJ-1135: 砍竹子