首先,中断相关的讲解和底层原理,详见另外一篇博客
ZYNQ PL触发PS中断函数_wangjie36的博客-CSDN博客_pl到ps中断
硬件平台:
一,问题描述和原因分析
问题描述:在单独的CPU0或者CPU1中接收中断,中断服务程序响应正常;但使用AMP模式,CPU0能接收中断触发,能够正常响应外设中断;若用以同样的初始化方式在CPU1中触发,CPU1是收不到外部中断触发的。
?原因分析:当使用openAMP双核模式的时候,编译器选项中添加的是“-DUSE_AMP=1”这个参数,那么在源码中的预编译指令是用“USE_AMP”定义的,主要影响到CPU1对缓存的操作。CPU1对全局时钟的操作。当定义了USE_AMP等于1时,编译的时候都是直接返回,不对CPU1外部中断进行配置。“-DUSE_AMP=1”编译选项将影响到工程代码里的USE_AMP预编译指令,使得Cache操作函数、全局时钟以及中断控制器SCUGIC的初始化函数不被编译进CPU1的应用程序中,避免可能会出现的CPU0和CPU1Cache一致性维护异常和中断异常等问题。
二,解决CPU1响应中断的办法
1,CPU1无法响应中断,需要将指定的中断号map到对应的CPU,使用关键函数才能使中断生效:XScuGic_InterruptMaptoCpu(intc,SOFT_INTR_ID_TO_CPU1,UART_INT_IRQ_ID);
XScuGic_InterruptMaptoCpu函数的底层操作
void XScuGic_InterruptMaptoCpu(XScuGic *InstancePtr, u8 Cpu_Id, u32 Int_Id)
{
u32 RegValue, Offset;
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
Offset = (Int_Id & 0x3U);
Cpu_Id = (0x1U << Cpu_Id);//根据寄存器说明设置触发的CPU号
RegValue = (RegValue & (~(0xFFU << (Offset*8U))) );
RegValue |= ((Cpu_Id) << (Offset*8U));
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id), RegValue);
//XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id)是通过中断号计算操作哪个ICDIPTR寄存器的
//公式宏定义
}
2,设置触发方式的时候,参数用寄存器地址触发,用XScGic_SetPrioityTriggerTypeByDistAddr函数能从cpu1触发。中断名称XScGic_SetPrioityTriggerType函数,有时候触发不到CPU1。
在设置硬中断时,设置中断的类型和优先级使用到的函数XScuGic_SetPriTrigTypeByDistAddr(DIST_BASE_ADDR, CPU0_HW_INT_ID, 0x20, 0x03); 其中第一个参数是,中断设置的基地址,ZYNQ的中断设置都有一个基地址,对各个中断号的中断的响应,可以通过中断号来进行偏移,除此之外还需要设置中断的优先级,中断的优先级是以8为递增的,也就是中断的优先级有0x00,0x08,0x10,0x18等等,优先级最低的是0xF8;中断的类型根据中断的类型不同可以设置为不同类型的中断,其中私有中断(PPI)默认为上升沿触发,软中断(SFI),共享中断(SPI)可以设置为电平中断和边沿中断。
3,具体代码
(1),CPU0
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xscugic.h"
#include "sleep.h"
// OCM
#define OCM3_ADDR 0xFFFF0000
#define GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define DIST_BASE_ADDR XPAR_PS7_SCUGIC_0_DIST_BASEADDR
//software interrupt ID is 0x00~0x0F
#define CPU0_SW_INT_ID 0x0D //CPU0的软中断ID,用于中断CPU0
#define CPU1_SW_INT_ID 0x0E //CPU1的软中断ID,用于中断CPU1
//PL 到 arm的中断
#define CPU0_HW_INT_ID 61 //CPU0的硬中断,1S计数器中断
#define CPU1_HW_INT_ID 62 //CPU1的硬中断,2S计数器中断
static XScuGic gicInst;
static XScuGic_Config * gicCfg_Ptr;
void hwIntrHandler(void * CallBackRef)
{
printf("CPU0 is interrupted by HW\n");
}
int initGic()
{
int status;
//1. 初始化异常处理系统
Xil_ExceptionInit();
//2. 初始化中断控制器
gicCfg_Ptr = XScuGic_LookupConfig(GIC_DEV_ID);
status = XScuGic_CfgInitialize(&gicInst, gicCfg_Ptr, gicCfg_Ptr->CpuBaseAddress);
if(status != XST_SUCCESS)
{
printf("initialize GIC failed\n");
return XST_FAILURE;
}
//3. 注册异常回调函数
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &gicInst);
//4. 连接GIC对应的硬中断ID
status = XScuGic_Connect(&gicInst, CPU0_HW_INT_ID, (Xil_InterruptHandler)hwIntrHandler, &gicInst);
if(status != XST_SUCCESS)
{
printf("Connect GIC failed\n");
return XST_FAILURE;
}
XScuGic_SetPriTrigTypeByDistAddr(DIST_BASE_ADDR, CPU0_HW_INT_ID, 0x20, 0x03);
//6. 将硬中断绑定到CPU0
XScuGic_InterruptMaptoCpu(&gicInst, 0x00, CPU0_HW_INT_ID);
//7. 使能硬中断
XScuGic_Enable(&gicInst, CPU0_HW_INT_ID);
//8. 使能异常处理系统
Xil_ExceptionEnable();
return status;
}
int main()
{
//初始化中断控制器
int status;
status = initGic();
if(status != XST_SUCCESS)
{
printf("initialize GIC failed\n");
return XST_FAILURE;
}
while(1)
{
sleep(1);
}
return 0;
}
(2),CPU1
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xscugic.h"
#include "sleep.h"
// on chip memory3 address
#define OCM3_ADDR 0xFFFF0000
#define GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define DIST_BASE_ADDR XPAR_PS7_SCUGIC_0_DIST_BASEADDR
//software interrupt ID is 0x00~0x0F
#define CPU0_SW_INT_ID 0x0D //CPU0的软中断ID,用于中断CPU0
#define CPU1_SW_INT_ID 0x0E //CPU1的软中断ID,用于中断CPU1
#define CPU0_HW_INT_ID 61 //CPU0的硬中断,1S计数器中断
#define CPU1_HW_INT_ID 62 //CPU1的硬中断,2S计数器中断
static XScuGic gicInst;
static XScuGic_Config * gicCfg_Ptr;
void hwIntrHandler(void * CallBackRef)
{
usleep(1000);
printf("CPU1 is interrupted by HW\n");
}
int initGic()
{
int status;
//1. 初始化异常处理系统
Xil_ExceptionInit();
//2. 初始化中断控制器
gicCfg_Ptr = XScuGic_LookupConfig(GIC_DEV_ID);
status = XScuGic_CfgInitialize(&gicInst, gicCfg_Ptr, gicCfg_Ptr->CpuBaseAddress);
if(status != XST_SUCCESS)
{
printf("initialize GIC failed\n");
return XST_FAILURE;
}
//3. 注册异常回调函数,中断类型的异常
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &gicInst);
//4. 连接GIC对应的硬中断ID
status = XScuGic_Connect(&gicInst, CPU1_HW_INT_ID, (Xil_InterruptHandler)hwIntrHandler, &gicInst);
if(status != XST_SUCCESS)
{
printf("Connect GIC failed\n");
return XST_FAILURE;
}
//5. 设置硬中断优先级和中断类型
XScuGic_SetPriTrigTypeByDistAddr(DIST_BASE_ADDR, CPU1_HW_INT_ID, 0x20, 0x03);
//6. 将硬中断绑定到CPU0(双使用双核时,需要对硬中断进行一次映射)
XScuGic_InterruptMaptoCpu(&gicInst, 0x01, CPU1_HW_INT_ID);
//7. 使能硬中断
XScuGic_Enable(&gicInst, CPU1_HW_INT_ID);
//8. 使能异常处理系统
Xil_ExceptionEnable();
return status;
}
int main()
{
//初始化中断控制器
int status;
status = initGic();
if(status != XST_SUCCESS)
{
printf("initialize GIC failed\n");
return XST_FAILURE;
}
while(1)
{
sleep(1);
}
return 0;
}
三,解决固化时候的CPU1中断不响应
问题描述:按照第二步解决后,仿真器仿真时所有程序在线运行一切正常,CPU1能正常响应外部接收中断。但是,在制BOOT.BIN文件固化进去以后,在FSBL文件当中添加CPU1启动函数,此时发现固化的程序无法正常运行,使用打断点的方式发现CPU1的外部中断又不能触发了。
解决办法:在startcpu1之间加入?cpu0_intr_init(&IntrruptController);函数
#define sev() __asm__("sev")
#define CPU1STARTADR 0xFFFFFFF0
#define CPU1STARTMEM 0x20000000
void StartCpu1(void)
{
printf("Write the address of the application for CPU1 to 0xFFFFFFF0\r\n");
Xil_Out32(CPU1STARTADR, CPU1STARTMEM);
dmb();
printf("Execute the SEV instruction to cause CPU1 to wake up\r\n");
sev();
}
int main()
{
Xil_SetTlbAttributes(0xFFFF0000,0x14de2);
cpu0_intr_init(&IntrruptController);
StartCpu1();
}
|