參考文獻
[1]、V3學院
[2]、 judyzhong
項目簡述
接觸過單片機、ARM的同學應該都瞭解中斷,因爲這是保證CPU實時性的前提。學到這裏的同學會不免發出疑惑,爲什麼FPGA沒事有實時性的要求呢,因爲FPGA本身就是併發執行,這也是其本身最重要的特性。ZYNQ中有兩個CPU,又分爲不同的中斷類型,將在下面的文章中一一介紹。如果學不會中斷,那麼ZYNQ的學習將沒有意義。
工程描述:運行ZYNQ的兩個ARM核,利用ZYNQ的軟中斷,用CPU0中斷CPU1,同時CPU1中斷CPU0。
本次實驗所用到的軟硬件環境如下:
1、VIVADO 2019.1
2、米聯客MZ7015FA開發板
ZYNQ中斷描述
ZYNQ中的中斷主要包括三種類型,如下:
從上面可以看出三種終端類型包括:
1.軟件中斷(SGI,Software generatedinterrupts,中斷號0-15)(16–26 reserved) :被路由到一個或者兩個CPU上,通過寫ICDSGIR寄存器產生SGI.
2.私有外設中斷(PPI,private peripheralinterrupts ,中斷號27-31):每個CPU都有一組PPI,包括全局定時器、私有看門狗定時器、私有定時器和來自PL的FIQ/IRQ.
3.共享外設中斷(SPI,shared peripheralinterrupts,中斷號32-95):由PS和PL上的各種I/O控制器和存儲器控制器產生,這些中斷信號被路由到相應的CPU. 其中共享中斷包括外設中斷和PL中斷
中斷控制器(GIC,generic interrupt controller ):用於集中管理從PS和PL產生的中斷信號的資源集合。控制器可以使能、關使能、屏蔽中斷源和改變中斷源的優先級,並且會將中斷送到對應的CPU中,CPU通過私有總線訪問這些寄存器。
PL和PS之間的中斷有:
兩個CPU都具有各自16個軟件中斷,CPU可以中斷自己,也可以中斷其他CPU,上升沿觸發,不可修改
CPU的私有中斷,這些中斷都是固定死的,不能修改:
共享中斷就是一些端口共用一箇中斷請求線, PL部分有16個共享中斷,他們的觸發方式可以設置
這些中斷的使用在接下來的文章中都會進行相應的講解,本篇文章我們主要講解軟中斷,軟中斷CPU可以中斷自己,也可以中斷其他CPU,上升沿觸發,不可修改。
ZYNQ中斷的整個註冊流程如下,上面三種類型的中斷都遵循下面的過程,只是稍許細節不一樣,詳細的情況可以觀察我們下面給出的代碼:
PL端設計
因爲我們這篇文章主要講解兩個CPU之間的中斷,那麼關於PL側的設計將沒有什麼重要設計,只需要例化相應的ZYNQ IP核,然後分配相應的DDR3即可。相應的Block Design如下:
PS端設計
這裏因爲要跑雙核程序,那麼就需要進行相應的設置,詳細的設置可以觀察博主的這篇文章基於ZYNQ的雙核CPU之間的通信,注意這裏的設置特別重要,否則ZYNQ的雙核無法正常工作。
CPU0代碼
這裏我們廢話不多說直接給出CPU0的代碼:
這裏使用了13,14軟中斷
#include <stdio.h>
#include "xscugic.h"
#include "xparameters.h"
#include "sleep.h"
#define GIC_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define CPU0_SW_INTR 0x0d
#define CPU1_SW_INTR 0x0e
static XScuGic ScuGic;
static XScuGic_Config * ScuGicCfgPtr;
//initial gic & software intr
int initSwIntr();
//callback func
void cpu0IntrHandler(void * callbackref);
int main()
{
int status;
status = initSwIntr();
if(status != XST_SUCCESS){
return status;
}
while(1){
usleep(100000);
XScuGic_SoftwareIntr(&ScuGic,CPU1_SW_INTR,XSCUGIC_SPI_CPU1_MASK);
}
return 0;
}
int initSwIntr(){
int status;
Xil_ExceptionInit();
ScuGicCfgPtr = XScuGic_LookupConfig(GIC_ID);
status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
if(status != XST_SUCCESS){
return status;
}
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
status = XScuGic_Connect(&ScuGic,CPU0_SW_INTR,(Xil_ExceptionHandler)cpu0IntrHandler,&ScuGic);
if(status != XST_SUCCESS){
return status;
}
XScuGic_Enable(&ScuGic,CPU0_SW_INTR);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
void cpu0IntrHandler(void * callbackref){
printf("cpu1 interrupt cpu0 success! \n\r");
}
這裏需要特別注意軟中斷的觸發函數:
第二個參數是軟中斷編號,第三個參數是CPU編號。
CPU1代碼
CPU1的代碼如下:
#include <stdio.h>
#include "xscugic.h"
#include "xparameters.h"
#include "sleep.h"
#define GIC_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define CPU0_SW_INTR 0x0d
#define CPU1_SW_INTR 0x0e
static XScuGic ScuGic;
static XScuGic_Config * ScuGicCfgPtr;
//initial gic & software intr
int initSwIntr();
//callback func
void cpu1IntrHandler(void * callbackref);
int main()
{
int status;
status = initSwIntr();
if(status != XST_SUCCESS){
return status;
}
while(1){
XScuGic_SoftwareIntr(&ScuGic,CPU0_SW_INTR,XSCUGIC_SPI_CPU0_MASK);
usleep(100000);
}
return 0;
}
int initSwIntr(){
int status;
Xil_ExceptionInit();
ScuGicCfgPtr = XScuGic_LookupConfig(GIC_ID);
status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
if(status != XST_SUCCESS){
return status;
}
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
status = XScuGic_Connect(&ScuGic,CPU1_SW_INTR,(Xil_ExceptionHandler)cpu1IntrHandler,&ScuGic);
if(status != XST_SUCCESS){
return status;
}
XScuGic_Enable(&ScuGic,CPU1_SW_INTR);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
void cpu1IntrHandler(void * callbackref){
printf("cpu0 interrupt cpu1 success! \n\r");
}
這裏特別提醒何爲中斷,中斷的頻率肯定不會太高,因爲那樣就不是中斷了,當頻率特別高的時候中斷會因爲響應不及時而出現錯誤。
測試結果
我們將代碼進行雙核運行的必要控制,並且下板可以觀察到如下現象:
從上面的實驗結果我們可以看出我們的軟中斷實驗成功執行,進而驗證了我們實驗的正確性。
總結
創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。或者對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣: