[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中的中断主要包括三种类型,如下:
从上面可以看出三种终端类型包括:
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中断的整个注册流程如下,上面三种类型的中断都遵循下面的过程,只是稍许细节不一样,详细的情况可以观察我们下面给出的代码:
因为我们这篇文章主要讲解两个CPU之间的中断,那么关于PL侧的设计将没有什么重要设计,只需要例化相应的ZYNQ IP核,然后分配相应的DDR3即可。相应的Block Design如下:
这里因为要跑双核程序,那么就需要进行相应的设置,详细的设置可以观察博主的这篇文章基于ZYNQ的双核CPU之间的通信,注意这里的设置特别重要,否则ZYNQ的双核无法正常工作。
这里我们废话不多说直接给出CPU0的代码:
这里使用了13,14软中断
#include
#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的代码如下:
#include
#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");
}
这里特别提醒何为中断,中断的频率肯定不会太高,因为那样就不是中断了,当频率特别高的时候中断会因为响应不及时而出现错误。
我们将代码进行双核运行的必要控制,并且下板可以观察到如下现象:
从上面的实验结果我们可以看出我们的软中断实验成功执行,进而验证了我们实验的正确性。
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: