PL读不到PS写入DDR的数据

这篇具有很好参考价值的文章主要介绍了PL读不到PS写入DDR的数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、背景

平台:ZYNQ7020

CPU0的设置默认,CPU1工程bsp设置-DUSE_AMP=1,PL以AXI接口访问DDR。

CPU1往DDR中写数据后用Xil_DCacheFlushRange函数将数据回写到DDR,但是随后PL在DDR中读不到相关数据。

把CPU1中的相关代码放到CPU0中运行,PL能读到DDR中的相关数据。

2、原因分析

CPU1工程bsp中设置-DUSE_AMP=1只是让CPU1工程中涉及到共享资源初始化的代码段不被编译进应用程序,这样CPU1启动后就不会再进行共享资源的初始化1。但是由于CPU0工程bsp未设置-DUSE_AMP=1,所以这些共享资源的初始化代码会被编译进CPU0的应用程序。也就是说,在两个CPU启动之后,相关的共享资源会被CPU0初始化,所以CPU1不需要去重复初始化就可以使用相关的共享资源了(包括L2 Cache),防止两个CPU同时进行初始化时出现问题。

Xil_DCacheFlushRange函数源码中有预编译宏#ifndef USE_AMP,如下:

void Xil_DCacheFlushRange(INTPTR adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	u32 currmask;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INV_CLN_PA_OFFSET);

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		end = LocalAddr + len;
		LocalAddr &= ~(cacheline - 1U);

		while (LocalAddr < end) {

	/* Flush L1 Data cache line */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_clean_inval_dc_line_mva_poc(LocalAddr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_CLEAN_INVAL_DC_LINE_MVA_POC);
			  Reg = LocalAddr; }
#endif
#ifndef USE_AMP
			/* Flush L2 cache line */
			*L2CCOffset = LocalAddr;
			Xil_L2CacheSync();
#endif
			LocalAddr += cacheline;
		}
	}
	dsb();
	mtcpsr(currmask);
}

即当CPU1工程bsp设置了-DUSE_AMP=1时,#ifndef USE_AMP ...... #endif部分代码便不会编译进应用程序中。而这部分代码又与L2 Cache有关,所以CPU1通过Xil_DCacheFlushRange函数往DDR中刷新数据时,只是将数据刷新到了L2 Cache,没有立即刷新到DDR,故PL在DDR中读不到数据。

3、解决办法

针对以上问题,我们可以将Xil_DCacheFlushRange函数改写,将其中的预编译宏#ifndef USE_AMP删除,如下:

void Amp_DCacheFlushRange(INTPTR adr, u32 len)
{
	u32 LocalAddr = adr;
	const u32 cacheline = 32U;
	u32 end;
	u32 currmask;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INV_CLN_PA_OFFSET);

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		/* Back the starting address up to the start of a cache line
		 * perform cache operations until adr+len
		 */
		end = LocalAddr + len;
		LocalAddr &= ~(cacheline - 1U);

		while (LocalAddr < end) {

	/* Flush L1 Data cache line */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_clean_inval_dc_line_mva_poc(LocalAddr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_CLEAN_INVAL_DC_LINE_MVA_POC);
			  Reg = LocalAddr; }
#endif

			/* Flush L2 cache line */
			*L2CCOffset = LocalAddr;
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_SYNC_OFFSET, 0x0U);
			LocalAddr += cacheline;
		}
	}
	dsb();
	mtcpsr(currmask);
}

然后再CPU1中以Amp_DCacheInvalidateRange函数替代Xil_DCacheFlushRange函数,以上问题便可解决。

4、深入挖掘

Xil_DCacheInvalidateRange函数源码如下:

void Xil_DCacheInvalidateRange(INTPTR adr, u32 len)
{
	const u32 cacheline = 32U;
	u32 end;
	u32 tempadr = adr;
	u32 tempend;
	u32 currmask;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INVLD_PA_OFFSET);

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		end = tempadr + len;
		tempend = end;
		/* Select L1 Data cache in CSSR */
		mtcp(XREG_CP15_CACHE_SIZE_SEL, 0U);

		if ((tempadr & (cacheline-1U)) != 0U) {
			tempadr &= (~(cacheline - 1U));

			Xil_L1DCacheFlushLine(tempadr);
#ifndef USE_AMP
			/* Disable Write-back and line fills */
			Xil_L2WriteDebugCtrl(0x3U);
			Xil_L2CacheFlushLine(tempadr);
			/* Enable Write-back and line fills */
			Xil_L2WriteDebugCtrl(0x0U);
			Xil_L2CacheSync();
#endif
			tempadr += cacheline;
		}
		if ((tempend & (cacheline-1U)) != 0U) {
			tempend &= (~(cacheline - 1U));

			Xil_L1DCacheFlushLine(tempend);
#ifndef USE_AMP
			/* Disable Write-back and line fills */
			Xil_L2WriteDebugCtrl(0x3U);
			Xil_L2CacheFlushLine(tempend);
			/* Enable Write-back and line fills */
			Xil_L2WriteDebugCtrl(0x0U);
			Xil_L2CacheSync();
#endif
		}

		while (tempadr < tempend) {
#ifndef USE_AMP
			/* Invalidate L2 cache line */
			*L2CCOffset = tempadr;
			Xil_L2CacheSync();
#endif

	/* Invalidate L1 Data cache line */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_inval_dc_line_mva_poc(tempadr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_INVAL_DC_LINE_MVA_POC);
			  Reg = tempadr; }
#endif
			tempadr += cacheline;
		}
	}

	dsb();
	mtcpsr(currmask);
}

可见Xil_DCacheInvalidateRange函数中也用预编译宏#ifndef USE_AMP预编译了L2 Cache的相关代码,那CPU1在用Xil_DCacheInvalidateRange函数获取PL写到DDR中的数据为什么又可以呢?

这应该与CPU0有关,因为L2 Cache是CPU0和CPU1共享的,CPU0也在不断地从DDR中获取指令和数据,这个过程中可能就将CPU1从DDR中获取并缓存在L2 Cache中的数据给覆盖掉。当CPU1要获取PL写到DDR中的数据时,调用Xil_DCacheInvalidateRange函数会先使L1 Cache中的数据无效,然后再到L2 Cache中去找。因为已经被CPU0的数据给覆盖了,所以找不到,然后直接到DDR中获取数据。

所以CPU1在调用Xil_DCacheInvalidateRange函数时,最好将其中的预编译宏#ifndef USE_AMP给删掉,如下:

void Amp_DCacheInvalidateRange(INTPTR adr, u32 len)
{
	const u32 cacheline = 32U;
	u32 end;
	u32 tempadr = adr;
	u32 tempend;
	u32 currmask;
	volatile u32 *L2CCOffset = (volatile u32 *)(XPS_L2CC_BASEADDR +
				    XPS_L2CC_CACHE_INVLD_PA_OFFSET);

	currmask = mfcpsr();
	mtcpsr(currmask | IRQ_FIQ_MASK);

	if (len != 0U) {
		end = tempadr + len;
		tempend = end;
		/* Select L1 Data cache in CSSR */
		mtcp(XREG_CP15_CACHE_SIZE_SEL, 0U);

		if ((tempadr & (cacheline-1U)) != 0U) {
			tempadr &= (~(cacheline - 1U));

			Xil_L1DCacheFlushLine(tempadr);
			/* Disable Write-back and line fills */
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_DEBUG_CTRL_OFFSET, 0x3U);
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_CLEAN_PA_OFFSET, tempadr);
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INVLD_PA_OFFSET, tempadr);
			dsb();
			/* Enable Write-back and line fills */
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_DEBUG_CTRL_OFFSET, 0x0U);
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_SYNC_OFFSET, 0x0U);

			tempadr += cacheline;
		}
		if ((tempend & (cacheline-1U)) != 0U) {
			tempend &= (~(cacheline - 1U));

			Xil_L1DCacheFlushLine(tempend);

			/* Disable Write-back and line fills */
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_DEBUG_CTRL_OFFSET, 0x3U);
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_CLEAN_PA_OFFSET, tempend);
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_INVLD_PA_OFFSET, tempend);
			dsb();
			/* Enable Write-back and line fills */
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_DEBUG_CTRL_OFFSET, 0x0U);
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_SYNC_OFFSET, 0x0U);

		}

		while (tempadr < tempend) {
			/* Invalidate L2 cache line */
			*L2CCOffset = tempadr;
			Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_SYNC_OFFSET, 0x0U);

	/* Invalidate L1 Data cache line */
