如何在 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 端程序的开发。

程序的整体流程如下:

  1. 在 PS 端输入起始地址和长度
  2. CPU通过 axi BRAM controller 写入BRAM数据
  3. 通知 PL BRAM controller 读取数据
  4. PL 内部读完后向相同的位置写入数据,初始数据有CPU告知
  5. 写完后使能 write_end 信号,出发GPIO 中断
  6. 中断读取 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 的数据交互相关推荐

  1. PL和PS端数据交互方式

    常用的PL和PS数据交互方式根据交互数据量的大小有以下几种方式: 1.bit或Byte级别的交互,常用于设备状态交互 使用ram,AXI,RAM操作复杂,AXI操作简单,速度更快 2.100MB以下的 ...

  2. ZYNQ SDK开发调试踩坑指南

    关注+星标公众号,及时获取更多技术分享~  作者 | 冰茶奥利奥 微信公众号 | 嵌入式电子创客街 目录 坑1:裸机Ps串口收数问题 坑2:多个中断不能同时使用问题 坑3:block design中D ...

  3. web开发中前端页面是如何跟后端服务器数据交互的

    后端服务器一般是指servlet容器,用于执行java源程序 常见的网页有html,htm,shtml,asp,aspx,php,jsp等格式 前两个常用于静态网页,后面几个常用于动态网页. 这里前端 ...

  4. 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地址映射 ...

  5. ZYNQ——BRAM全双工PS_PL_数据交互

    前言:由于本历程在网上确实没有发现,而且确实在PL端的读写数据废了很长的时间,个人认为具有一定的价值,因此本历程暂不免费开源.目前所有的代码都只放出部分参考不能复制直接用,希望购买资源的朋友们不要倒卖 ...

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

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

  7. FPGA----ZCU106基于axi-hp通道的pl与ps数据交互(全网唯一最详)

    1.大家好,今天给大家带来的内容是,基于AXI4协议的采用AXI-HP通道完成PL侧数据发送至PS侧(PS侧数据发送至PL侧并没有实现,但是保留了PL读取PS测数据的接口) 2.如果大家用到SoC这种 ...

  8. zynq开发系列5:通过AXI GPIO的中断实现PL端按键控制PS端LED(SDK开发详解)

    axi_gpio是PL端gpio(FPGA资源搭建的软核),ps7_gpio是ps端gpio(硬核).打开Documentation的示例Examples,可知第二个是关于中断的示例.导入示例impo ...

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

    目录 一.实验简介 二.vivado部分处理 三.SDK编程 四.实验测试 五.总结 一.实验简介 ZYNQ系列嵌入式FPGA可以使PS将数据写入PL部分BRAM,PL可以将数据读取后再重新写入BRA ...

最新文章

  1. 程序员锁死服务器致公司倒闭当事人逐条反驳:这锅我不背
  2. 【Mybatis 之应用篇】1_Mybatis简介、第一个Mybatis程序和增删改查在Mybatis中的使用方式
  3. [转]为什么我们不用软件工程?软件工程能帮多大忙?
  4. poj1284 Primitive Roots
  5. MIT新研究:基于基本运动能力让机器人自主学习,感知世界
  6. Echarts鼠标悬浮样式
  7. mybatis generator 中文注释_[SpringBoot2.X] 23- 整合持久层技术 -MyBatis - 配置
  8. svn 服务器修改密码,用户自行修改svn密码的简单服务
  9. 计算机网络路由计算,计算机网络中的多播路由算法
  10. 三菱plc控制步进电机实例_电工进阶PLC工程师!必学步进电机的编程控制指令,你掌握了吗...
  11. python调用百度云文字识别
  12. 激光雷达在无人驾驶应用中如何应对雨雾灰尘环境及经典案例分析
  13. 电脑小知识:电脑怎么查看ip地址?
  14. 电信光猫F420破解
  15. between oracle的用法,关于 oracle between and的用法! | 学步园
  16. C语言求水仙花数(自幂数)
  17. [Redis] Redis实战
  18. spring boot项目报错:Validation failed for query for method public abstract...
  19. [HDF5]如何使用CMake一起编译自己的代码和HDF5库
  20. 【转载】CDS view自学系列

热门文章

  1. Java用栈简单实现迷宫
  2. JavaScript常见输出语句
  3. 【毕业设计/课程设计】基于SSM的电影票预订系统设计与实现(源码+文章) Java | JSP | MVC | Web
  4. 一个数学残的CRPS笔记(连续概率排位分数 Continuous Rank Probabilistic Score)
  5. MATLAB批量为png透明(抠图)图片替换添加背景
  6. 烽火通信张宾:智慧光网,开创工业互联网新时代
  7. Microsoft HoloLens开发入门
  8. SpringBoot系列之Hikari连接池
  9. html input提交按钮标签样式,html - 输入类型=“提交”Vs按钮标签是否可以互换?...
  10. 微信公众平台测试号管理接口配置信息配置失败