雙核運行原理
ZYNQ是一種主從關系的AMP架構,通過松散耦合共享資源,允許兩個處理器同時運行自己的操作系統或者裸機應用程序,在各自運行自己的程序或者系統的時候,可以通過共享內存進行雙核之間的交互。雙核啟動中,cpu0完成系統的初始化,與cpu1進行通信,讀寫共享內存。
共享資源防止沖突
1. DDR的內存使用,CPU0使用內存地址為0x00100000-0x001fffff,CPU1的使用地址應該避開這段地址,使用地址設為0x00200000-0x003fffff;
2. CPU1不使用L2內存,僅僅CPU0使用;
3. CPU1從核心在PL的中斷路由到PPI控制器,通過使用PPI將中斷路由到核心,而CPU0通過ICD路由到核心;
4. 當CPU0訪問共享內存的時候,CPU1不訪問,同理,CPU1訪問共享內存的時候,CPU0不訪問。
雙核運行的過程
ZYNQ是一個可擴展平臺,就是有FPGA作為外設的A9雙核處理器,它的啟動流程與FPGA完全不同,而與傳統的ARM處理器類似,ZYNQ的啟動配置需要多個處理步驟,通常情況,需要包含以下3個階段:
1. 階段1:在芯片上電運行后,處理器自動開始stage0-boot,就是片內的BOORROM中的代碼,上電復位或者熱復位后,處理器執行不可修改的代碼;
2. 階段2:BOORROM初始化CPU和一些外設后,讀取下一個階段所需的程序代碼FSBL(first stage boot loader),它是可以由用戶修改控制的代碼;
3. 階段3:這是用戶基于BSP(板級支持包),也可以是操作系統的啟動引導程序,這個階段完全是在用戶的控制下實現的。
系統上電啟動后,第0階段啟動代碼判斷啟動模式,將第一階段啟動代碼FSBL下載到DDR中并且執行。FSBL會配置硬件比特流文件,加載CPU0可執行文件和CPU1可執行文件到DDR對應的鏈接地址,在這一階段,所有代碼在CPU0中執行,在執行CPU0程序的時候,把CPU1上將要執行的應用程序執行地址寫入到OCM的0xFFFFFFF0地址,然后執行 SEV匯編指令,激活CPU1,CPU1激活后,將會到OCM的0xFFFFFFF0地址讀取其數值,其數值就是CPU1執行可執行程序的地址,CPU1將從該地址執行。
雙核運行的配置
建立工程的區別
核0建立工程和以前一樣,但是核1建立工程與以前不同,需要單獨建立一個板級支持包bsp。建立CPU1的板級支持包步驟:
1. 在SDK主界面主菜單下,選擇File->New->Board Support Package;
2. 出現“New Board Support Package Project”對話框,如圖1所示;
圖1 新建cpu1板級支持包
點擊finish建立好支持包后,出現“Board Support Package Settings”對話框,在界面左側窗口中,展開Overview,在展開項中,找到并展開drivers,找到ps_cortexa9_1,并選擇它;
在右側的Configuration for OS界面中,找到名字為extra_compiler_flags一行,將其對應的Value一列的值改為-g –DUSE_AMP_ = 1,如圖2所示;
圖2 板級開發包的屬性設置
建立好板級開發包后,建立cpu1的sdk工程,該工程的配置與和0也有不同,就是在新建工程對話框的參數配置要與核0不同,其核心選擇核心1,板級支持包選擇剛剛建立的cpu1的板級支持包(proccessor:ps7_cortexa9_1;Borad Support Package:app_cpu1_bsp),建立好雙核的應用工程和板級開發包后,進行軟件的設計。
軟件設計
1. cpu0的軟件,在fsbl啟動cpu0程序后,其程序需要增加啟動cpu1的流程代碼;
2. cpu0和cpu1的軟件需要有一片共享內存,該內存不能被cache化;
3. 通過共享內存的分時訪問,設計兩個cpu的程序流程。
增加啟動cpu1的代碼如下:
#define sev() __asm__("sev")
#define CPU1STARTADDR 0xFFFFFFF0
#define CPU1STARTMEM 0x10000000
void StartCpu1(void)
{
Xil_Out32(CPU1STARTADDR,CPU1STARTMEM);
dmb();
sev();
}
禁用共享內存的代碼:
#define COMM_VAL (*(volatile unsigned long *)(0xffff0000))
Xil_SetTlbAttributes(0xffff0000,0x14de2);
雙核源碼與測試
利用共享內存做通訊的例子
Cpu0代碼:
#include "stdio.h"
#include "xil_io.h"
#include "xil_mmu.h"
#include "xil_exception.h"
#include "xpseudo_asm.h"
#include "sleep.h"
#define COMM_VAL (*(volatile unsigned long *)(0xffff0000))
#define sev() __asm__("sev")
#define CPU1STARTADDR 0xFFFFFFF0
#define CPU1STARTMEM 0x10000000
void StartCpu1(void)
{
Xil_Out32(CPU1STARTADDR,CPU1STARTMEM);
dmb();
sev();
}
int main(void)
{
Xil_SetTlbAttributes(0xffff0000,0x14de2);
StartCpu1();
COMM_VAL = 0;
while(1)
{
print("CPU0:hello world CPU0/r/n");
sleep(2);
COMM_VAL = 1;
while(COMM_VAL == 1);
}
return 0;
}
Cpu1代碼:
#include "stdio.h"
#include "xil_io.h"
#include "xil_mmu.h"
#include "xil_exception.h"
#include "xpseudo_asm.h"
#include "xparameters.h"
#include "sleep.h"
#define COMM_VAL (*(volatile unsigned long *)(0xffff0000))
int main(void)
{
Xil_SetTlbAttributes(0xffff0000,0x14de2);
while(1)
{
while(COMM_VAL == 0);
print("CPU1:hello world CPU1/r/n");
sleep(2);
COMM_VAL = 0;
}
return 0;
}
測試結果:
將上述程序生成boot.bin文件,然后下載到flash,啟動后通過串口助手可以收到cpu0與cpu1的打印信息,每間隔兩秒打印一次,如圖3所示。
圖3 程序測試
利用軟件中斷做通訊的例子
該例子中,cpu0和cpu1都注冊兩個軟件中斷,將1號軟件中斷注冊給cpu1,表示cpu0發送中斷給cpu1,將2號軟件中斷注冊給cpu0,表示cpu1發送中斷給cpu0;然后在程序運行時,cpu0觸發1號軟件中斷,此時cpu1正在運行主程序被該中斷中斷,進入中斷服務函數,其處理完中斷后觸發2號軟件中斷,此時該中斷會中斷cpu0,進入中斷服務函數,cpu0處理完中斷后回到主函數,再觸發1號軟件中斷,往復運行。
Cpu0代碼:
/*
* app_cpu0.c
*
* Created on: 2019年3月27日
* Author: dz
*/
#include "xil_cache.h"
#include "xparameters.h"
#include "xil_mmu.h"
#include "xil_misc_psreset_api.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "xuartps.h"
#include "stdio.h"
#include "sleep.h"
volatile u8 software_intr_received = 0;
#define CORE0_TO_CORE1_INTR_ID 0x01
#define CORE1_TO_CORE0_INTR_ID 0x02
void sys_intr_init(void);
void Setup_Intr_Exception(XScuGic * IntcInstancePtr);
void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID);
void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId);
void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId);
void Cpu0_Intr_Hanedler(void *Callback);
void Cpu1_Intr_Hanedler(void *Callback);
XScuGic ScuGic;
void delay(unsigned int count)
{
int i = 0;
for(i = 0;i
}
int main(void)
{
sys_intr_init();
xil_printf("cpu0 start!/r/n");
while(1)
{
if(XPAR_CPU_ID == 0)
Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU1_MASK);
/*Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU0_MASK);*/
else
Gen_Software_Intr(&ScuGic, CORE1_TO_CORE0_INTR_ID, XSCUGIC_SPI_CPU0_MASK);
delay(200000000);
if(1 == software_intr_received)
{
xil_printf("cpu0_main/r/n");
software_intr_received = 0;
}
}
return 0;
}
void sys_intr_init(void)
{ Scu_Intr_Init(&ScuGic,XPAR_SCUGIC_SINGLE_DEVICE_ID);
// if(XPAR_CPU_ID == 0)
Init_Software_Intr(&ScuGic, Cpu0_Intr_Hanedler, CORE0_TO_CORE1_INTR_ID, 0);
// else
Init_Software_Intr(&ScuGic, Cpu1_Intr_Hanedler, CORE1_TO_CORE0_INTR_ID, 1);
Setup_Intr_Exception(&ScuGic);
}
void Setup_Intr_Exception(XScuGic * IntcInstancePtr)
{
//connect hardware interrupt
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr);
//enable hardware interrupt
Xil_ExceptionEnable();
}
void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID)
{
XScuGic_Config *ScuConfigPtr;
//find device
ScuConfigPtr = XScuGic_LookupConfig(Scu_Int_ID);
//config scuint
XScuGic_CfgInitialize(IntancePtr, ScuConfigPtr,ScuConfigPtr->CpuBaseAddress);
}
void Cpu0_Intr_Hanedler(void *Callback)
{
xil_printf("receive interrupt from core1!/r/n");
software_intr_received = 1;
}
void Cpu1_Intr_Hanedler(void *Callback)
{
xil_printf("receive interrupt from core0!/r/n");
software_intr_received = 1;
}
void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId)
{
int Status;
// XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xB0, 0x2);
XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xd0, 0x3);
Status = XScuGic_Connect(GicInstancePtr, SoftwareIntrId,
(Xil_InterruptHandler)IntrHanedler, NULL);
if (Status != XST_SUCCESS) {
xil_printf("core%d: interrupt %d set fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);
return;
}
XScuGic_InterruptMaptoCpu(GicInstancePtr, CpuId, SoftwareIntrId); //map the the Software interrupt to target CPU
XScuGic_Enable(GicInstancePtr, SoftwareIntrId);//enable the interrupt for the Software interrupt at GIC
}
void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId)
{
int Status;
Status = XScuGic_SoftwareIntr(GicInstancePtr, SoftwareIntrId, CpuId);
if (Status != XST_SUCCESS) {
xil_printf("core%d: interrupt %d gen fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);
return;
}
}
Cpu1代碼:
/*
* app_cpu0.c
*
* Created on: 2019年3月27日
* Author: dz
*/
#include "xil_cache.h"
#include "xparameters.h"
#include "xil_mmu.h"
#include "xil_misc_psreset_api.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "xuartps.h"
#include "stdio.h"
#include "sleep.h"
volatile u8 software_intr_received = 0;
#define CORE0_TO_CORE1_INTR_ID 0x01
#define CORE1_TO_CORE0_INTR_ID 0x02
void sys_intr_init(void);
void Setup_Intr_Exception(XScuGic * IntcInstancePtr);
void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID);
void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId);
void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId);
void Cpu0_Intr_Hanedler(void *Callback);
void Cpu1_Intr_Hanedler(void *Callback);
XScuGic ScuGic;
void delay(unsigned int count)
{
int i = 0;
for(i = 0;i
;
}
int main(void)
{
sys_intr_init();
// Gen_Software_Intr(&ScuGic, CORE1_TO_CORE0_INTR_ID, XSCUGIC_SPI_CPU1_MASK);
xil_printf("cpu1 start!/r/n");
while(1)
{
if(1 == software_intr_received)
{
software_intr_received = 0;
if(XPAR_CPU_ID == 0)
Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU1_MASK);
/* Gen_Software_Intr(&ScuGic, CORE0_TO_CORE1_INTR_ID, XSCUGIC_SPI_CPU0_MASK);*/
else
Gen_Software_Intr(&ScuGic, CORE1_TO_CORE0_INTR_ID, XSCUGIC_SPI_CPU0_MASK);
delay(200000000);
xil_printf("cpu1_main/r/n");
}
}
return 0;
}
void sys_intr_init(void)
{ Scu_Intr_Init(&ScuGic,XPAR_SCUGIC_SINGLE_DEVICE_ID);
// if(XPAR_CPU_ID == 0)
Init_Software_Intr(&ScuGic, Cpu0_Intr_Hanedler, CORE0_TO_CORE1_INTR_ID, 0);
// else
Init_Software_Intr(&ScuGic, Cpu1_Intr_Hanedler, CORE1_TO_CORE0_INTR_ID, 1);
Setup_Intr_Exception(&ScuGic);
}
void Setup_Intr_Exception(XScuGic * IntcInstancePtr)
{
//connect hardware interrupt
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr);
//enable hardware interrupt
Xil_ExceptionEnable();
}
void Scu_Intr_Init(XScuGic* IntancePtr,int Scu_Int_ID)
{
XScuGic_Config *ScuConfigPtr;
//find device
ScuConfigPtr = XScuGic_LookupConfig(Scu_Int_ID);
//config scuint
XScuGic_CfgInitialize(IntancePtr, ScuConfigPtr,ScuConfigPtr->CpuBaseAddress);
}
void Cpu0_Intr_Hanedler(void *Callback)
{
xil_printf("receive interrupt from core1!/r/n");
software_intr_received = 1;
}
void Cpu1_Intr_Hanedler(void *Callback)
{
xil_printf("receive interrupt from core0!/r/n");
software_intr_received = 1;
}
void Init_Software_Intr(XScuGic *GicInstancePtr, Xil_InterruptHandler IntrHanedler, u16 SoftwareIntrId, u8 CpuId)
{
int Status;
XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xB0, 0x2);
// XScuGic_SetPriorityTriggerType(GicInstancePtr, SoftwareIntrId, 0xd0, 0x3);
Status = XScuGic_Connect(GicInstancePtr, SoftwareIntrId,
(Xil_InterruptHandler)IntrHanedler, NULL);
if (Status != XST_SUCCESS) {
xil_printf("core%d: interrupt %d set fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);
return;
}
XScuGic_InterruptMaptoCpu(GicInstancePtr, CpuId, SoftwareIntrId); //map the the Software interrupt to target CPU
XScuGic_Enable(GicInstancePtr, SoftwareIntrId);//enable the interrupt for the Software interrupt at GIC
}
void Gen_Software_Intr(XScuGic *GicInstancePtr, u16 SoftwareIntrId, u32 CpuId)
{
int Status;
Status = XScuGic_SoftwareIntr(GicInstancePtr, SoftwareIntrId, CpuId);
if (Status != XST_SUCCESS) {
xil_printf("core%d: interrupt %d gen fail!/r/n", XPAR_CPU_ID, SoftwareIntrId);
return;
}
}
將上述程序生成boot.bin文件,然后下載到flash,啟動后通過串口助手可以收到cpu0與cpu1的打印信息,每間隔兩秒打印一次,如圖4所示。
圖4 測試結果
編輯:hfy
-
Zynq
+關注
關注
10文章
610瀏覽量
47296
發布評論請先 登錄
相關推薦
評論