#if defined (__GNUC__) || defined (__ICCARM__)
			asm_cp15_inval_dc_line_mva_poc(tempadr);
#else
			{ volatile register u32 Reg
				__asm(XREG_CP15_INVAL_DC_LINE_MVA_POC);
			  Reg = tempadr; }
#endif
			tempadr += cacheline;
		}
	}

	dsb();
	mtcpsr(currmask);
}

此时,基本上就没什么问题了。

除了性能的不确定性。2

因为L2 Cache是CPU0和CPU1共享的,同时使用时肯定会存在竞争问题,从而给CPU的运行速度带来不确定性。

此时我们可以考虑将L2 Cache均分给CPU0和CPU1使用,使两者互不影响。3

我们可以在CPU0应用程序入口处调用如下函数将L2 Cache均分给CPU0和CPU1:

//L2 CACHE在cpu0与cpu1进行等分
void L2CacheCut(void)
{
	u32 RegValue;
	
	RegValue = 0x0000000f;
    Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_DLCKDWN_0_WAY_OFFSET, RegValue);
    Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_ILCKDWN_0_WAY_OFFSET, RegValue);

    RegValue = 0x000000f0;
    Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_DLCKDWN_1_WAY_OFFSET, RegValue);
    Xil_Out32(XPS_L2CC_BASEADDR + XPS_L2CC_CACHE_ILCKDWN_1_WAY_OFFSET, RegValue);
}

至此,相关问题才算完美解决。

不过有些地方纯属个人猜测,如果有误,欢迎评论指正。


  1. ZYNQ双核AMP开发详解(USE_AMP -DUSE_AMP=1 含义和作用详解) ↩︎

  2. ZYNQ7000双核AMP工作方式下如何共享L2 Cache ↩︎

  3. Zynq7000 双核运行 L2Cache 寄存器配置 划分Cache ↩︎文章来源地址https://www.toymoban.com/news/detail-458143.html

