zynq sdk 开发之通过 BRAM 进行 PL 与 PS 的数据交互
如何在 zynq 中进行 PL 端与 PS 端的数据交互?
在zynq的使用中,高效的进行 BRAM 与 zynq 硬核的数据交换至关重要,当我们需要进行小批量的数据交换时,可以考虑采用 BRAM 作为数据交换的媒介,在 PS 端,将数据通过 AXI 总线写入 PL端的 BRAM 里面去,并且再读出来。整体的设计图如下所示:
PL 端硬件的设计
在 vivado 2017.4 下,可以通过 block design 快速进行模块的添加与设计。设计出来的架构如图所示:
注意事项:
- BRAM 使用真实双端口 RAM ,一个端口连接 axi BRAM controller(用于 PS 端控制 BRAM),一个连接 PL BRAM controller(一个自定义的 PL 端 BRAM 控制器,可以由 PS 端给一些控制信号)。
- BRAM需要选择 BRAM controller 模式。
- zynq 端要打开中断
在经过上面的 block design 设计之后,可将硬件导出,然后打开 sdk ide 开始进行 PS 端程序的开发。
程序的整体流程如下:
- 在 PS 端输入起始地址和长度
- CPU通过 axi BRAM controller 写入BRAM数据
- 通知 PL BRAM controller 读取数据
- PL 内部读完后向相同的位置写入数据,初始数据有CPU告知
- 写完后使能 write_end 信号,出发GPIO 中断
- 中断读取 BRAM 数据,打印显示
关于自定义 IP 的设计:
自定义 IP 实现了对 PL 端 BRAM 的读写操作,并且在外部封装的有 AXI 总线的端口,核心的读写逻辑如下:
module ram_read_write(input clk,input rst_n,//bram portinput [31:0] din, output reg [31:0] dout,output reg en,output reg [3:0] we,output rst,output reg [31:0] addr,//control signalinput start, //start to read and write braminput [31:0] init_data, //initial data defined by softwareoutput reg start_clr, //clear start registerinput [31:0] len, //data countinput [31:0] start_addr, //start bram address//Interruptinput intr_clr, //clear interruptoutput reg intr //interrupt);assign rst = 1'b0 ;localparam IDLE = 3'd0 ;
localparam READ_RAM = 3'd1 ;
localparam READ_END = 3'd2 ;
localparam WRITE_RAM = 3'd3 ;
localparam WRITE_END = 3'd4 ;reg [2:0] state ;
reg [31:0] len_tmp ;
reg [31:0] start_addr_tmp ;//Main statement
always @(posedge clk or negedge rst_n)
beginif (~rst_n)beginstate <= IDLE ;dout <= 32'd0 ;en <= 1'b0 ;we <= 4'd0 ;addr <= 32'd0 ;intr <= 1'b0 ;start_clr <= 1'b0 ;len_tmp <= 32'd0 ;start_addr_tmp <= 32'd0 ;endelsebegincase(state)IDLE : beginif (start)beginstate <= READ_RAM ;addr <= start_addr ;start_addr_tmp <= start_addr ;len_tmp <= len ;dout <= init_data ;en <= 1'b1 ;start_clr <= 1'b1 ;end if (intr_clr)intr <= 1'b0 ;endREAD_RAM : beginif ((addr - start_addr_tmp) == len_tmp - 4) //read completedbeginstate <= READ_END ;en <= 1'b0 ;endelsebeginaddr <= addr + 32'd4 ; //address is byte based, for 32bit data width, adding 4 endstart_clr <= 1'b0 ;endREAD_END : beginaddr <= start_addr_tmp ;en <= 1'b1 ;we <= 4'hf ;state <= WRITE_RAM ; endWRITE_RAM : beginif ((addr - start_addr_tmp) == len_tmp - 4) //write completedbeginstate <= WRITE_END ;dout <= 32'd0 ;en <= 1'b0 ;we <= 4'd0 ;endelsebeginaddr <= addr + 32'd4 ;dout <= dout + 32'd1 ; endendWRITE_END : beginaddr <= 32'd0 ;intr <= 1'b1 ;state <= IDLE ; end default : state <= IDLE ;endcaseend
end endmodule
这个自定义 IP 的实现是为了令实例更为完整,方便在逻辑分析仪中进行观察。
PS 端软件的设计
打开 sdk 工具,开始进行软件开发。
代码如下:
/* ------------------------------------------------------------ */
/* Include File Definitions */
/* ------------------------------------------------------------ */#include "xil_printf.h"
#include "xbram.h"
#include <stdio.h>
#include "pl_bram_ctrl.h"
#include "xscugic.h"#define BRAM_CTRL_BASE XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define BRAM_CTRL_HIGH XPAR_AXI_BRAM_CTRL_0_S_AXI_HIGHADDR
#define PL_RAM_BASE XPAR_PL_BRAM_CTRL_0_S00_AXI_BASEADDR
#define PL_RAM_CTRL PL_BRAM_CTRL_S00_AXI_SLV_REG0_OFFSET
#define PL_RAM_INIT_DATA PL_BRAM_CTRL_S00_AXI_SLV_REG1_OFFSET
#define PL_RAM_LEN PL_BRAM_CTRL_S00_AXI_SLV_REG2_OFFSET
#define PL_RAM_ST_ADDR PL_BRAM_CTRL_S00_AXI_SLV_REG3_OFFSET#define START_MASK 0x00000001
#define INTRCLR_MASK 0x00000002#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define INTR_ID XPAR_FABRIC_PL_BRAM_CTRL_0_INTR_INTR#define TEST_START_VAL 0xC
/** BRAM bytes number*/
#define BRAM_BYTENUM 4XScuGic INTCInst;int Len ;
int Start_Addr ;
int Intr_flag ;
/** Function declaration*/
int bram_read_write() ;
int IntrInitFuntion(u16 DeviceId);
void IntrHandler(void *InstancePtr);int main()
{int Status;Intr_flag = 1 ;IntrInitFuntion(INTC_DEVICE_ID) ;while(1){if (Intr_flag){Intr_flag = 0 ;printf("Please provide start address\t\n") ;scanf("%d", &Start_Addr) ;printf("Start address is %d\t\n", Start_Addr) ;printf("Please provide length\t\n") ;scanf("%d", &Len) ;printf("Length is %d\t\n", Len) ;Status = bram_read_write() ;if (Status != XST_SUCCESS){xil_printf("Bram Test Failed!\r\n") ;xil_printf("******************************************\r\n");Intr_flag = 1 ;}}}
}// 对BRAM的读写操作
int bram_read_write()
{u32 Write_Data = TEST_START_VAL ; // 要写入的数据int i ;/** if exceed BRAM address range, assert error*/if ((Start_Addr + Len) > (BRAM_CTRL_HIGH - BRAM_CTRL_BASE + 1)/4){xil_printf("******************************************\r\n");xil_printf("Error! Exceed Bram Control Address Range!\r\n");return XST_FAILURE ;}/** Write data to BRAM*/for(i = BRAM_BYTENUM*Start_Addr ; i < BRAM_BYTENUM*(Start_Addr + Len) ; i += BRAM_BYTENUM){XBram_WriteReg(XPAR_BRAM_0_BASEADDR, i , Write_Data) ;Write_Data += 1 ;}//Set ram read and write lengthPL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_LEN , BRAM_BYTENUM*Len) ;//Set ram start addressPL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_ST_ADDR , BRAM_BYTENUM*Start_Addr) ;//Set pl initial dataPL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_INIT_DATA , (Start_Addr+1)) ;//Set ram start signalPL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_CTRL , START_MASK) ;return XST_SUCCESS ;
}int IntrInitFuntion(u16 DeviceId)
{XScuGic_Config *IntcConfig;int Status ;//check device idIntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);//intializationStatus = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress) ;if (Status != XST_SUCCESS)return XST_FAILURE ;XScuGic_SetPriorityTriggerType(&INTCInst, INTR_ID,0xA0, 0x3);Status = XScuGic_Connect(&INTCInst, INTR_ID,(Xil_ExceptionHandler)IntrHandler,(void *)NULL) ;if (Status != XST_SUCCESS)return XST_FAILURE ;XScuGic_Enable(&INTCInst, INTR_ID) ;Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&INTCInst);Xil_ExceptionEnable();return XST_SUCCESS ;}void IntrHandler(void *CallbackRef)
{int Read_Data ;int i ;printf("Enter interrupt\t\n");//clear interrupt statusPL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_CTRL , INTRCLR_MASK) ;for(i = BRAM_BYTENUM*Start_Addr ; i < BRAM_BYTENUM*(Start_Addr + Len) ; i += BRAM_BYTENUM){Read_Data = XBram_ReadReg(XPAR_BRAM_0_BASEADDR , i) ;printf("Address is %d\t Read data is %d\t\n", i/BRAM_BYTENUM ,Read_Data) ;}Intr_flag = 1 ;
}
最终实现效果如下图所示:
需要完整项目代码的可以留言。
zynq sdk 开发之通过 BRAM 进行 PL 与 PS 的数据交互相关推荐
- PL和PS端数据交互方式
常用的PL和PS数据交互方式根据交互数据量的大小有以下几种方式: 1.bit或Byte级别的交互,常用于设备状态交互 使用ram,AXI,RAM操作复杂,AXI操作简单,速度更快 2.100MB以下的 ...
- ZYNQ SDK开发调试踩坑指南
关注+星标公众号,及时获取更多技术分享~ 作者 | 冰茶奥利奥 微信公众号 | 嵌入式电子创客街 目录 坑1:裸机Ps串口收数问题 坑2:多个中断不能同时使用问题 坑3:block design中D ...
- web开发中前端页面是如何跟后端服务器数据交互的
后端服务器一般是指servlet容器,用于执行java源程序 常见的网页有html,htm,shtml,asp,aspx,php,jsp等格式 前两个常用于静态网页,后面几个常用于动态网页. 这里前端 ...
- Zynq 7000 PL和PS通信——使用BRAM
Zynq 7000 PL和PS通信--使用BRAM 介绍 1 准备工作 2 PL搭建BRAM 3 下载PL程序 4 编写Linux应用程序 5 问题 5.1 BRAM的寻址的问题 5.2 PS地址映射 ...
- ZYNQ——BRAM全双工PS_PL_数据交互
前言:由于本历程在网上确实没有发现,而且确实在PL端的读写数据废了很长的时间,个人认为具有一定的价值,因此本历程暂不免费开源.目前所有的代码都只放出部分参考不能复制直接用,希望购买资源的朋友们不要倒卖 ...
- 【ZYNQ Ultrascale+ MPSOC FPGA教程】第三十二章 PL读写PS端DDR数据
本原创教程由芯驿电子科技(上海)有限公司(ALINX)创作,版权归本公司所有,如需转载,需授权并注明出处. 适用于板卡型号: AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4E ...
- FPGA----ZCU106基于axi-hp通道的pl与ps数据交互(全网唯一最详)
1.大家好,今天给大家带来的内容是,基于AXI4协议的采用AXI-HP通道完成PL侧数据发送至PS侧(PS侧数据发送至PL侧并没有实现,但是保留了PL读取PS测数据的接口) 2.如果大家用到SoC这种 ...
- zynq开发系列5:通过AXI GPIO的中断实现PL端按键控制PS端LED(SDK开发详解)
axi_gpio是PL端gpio(FPGA资源搭建的软核),ps7_gpio是ps端gpio(硬核).打开Documentation的示例Examples,可知第二个是关于中断的示例.导入示例impo ...
- ZYNQ学习之路(三):自定义IP实现PL处理PS写入BRAM的数据
目录 一.实验简介 二.vivado部分处理 三.SDK编程 四.实验测试 五.总结 一.实验简介 ZYNQ系列嵌入式FPGA可以使PS将数据写入PL部分BRAM,PL可以将数据读取后再重新写入BRA ...
最新文章
- 程序员锁死服务器致公司倒闭当事人逐条反驳:这锅我不背
- 【Mybatis 之应用篇】1_Mybatis简介、第一个Mybatis程序和增删改查在Mybatis中的使用方式
- [转]为什么我们不用软件工程?软件工程能帮多大忙?
- poj1284 Primitive Roots
- MIT新研究:基于基本运动能力让机器人自主学习,感知世界
- Echarts鼠标悬浮样式
- mybatis generator 中文注释_[SpringBoot2.X] 23- 整合持久层技术 -MyBatis - 配置
- svn 服务器修改密码,用户自行修改svn密码的简单服务
- 计算机网络路由计算,计算机网络中的多播路由算法
- 三菱plc控制步进电机实例_电工进阶PLC工程师!必学步进电机的编程控制指令,你掌握了吗...
- python调用百度云文字识别
- 激光雷达在无人驾驶应用中如何应对雨雾灰尘环境及经典案例分析
- 电脑小知识:电脑怎么查看ip地址?
- 电信光猫F420破解
- between oracle的用法,关于 oracle between and的用法! | 学步园
- C语言求水仙花数(自幂数)
- [Redis] Redis实战
- spring boot项目报错:Validation failed for query for method public abstract...
- [HDF5]如何使用CMake一起编译自己的代码和HDF5库
- 【转载】CDS view自学系列
热门文章
- Java用栈简单实现迷宫
- JavaScript常见输出语句
- 【毕业设计/课程设计】基于SSM的电影票预订系统设计与实现(源码+文章) Java | JSP | MVC | Web
- 一个数学残的CRPS笔记(连续概率排位分数 Continuous Rank Probabilistic Score)
- MATLAB批量为png透明(抠图)图片替换添加背景
- 烽火通信张宾:智慧光网,开创工业互联网新时代
- Microsoft HoloLens开发入门
- SpringBoot系列之Hikari连接池
- html input提交按钮标签样式,html - 输入类型=“提交”Vs按钮标签是否可以互换?...
- 微信公众平台测试号管理接口配置信息配置失败