雙核運(yùn)行原理
ZYNQ是一種主從關(guān)系的AMP架構(gòu),通過(guò)松散耦合共享資源,允許兩個(gè)處理器同時(shí)運(yùn)行自己的操作系統(tǒng)或者裸機(jī)應(yīng)用程序,在各自運(yùn)行自己的程序或者系統(tǒng)的時(shí)候,可以通過(guò)共享內(nèi)存進(jìn)行雙核之間的交互。雙核啟動(dòng)中,cpu0完成系統(tǒng)的初始化,與cpu1進(jìn)行通信,讀寫共享內(nèi)存。
共享資源防止沖突
1. DDR的內(nèi)存使用,CPU0使用內(nèi)存地址為0x00100000-0x001fffff,CPU1的使用地址應(yīng)該避開這段地址,使用地址設(shè)為0x00200000-0x003fffff;
2. CPU1不使用L2內(nèi)存,僅僅CPU0使用;
3. CPU1從核心在PL的中斷路由到PPI控制器,通過(guò)使用PPI將中斷路由到核心,而CPU0通過(guò)ICD路由到核心;
4. 當(dāng)CPU0訪問(wèn)共享內(nèi)存的時(shí)候,CPU1不訪問(wèn),同理,CPU1訪問(wèn)共享內(nèi)存的時(shí)候,CPU0不訪問(wèn)。
雙核運(yùn)行的過(guò)程
ZYNQ是一個(gè)可擴(kuò)展平臺(tái),就是有FPGA作為外設(shè)的A9雙核處理器,它的啟動(dòng)流程與FPGA完全不同,而與傳統(tǒng)的ARM處理器類似,ZYNQ的啟動(dòng)配置需要多個(gè)處理步驟,通常情況,需要包含以下3個(gè)階段:
1. 階段1:在芯片上電運(yùn)行后,處理器自動(dòng)開始stage0-boot,就是片內(nèi)的BOORROM中的代碼,上電復(fù)位或者熱復(fù)位后,處理器執(zhí)行不可修改的代碼;
2. 階段2:BOORROM初始化CPU和一些外設(shè)后,讀取下一個(gè)階段所需的程序代碼FSBL(first stage boot loader),它是可以由用戶修改控制的代碼;
3. 階段3:這是用戶基于BSP(板級(jí)支持包),也可以是操作系統(tǒng)的啟動(dòng)引導(dǎo)程序,這個(gè)階段完全是在用戶的控制下實(shí)現(xiàn)的。
系統(tǒng)上電啟動(dòng)后,第0階段啟動(dòng)代碼判斷啟動(dòng)模式,將第一階段啟動(dòng)代碼FSBL下載到DDR中并且執(zhí)行。FSBL會(huì)配置硬件比特流文件,加載CPU0可執(zhí)行文件和CPU1可執(zhí)行文件到DDR對(duì)應(yīng)的鏈接地址,在這一階段,所有代碼在CPU0中執(zhí)行,在執(zhí)行CPU0程序的時(shí)候,把CPU1上將要執(zhí)行的應(yīng)用程序執(zhí)行地址寫入到OCM的0xFFFFFFF0地址,然后執(zhí)行 SEV匯編指令,激活CPU1,CPU1激活后,將會(huì)到OCM的0xFFFFFFF0地址讀取其數(shù)值,其數(shù)值就是CPU1執(zhí)行可執(zhí)行程序的地址,CPU1將從該地址執(zhí)行。
雙核運(yùn)行的配置
建立工程的區(qū)別
核0建立工程和以前一樣,但是核1建立工程與以前不同,需要單獨(dú)建立一個(gè)板級(jí)支持包bsp。建立CPU1的板級(jí)支持包步驟:
1. 在SDK主界面主菜單下,選擇File->New->Board Support Package;
2. 出現(xiàn)“New Board Support Package Project”對(duì)話框,如圖1所示;
圖1 新建cpu1板級(jí)支持包
點(diǎn)擊finish建立好支持包后,出現(xiàn)“Board Support Package Settings”對(duì)話框,在界面左側(cè)窗口中,展開Overview,在展開項(xiàng)中,找到并展開drivers,找到ps_cortexa9_1,并選擇它;
在右側(cè)的Configuration for OS界面中,找到名字為extra_compiler_flags一行,將其對(duì)應(yīng)的Value一列的值改為-g –DUSE_AMP_ = 1,如圖2所示;
圖2 板級(jí)開發(fā)包的屬性設(shè)置
建立好板級(jí)開發(fā)包后,建立cpu1的sdk工程,該工程的配置與和0也有不同,就是在新建工程對(duì)話框的參數(shù)配置要與核0不同,其核心選擇核心1,板級(jí)支持包選擇剛剛建立的cpu1的板級(jí)支持包(proccessor:ps7_cortexa9_1;Borad Support Package:app_cpu1_bsp),建立好雙核的應(yīng)用工程和板級(jí)開發(fā)包后,進(jìn)行軟件的設(shè)計(jì)。
軟件設(shè)計(jì)
1. cpu0的軟件,在fsbl啟動(dòng)cpu0程序后,其程序需要增加啟動(dòng)cpu1的流程代碼;
2. cpu0和cpu1的軟件需要有一片共享內(nèi)存,該內(nèi)存不能被cache化;
3. 通過(guò)共享內(nèi)存的分時(shí)訪問(wèn),設(shè)計(jì)兩個(gè)cpu的程序流程。
增加啟動(dòng)cpu1的代碼如下:
#define sev() __asm__("sev")
#define CPU1STARTADDR 0xFFFFFFF0
#define CPU1STARTMEM 0x10000000
void StartCpu1(void)
{
Xil_Out32(CPU1STARTADDR,CPU1STARTMEM);
dmb();
sev();
}
禁用共享內(nèi)存的代碼:
#define COMM_VAL (*(volatile unsigned long *)(0xffff0000))
Xil_SetTlbAttributes(0xffff0000,0x14de2);
雙核源碼與測(cè)試
利用共享內(nèi)存做通訊的例子
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;
}
測(cè)試結(jié)果:
將上述程序生成boot.bin文件,然后下載到flash,啟動(dòng)后通過(guò)串口助手可以收到cpu0與cpu1的打印信息,每間隔兩秒打印一次,如圖3所示。
圖3 程序測(cè)試
利用軟件中斷做通訊的例子
該例子中,cpu0和cpu1都注冊(cè)兩個(gè)軟件中斷,將1號(hào)軟件中斷注冊(cè)給cpu1,表示cpu0發(fā)送中斷給cpu1,將2號(hào)軟件中斷注冊(cè)給cpu0,表示cpu1發(fā)送中斷給cpu0;然后在程序運(yùn)行時(shí),cpu0觸發(fā)1號(hào)軟件中斷,此時(shí)cpu1正在運(yùn)行主程序被該中斷中斷,進(jìn)入中斷服務(wù)函數(shù),其處理完中斷后觸發(fā)2號(hào)軟件中斷,此時(shí)該中斷會(huì)中斷cpu0,進(jìn)入中斷服務(wù)函數(shù),cpu0處理完中斷后回到主函數(shù),再觸發(fā)1號(hào)軟件中斷,往復(fù)運(yùn)行。
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,啟動(dòng)后通過(guò)串口助手可以收到cpu0與cpu1的打印信息,每間隔兩秒打印一次,如圖4所示。
圖4 測(cè)試結(jié)果
編輯:hfy
-
Zynq
+關(guān)注
關(guān)注
9文章
608瀏覽量
47125
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論