到了这里,关于PL读不到PS写入DDR的数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机)

    AXI DMA 操作需要先提供一个在内存中驻留的不变空间,用于存储需要进行的DMA操作。形容这“每一次操作”的东西叫做Buffer Descriptor,缩写叫BD,这些BD是连接成链表的形式的,因为BD会动态增加,而预先分配存储BD的空间是恒定的,因此BD被连成一个环(BD Ring),其实就是一个循

    2024年02月09日
    浏览(40)
  • ZYNQ7000 PL与PS交互(一): PL到PS中断的使用与实现

    翻译以xilinx用户手册ug585,知道ZYNQ7000有几类中断即可。 PS基于ARM架构,使用了两个Cortex-A9处理器和GIC PL390中断控制器。中断结构与CPU密切相关,并接收来自IO外设和可编程单元PL的中断请求。 本章主要信息: 私有、共享和软件中断。 GIC功能介绍 中断优先级和处理 1.1.1 Zynq 中

    2024年04月15日
    浏览(53)
  • zynq 使用AXI_dma 传输==pl到ps,linux驱动开发,应用层处理DMA数据

    在使用zynq输出处理时,会使用到pl和ps的数据传输,可供使用的方案有多种,由于我们的数据量较大打算,因此使用用以下两种方案处理: 1.使用pl直接写ddr3, 2.使用dma, 本次详细介绍使用axi_dma如何将pl的数据在linux应用层接收数据并处理,以及遇到的问题 fpga工程,我们使用

    2024年02月03日
    浏览(57)
  • zynq板上PS端给PL端复位

    最近接触zynq板-zcu104,记录一些实验笔记。 LED闪烁 在vivado中新建一个工程,命名为led-test。添加top.v文件。 其中,top模块接入zcu104开发板的clk_300_p/n信号,通过clock wizard转换为单端始终clock,接入到led闪烁模块led.v 其中,led模块通过一个24bit的计数器,经过相等间隔厚,让led灯

    2024年02月11日
    浏览(40)
  • zynq的PL向PS提供时钟和复位

    最近买了一块矿卡蚂蚁T9+,它的资源比EBAZ4205丰富。 需要矿卡资料包的朋友可以从这下载。里面包含蚂蚁T9+和EBAZ4205原理图和几个EBAZ4205例程,还有一些相关的pdf文档。 link 首先从fpga学起,可惜PL没有焊晶振,只好从PS端引,下面以点灯为例。 打开vivado,工具栏file -- project -

    2024年02月15日
    浏览(48)
  • FPGA - ZYNQ 基于EMIO的PS和PL交互

    Xilinx ZYNQ 系列的芯片,GPIO分为 MIO 、 EMIO 、 AXI_GPIO 三种方式。 MIO    :固定管脚,属于PS端,也就是ARM端。 EMIO   :通过PL扩展,使用时需要分配 PL ( FPGA ) 管脚,消耗PL端资源。 AXI_GPIO :封装好的IP核,PS通过M_AXI总线可以控制PL的IO接口,使用时,消耗管脚资源和逻辑资源。

    2024年04月26日
    浏览(37)
  • ZYNQ PS与PL通过AXI-LITE连接,在Linux下直接读写PL的物理地址,实现PS与PL的交互

    ZYNQ开发,如果PL与PS的交互方式仅为AXI-Lite总线的话,在Linux下可以通过直接访问PL的寄存器物理地址来实现PS-PL的数据交互。 测试代码的PC开发平台为Ubuntu18.04,QT5。 ZYNQ为7020,并移植了Linux系统和Ubuntu16.04的最小系统。 将PL的程序封装成IP核,通过AXI-LITE与PS连接,对外是18个寄

    2024年04月10日
    浏览(49)
  • PS和PL使用BRAM进行数据交互

    BRAM(Block RAM)是 PL 部分的存储器阵列,PS 和 PL 通过对 BRAM 进行读写操作,来实现数据的交互。在 PL 中,通过输出时钟、地址、读写控制等信号来对 BRAM 进行读写操作。而在 PS 中,处理器并不需要直接驱动 BRAM 的端口,而是通过 AXI BRAM 控制器来对 BRAM 进行读写操作。AXI BR

    2024年02月02日
    浏览(39)
  • FPGAC程序固化——ZYNQ7020

            刚开始学习FPGA的小伙伴们,想必都会遇见到一个问题就是程序固化。在FPGA开发过程中,我们不仅需要编写代码实现所需功能,还需要将这些代码固定在FPGA芯片上,以确保其稳定运行并符合项目需求。尽管此过程听起来可能有些复杂,但它却是使得FPGA成为如此强大工

    2024年02月21日
    浏览(36)
  • ZYNQ AC7020C的“点LED”实验

    一、创建 Vivado 工程 1、启动 Vivado 2、在 Vivado 开发环境里点击“Create New Project”,创建一个新的工程 3、弹出一个建立新工程的向导,点击“Next” 4、在弹出的对话框中输入工程名和工程存放的目录。需要注意工程路径“Project location”不能有中文、空格,路径名称也不能太长

    2024年02月20日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包