我们都知道ZYNQ中有两个ARM核,但是如何使ZYNQ运行这两个ARM核,以及双核之间的数据如何进行交互是非常重要的问题。双核CPU的运行方式主要有两种:1、SMP 对称处理器架构 ,2、 AMP 非对称处理器架构,SMP结构双核之间的关系比较密切,AMP架构双核之间逻辑关系较小,开发难度也比较简单。
工程描述:运行ZYNQ的两个ARM核,同时利用OCM3进行双核之间简单的数据交互。
本次实验所用到的软硬件环境如下:
1、VIVADO 2019.1
2、米联客MZ7015FA开发板
因为该工程除了在Block Design侧例化一个ZYNQ的IP,其他不需要做任何处理,所以我们将不再进行PL侧的讲解。直接对PS侧的代码进行讲解。PS端地址的分布情况如下:
在没有做其他设置的情况下ZYNQ上电后地址空间分别如上图所示。OCM共256KB按照64KB分为4块,其中前三块在SDK中表述为RAM0占192KB处于地址空间的最开头和DDR共用地址空间,最后一块64KB处于地址空间的最后。ZYNQ的DDR固定占地址空间的最开头1GB字节因而ZYNQ的DDR最大容量就只有1GB。为了避开OCM从上图可知实际使用的DDR只有1023MB(最开头的1MB被保留避开OCM的前三块)。从0x40000000到0xDFFFFFFF的2GB空间留给了自定义IP或者其他IP的寄存器,从BSP的xparameters.h可以看出在PL部分添加的IP其基址都是从0x40000000开始的,而ZYNQ自己的寄存器则从0xE0000000开始编制,具体寄存器内容请查阅UG585的附录B Register Details。其实Standalone作为基础的BSP所作的工作都是在通过指针访问各个寄存器而已,在不考虑安全性的前提下可以完全不用BSP直接操作寄存器对ZYNQ进行操作。所以我们一般使用OCM3作为CPU0与CPU1的共享内存使用。
我们这里给出相应的CPU0的代码;
#include
#include "xil_mmu.h"
#include "xil_printf.h"
#define COMM_VAL (*(volatile unsigned long *)(0xffff0000)) //OCM3
int main()
{
COMM_VAL =0;
//disable cache on chip mem
Xil_SetTlbAttributes(0xffff0000,0x14de2);
while(1){
print("Hello World CPU0 \n\r");
COMM_VAL =1;
while(COMM_VAL ==1){
}
}
return 0;
}
我们上面的代码唯一看不懂的就是下面这段话:
这段程序其实就是CPU0中禁止OCM的Cache属性,以为内Cache功能会向OCM3中填入一些缓存数据进而让OCM3中的数据进入不可控的状态。
上面对工程的原理已经进行了讲解,这里我们直接给出相应的CPU1的代码,如下:
#include
#include "xil_mmu.h"
#include "xil_printf.h"
#include "sleep.h"
#define COMM_VAL (*(volatile unsigned long *)(0xffff0000)) //OCM3
int main()
{
//disable cache on chip mem
Xil_SetTlbAttributes(0xffff0000,0x14de2);
while(1){
while(COMM_VAL ==0){
}
print("Hello World CPU1 \n\r");
usleep(100);
COMM_VAL =0;
}
return 0;
}
但是想要CPU1的代码成功运行起来,我们需要在CPU1的bsp文件中添加如下代码:创建CPU1的应用程序时需要设置CPU1的BSP SETTING extra-compel-flags 设置 -DUSE_AMP=1。
因为是AMP架构,所以得编辑lscript.ld文件,使得CPU0与CPU1的内存区间不相互重叠,如下;
CPU0修改:
CPU1修改:
在进行下板运行的过程中也需要进行相应的设置如下:
进行完上面的配置,我们便可以下载相应的代码到ZYNQ中,并且运行结果如下:
我们学完了双核CPU如何跑相应的程序,那么也需要进行学习如何固化相应的程序到达SD卡中。
在进行生成的FSBL的main文件中添加如下代码,该代码的意义是启动CPU1的代码:
#define sev() __asm__("sev")
#define CPU1STARTADR 0xFFFFFFF0
#define CPU1STARTMEM 0x02000000
void StartCpu1(void)
{
#if 1
fsbl_printf(DEBUG_GENERAL,"FSBL: Write the address of the application for CPU 1 to 0xFFFFFFF0\n\r");
Xil_Out32(CPU1STARTADR, CPU1STARTMEM);
dmb(); //waits until write has finished
fsbl_printf(DEBUG_GENERAL,"FSBL: Execute the SEV instruction to cause CPU 1 to wake up and jump to the application\n\r");
sev();
#endif
}
找到 Load boot image 的位置,把 CPU1 的启动函数,在此调用:
/*
* Load boot image
*/
HandoffAddress = LoadBootImage();
fsbl_printf(DEBUG_INFO,"Handoff Address: 0x%08lx\r\n",HandoffAddress);
StartCpu1(); /*add starting cpu1*/
然后创建 UBOOT.BIN
最后将生成的bin文件放到SD卡中即可。
[1]、V3学院
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: