☰
【正点原子ZYNQ领航者7020】PS写ramPL读ram实验
2026/9/30 8:56:57 网站建设 项目流程

文章目录

    • 前言
      • 1.创建Vivado工程
      • 2.创建AXI BRAM控制器
      • 3.创建pl_bram_rd IP核
      • 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:

modulebram_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)beginif(!rst_n)begin start_rd_d0<=1'b0;start_rd_d1<=1'b0;endelsebegin start_rd_d0<=start_rd;start_rd_d1<=start_rd_d0;end end//根据读开始信号,读RAM中的数据always @(posedge clk or negedge rst_n)beginif(!rst_n)begin rd_flag<=1'b0;ram_en<=1'b0;ram_addr<=32'd0;endelsebeginif(rd_flag==1'b0)beginif(pos_start_rd)begin//判断读开始信号的上升沿ram_en<=1'b1;ram_addr<=start_addr;rd_flag<=1'b1;end endelsebeginif(ram_addr==start_addr+rd_len-32'd4)begin ram_en<=1'b0;ram_addr<=32'd0;rd_flag<=0;endelseram_addr<=ram_addr+32'd4;end end end endmodule

在编写bram_rd.v之后编辑pl_bram_rd_v1_0_S00_AXI.v
加入以下两节:

bram_rdu_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));
//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中加入下面这两段:

//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读数据
.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

#include"xil_printf.h"#include"stdio.h"#include"xbram.h"#include"xparameters.h"#include"pl_bram_rd.h"#defineSTART_ADDR0//BRAM起始地址#defineBRAM_DATA_BYTE4//BRAM数据的字节宽度#definePL_BRAM_BASEXPAR_PL_BRAM_RD_0_S00_AXI_BASEADDR//PL_BRAM_RD_IP核的基地址#definePL_BRAM_STARTPL_BRAM_RD_S00_AXI_SLV_REG0_OFFSET//PL_BRAM_RD_IP核的读开始寄存器#definePL_BRAM_START_ADDRPL_BRAM_RD_S00_AXI_SLV_REG1_OFFSET//PL_BRAM_RD_IP核的读起始地址的寄存器#definePL_BRAM_LENPL_BRAM_RD_S00_AXI_SLV_REG2_OFFSET//PL_BRAM_RD_IP核的读长度寄存器voidstr_wr_bram(void);voidpl_bram_rd_cfg(void);voidstr_rd_bram(void);charch_data[1024];intch_data_len;intmain(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();//将用户输入的字符串写入BRAMpl_bram_rd_cfg();//配置PL_BRAMstr_rd_bram();//从BRAM中读出数据}}//将用户输入的字符串写入BRAMvoidstr_wr_bram(void){inti=0;intwr_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++;}}voidpl_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中读出数据voidstr_rd_bram(void){inti=0;intread_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_data[wr_cnt]) 时:

你的 ch_data[wr_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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询