文章目录
-
- 前言
-
- 1.创建Vivado工程
- [2.创建AXI BRAM控制器](#2.创建AXI BRAM控制器)
- [3.创建pl_bram_rd IP核](#3.创建pl_bram_rd IP核)
- [4.搭建block design](#4.搭建block design)
- 5.创建vitis工程并执行
- 6.如何对IP核内部信号进行debug
- 7.如何取消debug
- 总结
前言
无,仅作记录,不具有参考价值。在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 空间浪费。
工程分享:
为防止3/4的存储空间浪费,对IP核的源代码核vitismain.c做了修改: