【正点原子ZYNQ领航者7020】PS写ramPL读ram实验

文章目录

前言

无,仅作记录,不具有参考价值。在hello world实验工程上进行修改。

1.创建Vivado工程

在hello world的工程上完成block design的搭建。

点击OK完成配置

2.创建AXI BRAM控制器

配置如上

以下为添加bram的步骤

搜索

点击ok完成配置

以下为设置bram存储容量(变相修改深度,axi4-lite无法修改宽度)

修改容量为4k个字节,即深度=4k*8/32=1024

先删除wrapper文件,防止vivado不更新

对bd文件执行generate products

生成wrapper文件之后生成bit流文件

生成bit流文件之后到处xsa文件

3.创建pl_bram_rd IP核

根据自定义创建IP核那一节建立编辑IP核工程

将创建的文件放到hdl文件夹下

(这里名字打错了,应该是bram_rd)

bram_rd.v:

c 复制代码
module bram_rd(
    input clk,
    input rst_n,

    //PS配置的参数
    input start_rd, //读开始信号
    input [31:0] start_addr, //读起始地址
    input [31:0] rd_len, //读长度,以字节为单位

    //BRAM的端口
    output ram_clk,
    output ram_rst, //RAM复位信号,高电平有效
    output reg ram_en, //RAM使能信号,高电平有效
    output [3:0] ram_we, //RAM写使能信号1:写 0:读
    output reg [31:0] ram_addr, //RAM读写地址
    output [31:0] ram_we_data, //RAM写数据
    input [31:0] ram_rd_data //RAM读数据
);

reg start_rd_d0;
reg start_rd_d1;
reg rd_flag;

wire pos_start_rd;
assign pos_start_rd = start_rd_d0 & ~start_rd_d1;
assign ram_clk = clk;
assign ram_rst = 1'b0;
assign ram_we = 4'b0;
assign ram_we_data = 32'b0;

//对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
        rd_flag <= 1'b0;
        ram_en <= 1'b0;
        ram_addr <= 32'd0;
    end
    else begin
        if(rd_flag == 1'b0)begin
            if(pos_start_rd)begin //判断读开始信号的上升沿
                ram_en <= 1'b1;
                ram_addr <= start_addr;
                rd_flag <= 1'b1;
            end
        end
        else begin
            if(ram_addr == start_addr+ rd_len - 32'd4) begin
                ram_en <= 1'b0;
                ram_addr <= 32'd0;
                rd_flag <= 0;
            end
            else
                ram_addr <= ram_addr + 32'd4;
        end
    end
end
endmodule

在编写bram_rd.v之后编辑pl_bram_rd_v1_0_S00_AXI.v

加入以下两节:

c 复制代码
	bram_rd u_bram_rd(
		.clk         (S_AXI_ACLK       		),
		.rst_n       (S_AXI_ARESETN      	),

		.start_rd    (slv_reg0[0]    			),
		.start_addr  (slv_reg1  			),
		.rd_len      (slv_reg2      		),

		.ram_clk     (ram_clk     			),
		.ram_rst     (ram_rst     			),
		.ram_en      (ram_en      			),
		.ram_we      (ram_we      			),
		.ram_addr    (ram_addr    			),
		.ram_we_data (ram_we_data 			),
		.ram_rd_data (ram_rd_data 			)
	);
c 复制代码
		//BRAM的端口
		output ram_clk,
		output ram_rst, //RAM复位信号,高电平有效
		output ram_en, //RAM使能信号,高电平有效
		output [3:0] ram_we, //RAM写使能信号1:写 0:读
		output [31:0] ram_addr, //RAM读写地址
		output [31:0] ram_we_data, //RAM写数据
		input [31:0] ram_rd_data, //RAM读数据

pl_bram_rd_v1_0中加入下面这两段:

c 复制代码
		//BRAM的端口
		output ram_clk,
		output ram_rst, //RAM复位信号,高电平有效
		output ram_en, //RAM使能信号,高电平有效
		output [3:0] ram_we, //RAM写使能信号1:写 0:读
		output [31:0] ram_addr, //RAM读写地址
		output [31:0] ram_we_data, //RAM写数据
		input [31:0] ram_rd_data, //RAM读数据
c 复制代码
		.ram_clk     (ram_clk     			),
		.ram_rst     (ram_rst     			),
		.ram_en      (ram_en      			),
		.ram_we      (ram_we      			),
		.ram_addr    (ram_addr    			),
		.ram_we_data (ram_we_data 			),
		.ram_rd_data (ram_rd_data 			),

综合一下,看看有没有问题

以下为编辑总线接口:

右击ram_rst选择remove interface,ram_clk也是

点击ok

选择Port Mapping做好映射

点击ok,封装引脚完毕

对IP进行重新打包

若要添加IP适配型号,可以这样做

4.搭建block design

在原来的工程中添加IP核路径

链接pl_bram_rd和bram

再次自动连接

验证之后发现问题

原因是两个总线接口的Master Type参数不匹配

重新进入编辑IP核工程

填写BRAM_CTRL,然后重新打包

回到原始工程点击

先不对db文件进行编译

rerun刷新

没问题了,再次刷新

刷新之后进行更改自定义IP核的makefile文件

右击BRAM_PORTB的连接线,选择DEBUG

点击自动连线,添加ILAIP核

双击ILAIP核修改深度为2048

5.创建vitis工程并执行

编译最新的bd文件并生成wrapper然后重新生成bit流导出xsa文件

打开vitis创建工程及main.c

c 复制代码
#include "xil_printf.h"
#include "stdio.h"
#include "xbram.h"
#include "xparameters.h"
#include "pl_bram_rd.h"

#define START_ADDR 0 //BRAM起始地址
#define BRAM_DATA_BYTE 4 //BRAM数据的字节宽度
#define PL_BRAM_BASE XPAR_PL_BRAM_RD_0_S00_AXI_BASEADDR //PL_BRAM_RD_IP核的基地址
#define PL_BRAM_START 		PL_BRAM_RD_S00_AXI_SLV_REG0_OFFSET //PL_BRAM_RD_IP核的读开始寄存器
#define PL_BRAM_START_ADDR 	PL_BRAM_RD_S00_AXI_SLV_REG1_OFFSET //PL_BRAM_RD_IP核的读起始地址的寄存器
#define PL_BRAM_LEN 		PL_BRAM_RD_S00_AXI_SLV_REG2_OFFSET //PL_BRAM_RD_IP核的读长度寄存器

void str_wr_bram(void);
void pl_bram_rd_cfg(void);
void str_rd_bram(void);

char ch_data[1024];
int ch_data_len;
int main(void)
{
	while(1)
	{
		printf("please enter data tp write BRAM\r\n");
		scanf("%1024s", ch_data); //接收用户输入的字符串
		ch_data_len = strlen(ch_data); //计算字符串的长度

		str_wr_bram(); //将用户输入的字符串写入BRAM
		pl_bram_rd_cfg(); //配置PL_BRAM
		str_rd_bram(); //从BRAM中读出数据
	}
}

//将用户输入的字符串写入BRAM
void str_wr_bram(void)
{
	int i = 0;
	int wr_cnt = 0;
	for(i = START_ADDR * BRAM_DATA_BYTE; i < (START_ADDR + ch_data_len) * BRAM_DATA_BYTE;
			i = i + 4)
	{
		XBram_WriteReg(XPAR_BRAM_0_BASEADDR, i, ch_data[wr_cnt]);
		wr_cnt++;
	}
}

void pl_bram_rd_cfg(void)
{
	//配置读起始地址
	PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_START_ADDR, START_ADDR * BRAM_DATA_BYTE);
	//配置读长度
	PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_LEN, ch_data_len * BRAM_DATA_BYTE);
	//开始读
	PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_START, 1);
	PL_BRAM_RD_mWriteReg(PL_BRAM_BASE, PL_BRAM_START, 0);

}

//从BRAM中读出数据
void str_rd_bram(void)
{
	int i = 0;
	int read_data;
	for(i = START_ADDR * BRAM_DATA_BYTE; i < (START_ADDR + ch_data_len) * BRAM_DATA_BYTE;
			i = i + 4)
	{
		read_data = XBram_ReadReg(XPAR_BRAM_0_BASEADDR, i);
		printf("BRAM address is %d, read data is %c\r\n", i/4, read_data);
	}
}

复制以上代码

给板子上电之后,开启xilinx的seial terminal,选择对应的串口

回到vivado,点击open target

选择auto connect

将触发信号拖到右下角,并将触发类型设置为上升沿触发

点击运行,等待触发

随便输入什么,比如123456

功能是对的

6.如何对IP核内部信号进行debug

倘若想要测试ip核的内部信号,可以在.v文件中添加debug属性

例如在IP核中的bram_rd.v代码中添加(* mark_debug = "true" *)

添加好之后重新打包和封装即可

更新bd文件中的ip核

接下来对工程进行综合

之后打开综合后的设计(这一步软件可能会卡死,如果卡死了就重新打开vivado)

点击setup debug

右击undefined选择时钟

rd_flag信号也是一样,时钟设置之后点击next

深度选择完毕之后点击next

ctrl+s保存

这里会自动跳出创建xdc文件,命名debug

关闭综合设计之后重新生成bit流文件,编程完毕之后到处xsa文件,在vitis中更新硬件平台工程,重新编译并下载,先关闭open hardware manager,然后再打开,auto connect

可以看到有两个ila界面

把pos也拉到触发设置那并设置上升沿触发

点击运行,等待触发

依旧随便输入

看到信号有跳变沿

若要清除block design里的ilaip核,可以右击debug的信号线,选择clear debug

7.如何取消debug

若要删除自定义ip核的内部debug,要将xdc文件从工程中移除(从磁盘中移除)

再在原来的ip核代码中删除(* mark_debug = "true" *)

之后重新打包,并更新db即可

然后重新生成wrapper,生成bit流文件,导出xsa更新vitis工程

总结

需要注意的是正点原子的这份程序浪费了整整 3/4 的 BRAM 空间。

在 Zynq 的架构中,BRAM 控制器挂载在 AXI 总线上,标准的数据位宽是 32 位(4 个字节)。

XBram_WriteReg 底层实际上调用的是 Xil_Out32,也就是一次性写入 32 位(4 字节)的数据。

当你执行 XBram_WriteReg(BASE, i, ch_datawr_cnt) 时:

你的 ch_datawr_cnt 只是一个 8 位的 char(比如字符 'A',十六进制是 0x41)。

C 语言编译器会自动将它高位补零,强转成 32 位:0x00000041。

BRAM 控制器一口吞下了这个 32 位数据,把它存放在 i 到 i+3 这 4 个字节的地址空间里。

接着你的循环 i = i + 4 直接跳到了下一个 32 位地址。

结果就是: 每 4 个字节的 BRAM 空间里,只有最低的 1 个字节存了你的有效字符,高 3 个字节全是 0x00。这就是 3/4 空间浪费。

工程分享:

ps_pl_bram_pra.zip

为防止3/4的存储空间浪费,对IP核的源代码核vitismain.c做了修改:

ps_pl_bram_pra_fix.zip

相关推荐
szxinmai主板定制专家6 天前
RK3588+FPGA异构架构|高速数据采集+边缘AI落地应用全解析
人工智能·嵌入式硬件·fpga开发·架构·zynq
szxinmai主板定制专家6 天前
FPGA高速IO+RK182X AI算力|RK3576+CODESYS工业自动化运动控制平台设计
运维·人工智能·嵌入式硬件·fpga开发·自动化·zynq
szxinmai主板定制专家17 天前
【工控实战】RK3568/RK3588 8路隔离CAN/CANFD完整解决方案|多路总线并行通信、车载/工控量产落地
人工智能·fpga开发·zynq·mpsoc·半导体设备
szxinmai主板定制专家17 天前
【嵌入式实战】RK3588+GMSL多路相机采集方案|SerDes长距离低延时视频流落地教程
人工智能·单片机·机器人·边缘计算·zynq
火花页.20 天前
【正点原子ZYNQ领航者7020】自定义IP核
zynq
szxinmai主板定制专家1 个月前
基于 RK3588+RK1820/28 算力卡的国产工控机,适用于机器视觉,机器人等边缘算力场景
人工智能·fpga开发·zynq·控制器·mpsoc·半导体设备
努力搬砖的小H1 个月前
FPGA应用技巧之Block Design基本使用方法
功能测试·fpga开发·zynq·block design
szxinmai主板定制专家2 个月前
基于 RK3588 + Xilinx Kintex-7 FPGA 异构工业主控板设计方案
arm开发·人工智能·嵌入式硬件·fpga开发·zynq
szxinmai主板定制专家2 个月前
Zynq MPSoC实现半导体真空腔体状态监测|FPGA多传感器时序同步采集方案
fpga开发·zynq·控制器·mpsoc·半导体设备·fpga同步