項目簡述
我們都知道ZYNQ中有兩個ARM核,但是如何使ZYNQ運行這兩個ARM核,以及雙核之間的數據如何進行交互是非常重要的問題。雙核CPU的運行方式主要有兩種:1、SMP 對稱處理器架構 ,2、 AMP 非對稱處理器架構,SMP結構雙核之間的關係比較密切,AMP架構雙核之間邏輯關係較小,開發難度也比較簡單。
工程描述:運行ZYNQ的兩個ARM核,同時利用OCM3進行雙核之間簡單的數據交互。
本次實驗所用到的軟硬件環境如下:
1、VIVADO 2019.1
2、米聯客MZ7015FA開發板
CPU0代碼
因爲該工程除了在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 <stdio.h>
#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代碼
上面對工程的原理已經進行了講解,這裏我們直接給出相應的CPU1的代碼,如下:
#include <stdio.h>
#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卡
我們學完了雙核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學院
總結
創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。或者對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣: