ZYNQ PS与PL共享DDR

平台:   

 开发板:ZYNQ-7000系列 裸板开发

开发环境:vivado hls、vivado、sdk

参考

https://blog.csdn.net/weixin_36474809/article/details/85111550

https://www.csdn.net/link?target_url=http%3A%2F%2Fwww.eefocus.com%2Fantaur%2Fblog%2F17-08%2F423773_0818c.html&id=97229926&token=187366a068bb5613952e707d19e33bfa

https://blog.csdn.net/yqq654101/article/details/80373971

背景

    很多时候FPGA上的资源是很有限的,有时候需要PL端存储和读取大量的数据,光靠BRAM是不够的,因此需要与PS共享DDR作为PL端的Global memory。用DDR作为PL端的全局内存有两大好处:1  缓解PL端缓存压力 2  作为共享内存实现PL端和PS端通信

目的

    个人课题为FPGA加速CNN,因此目的如下:

1 能够将CNN的权重放在DDR上,PL端需要权重的时候从DDR上读取。即PS写DDR,PL读DDR数据。

2 PL将卷积结果写回到DDR,PS端对结果进行后续处理。即PL写DDR,PS读。

hls 测试用IPCore

测试用的IPCore在vivado hls中编写,代码如下:

void pl_ddr_RW(volatile int*din,volatile int*dout,int add)

{

#pragma HLS INTERFACE s_axilite port=return bundle=CONTROL

#pragma HLS INTERFACE s_axilite port=add bundle=CONTROL

#pragma HLS INTERFACE m_axi depth=4 port=dout offset=slave bundle=DATA_OUT

#pragma HLS INTERFACE m_axi depth=4 port=din offset=slave bundle=DATA_IN

    for(int i = 0; i < 10;i++)

    {

  #pragma HLS PIPELINE II=1

        dout[i] = din[i] + add;

}

    核心功能很简单,从DDR某个地址开始连续读10个数,加上一个偏置ADD,写回到DDR的另一个地址开始连续写10个数。从而测试PL的读写功能。

    将din和dout设置为m_axi总线用于向DDR读写数据,offset  = slave指定其起始地址的偏置由ps端设定,return和add是少量数据,适合于用s_axilite 总线来读写,并绑定为CONTROL总线。

    因为程序比较简单因此省略了testBench和cosimulation,具体设计过程的hls参考ug902,最后输出export RTL在vivado中使用。

vivado 硬件环境搭建

选择part,添加和编辑IPCore等等一些常规操作后搭建的硬件系统如下:

别忘了检查address,在zynq中ps和pl的地址映射关系是一致的,因此可不用特别设置。

地址设置

    generate output product和create HDL wrapper,并且生成bitstream,file 中 export hardware 要include bitstream,最后再vivado中lauch SDK,进行软件程序的编写。

SDK 软件测试程序

#include <stdio.h>

#include "platform.h"

#include "xil_printf.h"

#include "xpl_ddr_rw.h"

XPl_ddr_rw pl_ddr_rw_instance;

int main()

{

init_platform();

printf("begin...\n\r");

volatile int *read_ptr = 0x30000000;

volatile int *write_ptr = 0x30000000+sizeof(int)*10;

for(int i =0; i < 10;i++) *(read_ptr+i) = i;

for(int i =0; i < 10;i++) *(write_ptr+i) = 0; 

XPl_ddr_rw_Initialize(&pl_ddr_rw_instance,XPAR_PL_DDR_RW_0_DEVICE_ID);

XPl_ddr_rw_InterruptGlobalDisable(&pl_ddr_rw_instance);

XPl_ddr_rw_DisableAutoRestart(&pl_ddr_rw_instance); 

XPl_ddr_rw_Set_din(&pl_ddr_rw_instance,read_ptr); 

XPl_ddr_rw_Set_dout(&pl_ddr_rw_instance,write_ptr); 

XPl_ddr_rw_Set_add(&pl_ddr_rw_instance,10); 

XPl_ddr_rw_Start(&pl_ddr_rw_instance); 

while(!XPl_ddr_rw_IsDone(&pl_ddr_rw_instance))
    printf("working...\n\r");

for(int i =0; i < 10;i++)

{

printf("loc: %d val: %d \n\r",i,*(write_ptr+i));

}

printf("end...\n\r");

cleanup_platform();

return 0;

}

注意:新建的application是用的memory test模板,和hello world模板不同的是其init_platform()函数中有Xil_DCacheDisable()函数,能够声明不使用DCache。

    如果用的是helloworld模板,Dcache还是在使用的。(猜测,未证实)由于Dcache的存在,会导致PL写DDR的时候,写到了一个特殊的内存中,等待main函数结束后才会从该特殊内存复制到DDR中指定的地址,因此在同一个程序中PS无法从DDR指定地址中获得希望的结果。证明方法:在不reset的情况下,debug模式运行程序,会发现DDR中指定地址的数据是希望得到的结果。

    因此,在没弄清楚原理的时候,Dcache记得先disable。

2022/09/22更新:Dcache贼简单了,是 cpu 的多级缓存架构 L1/L2/L3,因为FPGA属于异构设备(没有consistency机制,或者需要实现)需要通过软件把数据从dcache刷到ddr,或者干脆禁用dcache(对cpu性能影响很大)

串口接收验证

上述SDK程序在串口窗口中打印的结果如下:

串口打印

综合SDK程序,成功测试了PL的对DDR的读写和PS对DDR的读写功能。

总结

    该测试实验中,关闭Dcache是比较重要的操作。另外,读写的数据直接选择了0x30000000是比较危险的做法,因为如果该位置有指令的话会直接覆盖指令从而造成应用程序的崩溃。但是看一些分析指令集中在0x00100000~0x00000000的位置,因此不会崩溃。从lscript.ld链接文件也能看出,对于裸机开发来说指令空间是很小的,在较低的地址空间,选较高的地址位不会出现崩溃的问题。

  • 1
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值