ZYNQ裸板:BRAM篇

简介: 在 ZYNQ SOC 开发过程中, PL 和 PS 之间经常需要做数据交互。对于传输速度要求较高、数据量大、地址连续的场合,可以通过 AXI DMA 来完成。而对于数据量较少、地址不连续、长度不规则的情况,此时 AXIDMA 便不再适用了。针对这种情况,可以通过 BRAM 来进行数据的交互。

前言

 在 ZYNQ SOC 开发过程中, PL 和 PS 之间经常需要做数据交互。对于传输速度要求较高、数据量大、地址连续的场合,可以通过 AXI DMA 来完成。而对于数据量较少、地址不连续、长度不规则的情况,此时 AXIDMA 便不再适用了。针对这种情况,可以通过 BRAM 来进行数据的交互。(还不是因为DMA麻烦一点才先写这个)

 Block RAM是PL部分的存储器阵列,为了与DRAM(分布式RAM)区分开,所以叫BLOCK RAM。ZYNQ的每一个BRAM 36KB,7020的BRAM有140个(4.9M),7030有265个(9.3M),7045有545个(19.2M)。每一个BRAM都有两个共享数据的独立端口,当然是可以配置的,可用于片内数据缓存、FIFO缓冲。

 在Vivado里有一个IP核叫Block Memory Generator,它使用FPGA的BRAM资源为我们提供可编程的RAM。

读和写由时钟控制,

数据宽度是可编程的。

与DRAM相比补充:

1.BRAM是使用FPGA中的整块双口RAM资源

2.DRAM是FPGA中的查找表(LUT)拼凑出来的,要占用逻辑资源。

3.物理上看,BRAM 是单纯的存储资源,但是要一块一块的用,不像DRAM 想要多少bit都可以。

4.DRAM可以是纯组合逻辑,即给出地址马上出数据,BRAM是有时钟的。

再补充个使用条件吧

1、较大的存储应用,建议用BRAM;零星的小RAM,一般就用DRAM。(小于或等于64bit容量的用分布式实现,深度在64~128之间的,若无额外的block可用分布式RAM。数据宽度大于16时用Block RAM)

2、dram可以是纯组合逻辑,即给出地址马上出数据,也可以加上register变成有时钟的ram。而bram一定是有时钟的。(要求异步读取就使用分布式RAM)

3、如果要产生大的FIFO或timing要求较高,就用Block RAM。否则,就可以用Distributed RAM。块RAM是比较大块的RAM,即使用了它的一小部分,那么整个Block RAM就不能再用了。所以,当你要用的RAM比较小,时序要求不高时,可以用分布式RAM,节省资源。

FPGA中的资源位置是固定的,例如BRAM就是一列一列分布的(存储器阵列),会产生较大的布线时延。在大规模FPGA中,如果用光所有的BRAM,性能一般会下降,甚至出现route不通的情况,就是这个原因。

4.用户申请存储资源,FPGA先提供Block RAM ,当Block RAM 数量不够时再用分布式RAM补充。

再说回到BRAM,在 PL 中,通过输出时钟、地址、读写控制等信号来对 BRAM 进行读写操作;而在 PS 中,处理器并不需要直接驱动 BRAM 的端口,而是通过 AXIBRAM 控制器来对 BRAM 进行读写操作。 AXI BRAM 控制器是集成在 Vivado 设计软件中的软核,可以配置成 AXI4-lite 接口模式或者 AXI4 接口模式。

 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 核支持单次传输和突发传输两种方式

对BRAM的操作是很好理解的,一切通过地址:

 代码部分借鉴下原子的吧还是

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


相关文章
|
9月前
|
开发工具 C++ 异构计算
Xilinx ZYNQ的应用开发介绍
Xilinx的ZYNQ系列是首款可扩展处理平台,以其高性能和计算能力适用于高端嵌入式应用如视频监控和汽车辅助系统。ZYNQ结合了双ARM Cortex-A9 MPCore处理器系统与28nm FPGA技术,提供高度灵活性和配置选项。开发环境包括Xilinx Platform Studio、ARM工具链及第三方软件,支持C/C++和系统C综合。ZYNQ由处理系统(PS)和可编程逻辑(PL)组成,PS是ARM SOC,PL是FPGA。开发工具推荐使用Vivado,Zynq 7000 XC7Z100作为旗舰型号提供丰富的资源。
157 0
|
9月前
|
Ubuntu Linux 开发工具
【ZYNQ】那些年我们拿下了 Zynq
【ZYNQ】那些年我们拿下了 Zynq
124 0
|
Ubuntu Linux
嵌入式Linux开发: 龙芯2K1000使用EJTAG烧写POPM
嵌入式Linux开发: 龙芯2K1000使用EJTAG烧写POPM
629 0
嵌入式Linux开发: 龙芯2K1000使用EJTAG烧写POPM
|
存储 Linux API
ZYNQ裸板:串口篇
使用 PS 的时候,通常会添加 UART 控制器,用于打印信息和调试代码。除此之外, PS 在和外部设备通信时,也会经常使用串口进行通信。先从UART控制器开始讲起吧,从简单的测试再到工程实例。
967 0
ZYNQ裸板:串口篇
|
存储 安全 存储控制器
ZYNQ裸板:中断篇
中断对于单片机过来的我们来说,相对也算比较熟悉了,还是严谨一点从头开始说吧。中断是什么?是一种当满足要求的突发事件发生时通知处理器进行处理的信号。中断可以由硬件处理单元和外部设备产生,也可以由软件本身产生。对硬件来说,中断信号是一个由某个处理单元产生的异步信号,用来引起处理器的注意。对软件来说,中断还是一种异步事件,用来通知处理器需要改变代码的执行,当然,轮询所产生的中断的过程是同步的。
1015 0
ZYNQ裸板:中断篇
|
存储 Linux 数据安全/隐私保护
ZYNQ - 嵌入式Linux开发 -10- ZYNQ启动流程分析
ZYNQ - 嵌入式Linux开发 -10- ZYNQ启动流程分析
1182 0
ZYNQ - 嵌入式Linux开发 -10- ZYNQ启动流程分析
|
存储 缓存 网络协议
ZYNQ裸板:AXIDMA篇(简单模式)
DMA(Direct Memory Access,直接存储器访问)是计算机科学中的一种内存访问技术。它允许某些计算机内部的硬件子系统可以独立地直接读写系统内存,而不需中央处理器( CPU)介入处理。 DMA 是一种快速的数据传送方式, 通常用来传送数据量较多的数据块
1396 0
ZYNQ裸板:AXIDMA篇(简单模式)
|
存储 安全 开发工具
ZYNQ裸板:程序固化篇
一般的调试中我们都是通过 JTAG 接口将 FPGA 配置文件和应用程序下载到 ZYNQ 器件中。同样的,我们也可以将尝试把程序存储在非易失性存储器中,在上电或者复位时让程序自动运行,这个过程需要启动引导程序( Boot Loader) 的参与
443 0
ZYNQ裸板:程序固化篇
|
芯片 内存技术
ZYNQ裸板:DDR篇
DDR是zynq的内存又可以叫做主存。是CPU能直接寻址的存储空间,没有DDR的话,运行内存只有很小的内部RAM,软件大小受限,也几乎没法跑操作系统。作为下一篇AXIDMA的基础知识提前做个准备,内容很简单这里就简单做一个读写测试,也可以当做验证下DDR是否都可以正常访问
636 0
ZYNQ裸板:DDR篇
|
数据处理 数据安全/隐私保护 SoC
DSP+ZYNQ硬件说明手册-XQTyer【开源】
XQ6657Z35/45-EVM 高速数据处理评估板(XQTyer 评估板)由广州星嵌电子科技有限公司自主研发,包含一片TI DSP TMS320C6657和一片Xilinx ZYNQ-7000 SoC 处理器XC7Z035-2FFG676I。适用于无人机蜂群、软件无线电系统,基带信号处理,无线仿真平台,高速图像采集、处理等领域。
DSP+ZYNQ硬件说明手册-XQTyer【开源】