1. 程式人生 > >Zynq系列FPGA雙核ARM裸機非同步程式實現

Zynq系列FPGA雙核ARM裸機非同步程式實現

大門牙原創,歡迎隨意轉載,批評,引用。

一. 兩個處理器之間的關係

Zenq系列的兩個ARM處理器核心可以分別工作,其公用資源有cache、DDR、所有外設、OCM儲存器等。
可以將其看成是兩個執行緒,實際使用時,可以通過執行緒同步、互斥的方式來實現裸機情況下的簡單雙核排程與使用。

二. 實現方法

準備工作

由於兩個核心的記憶體空間是公用的,硬體上不作區分,也就是說每個核心都可以訪問到完整的DDR空間。
因此,需要在邏輯上將兩個核心使用的記憶體空間區分開來。例如
CPU DDR
CPU0 0 ~ 200MB
CPU1 200M ~ 512MB

開發時,需要建立3個application工程
1. FSBL
2. CPU0_app
3. CPU1_app

BootLoader的改動

對於ARM架構而言,不區分程式空間和資料空間,因此程式載入時也應該載入到各自對應的空間中。對於BootLoader而言,需要完成4件事情。
1. 晶片自檢,載入驅動
2. 載入bit stream,並通過VCAP模組寫入FPGA
3. 載入CPU0的程式和CPU1的程式
4. 將CPU0 hand off 到CPU0的使用者程式上

Linker Script的改動

對於CPU0和CPU1的工程而言,需要將其生成的ELF可執行檔案的載入地址改為各自空間的首地址。通過修改linker script來實現。例如 

CPU0 Linker Script

/* Define Memories in
the system */ MEMORY { ps7_ddr_0_S_AXI_BASEADDR : ORIGIN = 0x00100000, LENGTH = 0x9500000 ps7_ram_0_S_AXI_BASEADDR : ORIGIN = 0x00000000, LENGTH = 0x00030000 ps7_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0x0000FE00 }

CPU1 Linker Script

/* Define Memories in the system */
MEMORY
{
   ps7_ddr_0_S_AXI_BASEADDR : ORIGIN = 0x09600000, LENGTH = 0x03200000
ps7_ram_0_S_AXI_BASEADDR : ORIGIN = 0x00000000, LENGTH = 0x00030000 ps7_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0x0000FE00 }

上面的例子中
將CPU0的記憶體基地址改為1MB,記憶體大小改為149MB,即CPU0的邏輯記憶體空間為1MB~150MB
將CPU1的記憶體基地址改為150MB,記憶體大小改為50MB,即CPU1的邏輯記憶體空間為150MB~200MB
系統實際記憶體空間為512MB,剩餘的312MB留給FPGA使用

CPU1的啟動

Zenq系列的雙核ARM必須由CPU0先啟動,再通過CPU0來啟動CPU1。CPU0的程式需要將CPU1的程式入口地址設定好,再啟動CPU1。實現程式碼如下

#define sev()   __asm__("sev");
#define CPU1_START_ADDR                 (0xFFFFFFF0)
#define SHARE_RAM_BASE_ADDR             (0xFFFF0000)
Xil_SetTlbAttributes(SHARE_RAM_BASE_ADDR, 0x14DE2);
Xil_Out32(CPU1_START_ADDR, 0x09600000);
dmb();
sev();

在彙編指令sev執行以後,CPU1即開始從0xFFFFFFF0存放的地址處開始執行。
需要注意的是,由於cache是共享資源,因此兩塊CPU不能同時使用。在程式碼中,只能有一個CPU程式碼執行

    Xil_ICacheEnable();
    Xil_DCacheEnable();

在OCM區構建雙核控制塊

由於OCM記憶體為兩塊CPU公用,且訪問速度較快,可以將OCM記憶體區看作類似於IPC的“共享記憶體”,用於兩塊CPU程式之間的同步和互斥等。例如

struct DUAL_CORE_CTRL_STRU {
    /*  控制訊號量       */
    u8 sig_CalcDone;  // 同步訊號量
    u8 mutex_Printf;  // 互斥鎖
    u8 spin_SD_Card;  // 自旋鎖
}

然後在CPU0和CPU1的程式中分別應用這塊記憶體區域

#define SHARE_RAM_BASE_ADDR             (0xFFFF0000)
struct DUAL_CORE_CTRL_STRU *stDualCtrl =
        (struct DUAL_CORE_CTRL_STRU*) SHARE_RAM_BASE_ADDR;

具體的訊號量處理介面不再贅述,請自己造輪子 : )