学习内容
本文介绍关于AXI BRAM控制器的相关内容,针对数据量较少、地址不连续、长度不规则的情况,通过 BRAM 来进行数据的交互。
开发环境
vivado 18.3&SDK,PYNQ-Z2开发板。
AXI BRAM控制器
简介
BRAM控制器可以用于与 AXI 互连和系统主设备的集成,以与本地块 RAM 进行通信。 内核支持到块 RAM 的单次和突发传输,并针对性能进行了优化。
AX14或AX14- lite控制器配置中,可以配置到BRAM块的单个端口或到BRAM块的两个端口。通过第二个AX14-Lite控制端口连接,AXI BRAM控制器IP可以在数据路径上配置ECC功能,并通过可用的外部ECC寄存器设置。AXI BRAM Controller IP核的顶级端口连接和主模块如下图所示。展示了AX14-Lite模式下,AXI BRAM核心与BRAM块的连接。可以利用BRAM块的单端口利用率或BRAM块的双端口模式(通过参数设置)。
下图展示了为支持AX14接口而生成的HDL核心。对BRAM块的单端口使用可以配置在双端口配置中增强的性能设置。,详细结构框图如下:
所有与axis主设备的通信都是通过一个5通道的axis接口进行的。所有写操作都在AXI总线的写地址通道(AW)上启动,该通道指定了写事务的类型和相应的地址信息。写数据通道(W)为单个或突发写操作通信所有写数据。写响应通道(B)用作写操作的握手或响应。
在读操作上,当AXI主程序请求读传输时,读地址通道(AR)通信所有地址和控制信息。当可以处理读操作时,AXI从AXI BRAM控制器IP响应读地址通道(AR)。当读取数据可用时,读数据通道®将转换操作的数据和状态。
支持内存大小
AXI BRAM Controller支持的内存最大为2mbytes(字节大小为8或9),支持的内存宽度和深度如表1-1所示。
AXI BRAM Controller IP支持的最小深度为512字节。任何小于512的深度都被调整为512字节。
系统框图与工程设计
工程功能设计为PS 将串口接收到的数据写入 BRAM,然后从 BRAM 中读出数据,并通过串口打印出来;与此同时, PL 从 BRAM 中同样读出数据,并通过 ILA 来观察读出的数据与串口打印的数据是否一致。
系统框图如下:
硬件平台搭建
新建工程,创建 block design。
配置ZYNQ7
添加ZYNQ7 IP,对zynq进行初始化配置,勾选配置uart资源,
使能clock复位和 M_GP0接口,
配置时钟,
配置BRAM控制器和BRAM
接着配置BRAM控制器,基本是默认配置。
配置BRAM
连接连线后系统如下,
设计读取控制模块
首先点击tools创建一个新的IP,
选择创建一个AXI4接口的IP。
编辑IP名称等信息,设计IP的接口信息,
点击finish,完成IP创建。
在IP目录下找到自己创建好的IP,右键进行IP的编辑。
在顶层进行例化ram接口。
在AXI总线协议实现的文件中添加IP的例化,实现AXI-Lite接口的功能进行参数的传递。
这里引用正点原子的BRAM的读取模块,
bram_rd.v
module bram_rd( 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 define reg [1:0] flow_cnt; reg start_rd_d0; reg start_rd_d1; //wire define wire 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) begin if(!rst_n) begin start_rd_d0 <= 1'b0; start_rd_d1 <= 1'b0; end else begin start_rd_d0 <= start_rd; start_rd_d1 <= start_rd_d0; end end //根据读开始信号,从RAM中读出数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin flow_cnt <= 2'd0; ram_en <= 1'b0; ram_addr <= 32'd0; ram_we <= 4'd0; end else begin case(flow_cnt) 2'd0 : begin if(pos_start_rd) begin ram_en <= 1'b1; ram_addr <= start_addr; flow_cnt <= flow_cnt + 2'd1; end end 2'd1 : begin if(ram_addr - start_addr == rd_len - 4) begin //数据读完 ram_en <= 1'b0; flow_cnt <= flow_cnt + 2'd1; end else ram_addr <= ram_addr + 32'd4; //地址累加4 end 2'd2 : begin ram_addr <= 32'd0; flow_cnt <= 2'd0; end endcase end end endmodule
创建引脚接口,选择任意一个BRAM引脚,创建封装
设置接口和名称,
完成接口映射。
然后点击完成IP封装。
完成系统设计
完成IP的创建后,添加IP,完成连线,整体设计如下图所示:
然后在完成综合后进行setup debug ,抓取b端口有关的信号。
完成添加DEDUG信号后,进行综合生成bit流,然后导出硬件,launch SDK。
SDK软件部分
新建应用工程,
main.c中输入以下代码:
#include "xil_printf.h" #include "stdio.h" #include "xbram_hw.h" #include "ps_pl_rd_ip.h" #include "xparameters.h" #define PL_BRAM_START PS_PL_RD_IP_S00_AXI_SLV_REG0_OFFSET #define PL_BRAM_START_ADDR PS_PL_RD_IP_S00_AXI_SLV_REG1_OFFSET #define PL_BRAM_LEN PS_PL_RD_IP_S00_AXI_SLV_REG2_OFFSET #define PS_PL_BASEADDR XPAR_PS_PL_RD_IP_0_S00_AXI_BASEADDR #define START_ADDR 0 #define BRAM_DATA_BYTE 4 char input_data[1024]; int len_input_data; int main(){ while(1){ int i=0; int wr_cnt=0; printf("ps_pl_bram test\n"); scanf("%s",input_data); len_input_data= strlen(input_data); for(i = START_ADDR*BRAM_DATA_BYTE;i<(START_ADDR + len_input_data)*BRAM_DATA_BYTE;i+=BRAM_DATA_BYTE) { PS_PL_RD_IP_mWriteReg(XPAR_BRAM_0_BASEADDR,i,input_data[wr_cnt]); wr_cnt++; } //配置起始地址 PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_START_ADDR,START_ADDR*BRAM_DATA_BYTE); //配置读取长度 PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_LEN,len_input_data*BRAM_DATA_BYTE); //使能脉冲 PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_START,1); PS_PL_RD_IP_mWriteReg(PS_PL_BASEADDR,PL_BRAM_START,0); for(i = START_ADDR*BRAM_DATA_BYTE;i<(START_ADDR + len_input_data)*BRAM_DATA_BYTE;i+=BRAM_DATA_BYTE) { printf("bram address : %d ,read data : %c\n",i/BRAM_DATA_BYTE,PS_PL_RD_IP_mReadReg(XPAR_BRAM_0_BASEADDR,i)); } } }
部分代码讲解
本次工程比较简单,在while循环中实现了对串口输入的存储和显示打印。
运行效果
ila抓取数据
通过ILA抓取的读取数据和发送写入的数据一致。
references
- 正点原子开发视频
- 正点原子嵌入式开发指南
- UG585
- PG078