【JokerのZYNQ7020】INTERRUPT(PL产生,PS处理)。

软件环境:vivado 2017.4        硬件平台:XC7Z020 


 由图中可见,中断大体分为三类,包括SGI(软件中断)、PPI(私有中断)、SPI(共享中断)。

每个CPU均有16个SGI(软件中断),如下图。

 PPI(私有中断)中包含2个PL到CPU的快速中断,nFIQ,具体如下图。

 最后,SPI(共享中断)配置如下图,红圈处就是这次实验用的可由PL触发,PS处理的共享中断。

 


哔哔完了开始建工程,Vivado 2017.4 Create Block Design后,添加ZYNQ7 Processing system,在interrupt中勾PL-PS的IRQ_F2P[15:0]如图所示。

添加IP ------------> Vector(逻辑门),设置为not。添加IP------------>concat(链接)。

 如下图链接好后,记得添加IO管脚约束。

我是用PL侧的开关来触发产生的中断,IO约束如下。最后Generate the output products,Create a HDL wrapper,Generate Bitstream,Export Hardware(注意勾选include biestream),最后launch SDK进行测试。

set_property PACKAGE_PIN T19 [get_ports {SW1[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW1[0]}]

set_property PACKAGE_PIN R19 [get_ports {SW2[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW2[0]}]

SDK中new application然后添加如下测试代码。 

#include <stdio.h>
#include "xscugic.h"
#include "xil_exception.h"

#define INT_CFG0_OFFSET 0x00000C00

// Parameter definitions
#define SW1_INT_ID              61
#define SW2_INT_ID              62

#define INTC_DEVICE_ID          XPAR_PS7_SCUGIC_0_DEVICE_ID
#define INT_TYPE_RISING_EDGE    0x03
#define INT_TYPE_HIGHLEVEL      0x01
#define INT_TYPE_MASK           0x03

static XScuGic INTCInst;

static void SW_intr_Handler(void *param);
static int IntcInitFunction(u16 DeviceId);

static void SW_intr_Handler(void *param)
{
    int sw_id = (int)param;
    printf("SW%d int\n\r", sw_id);
}

void IntcTypeSetup(XScuGic *InstancePtr, int intId, int intType)
{
    int mask;

    intType &= INT_TYPE_MASK;
    mask = XScuGic_DistReadReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4);
    mask &= ~(INT_TYPE_MASK << (intId%16)*2);
    mask |= intType << ((intId%16)*2);
    XScuGic_DistWriteReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4, mask);
}

int IntcInitFunction(u16 DeviceId)
{
    XScuGic_Config *IntcConfig;
    int status;

    // Interrupt controller initialisation
    IntcConfig = XScuGic_LookupConfig(DeviceId);
    status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
    if(status != XST_SUCCESS) return XST_FAILURE;

    // Call to interrupt setup
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 &INTCInst);
    Xil_ExceptionEnable();

    // Connect SW1~SW3 interrupt to handler
    status = XScuGic_Connect(&INTCInst,
                             SW1_INT_ID,
                             (Xil_ExceptionHandler)SW_intr_Handler,
                             (void *)1);
    if(status != XST_SUCCESS) return XST_FAILURE;

    status = XScuGic_Connect(&INTCInst,
                             SW2_INT_ID,
                             (Xil_ExceptionHandler)SW_intr_Handler,
                             (void *)2);
    if(status != XST_SUCCESS) return XST_FAILURE;


    // Set interrupt type of SW1~SW3 to rising edge
    IntcTypeSetup(&INTCInst, SW1_INT_ID, INT_TYPE_RISING_EDGE);
    IntcTypeSetup(&INTCInst, SW2_INT_ID, INT_TYPE_RISING_EDGE);

    // Enable SW1~SW3 interrupts in the controller
    XScuGic_Enable(&INTCInst, SW1_INT_ID);
    XScuGic_Enable(&INTCInst, SW2_INT_ID);

    return XST_SUCCESS;
}

int main(void)
{
    print("PL int test\n\r");
    IntcInitFunction(INTC_DEVICE_ID);
    while(1);
    return 0;
}

接下来对程序中的一些函数做一些解释说明:

XScuGic结构体,中断配置初始化的一个接口,内部定义如下。

typedef struct
{
	XScuGic_Config *Config;  /**< Configuration table entry */
	u32 IsReady;		 /**< Device is initialized and ready */
	u32 UnhandledInterrupts; /**< Intc Statistics */
} XScuGic;

其中的XScuGic_Config,包括设备ID号,基址,向量表。

typedef struct
{
	u16 DeviceId;		/**< Unique ID  of device */
	u32 CpuBaseAddress;	/**< CPU Interface Register base address */
	u32 DistBaseAddress;	/**< Distributor Register base address */
	XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
				 Vector table of interrupt handlers */
} XScuGic_Config;

初始化老套路,一上来肯定先是XScuGic_LookupConfig()查查ID,看看设备在不在,在的话拿XScuGic_CfgInitialize找到配置寄存器基址。然后Xil_ExceptionRegisterHandler()是中断注册。其实说是中断注册,但是看介绍说的是,处理特定异常时用的。

void Xil_ExceptionRegisterHandler(u32 Exception_id,
				    Xil_ExceptionHandler Handler,
				    void *Data)
{
	XExc_VectorTable[Exception_id].Handler = Handler;
	XExc_VectorTable[Exception_id].Data = Data;
}

Xil_ExceptionRegisterHandler()初始化时候带的第二个参数XScuGic_InterruptHandler()实际上是主要的中断处理函数,必须将你配置的中断源与这个函数捆绑,否则无法将你配置的中断源在终端控制器中激活,因为这个函数主要解决的就是看哪个中断被使能了,处于激活状态,哪个中断有高优先级,中断处理函数是啥。个人认为,丢到XExc_VectorTable中,就是捆绑的过程。

void XScuGic_InterruptHandler(XScuGic *InstancePtr)
{

	u32 InterruptID;
	    u32 IntIDFull;
	    XScuGic_VectorTableEntry *TablePtr;

	    Xil_AssertVoid(InstancePtr != NULL);

	    IntIDFull = XScuGic_CPUReadReg(InstancePtr, XSCUGIC_INT_ACK_OFFSET);
	    InterruptID = IntIDFull & XSCUGIC_ACK_INTID_MASK;

	    if(XSCUGIC_MAX_NUM_INTR_INPUTS < InterruptID){
		goto IntrExit;
	    }

	    TablePtr = &(InstancePtr->Config->HandlerTable[InterruptID]);
		if(TablePtr != NULL) {
	        TablePtr->Handler(TablePtr->CallBackRef);
		}

	IntrExit:

	    XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_EOI_OFFSET, IntIDFull);

}

Xil_ExceptionEnable()就不多说了呀,注册完了使能呗。

#define Xil_ExceptionEnable() \
		Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ)

XScuGic_Connect()这个才是重点,所以干脆换了个颜色,作用是将中断Id、中断处理程序Handler和回调参数3者结合起来,当中断被触发时候,由中断号找到中断处理程序,并送个中断处理程序特定回调参数。

s32  XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
                      Xil_InterruptHandler Handler, void *CallBackRef)
{
	/*
	 * Assert the arguments
	 */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
	Xil_AssertNonvoid(Handler != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * The Int_Id is used as an index into the table to select the proper
	 * handler
	 */
	InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
	InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;

	return XST_SUCCESS;
}

XScuGic_Connect()中第三个参数,不论是Xil_ExceptionHandler类型还是Xil_InterruptHandler类型,可以看到,参数是少不了的。由中断函数SW_intr_Handler()可以看到,带的参数其实就是CallBackRef。

typedef void (*Xil_ExceptionHandler)(void *data);
typedef void (*Xil_InterruptHandler)(void *data);

IntcTypeSetup()函数用于设置中断类型,可以看到,其实是寄存器移位屏蔽或者移位置位操作。

void IntcTypeSetup(XScuGic *InstancePtr, int intId, int intType)
{
    int mask;

    intType &= INT_TYPE_MASK;
    mask = XScuGic_DistReadReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4);
    mask &= ~(INT_TYPE_MASK << (intId%16)*2);
    mask |= intType << ((intId%16)*2);
    XScuGic_DistWriteReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4, mask);
}

从最后一行才是真正写相关寄存器的操作,开XScuGic_DistWriteReg()函数可以看到。

#define XScuGic_DistWriteReg(InstancePtr, RegOffset, Data) \
(XScuGic_WriteReg(((InstancePtr)->Config->DistBaseAddress), (RegOffset), \
					((u32)(Data))))

当中断ID号是61的时候,被写的寄存器实际上是DistBaseAddress基址+RegOffset偏移量+(intId/16)*4=0xF8F01000+0x00000C00+0x0C=0xF8F01C0C

#define XPAR_PS7_SCUGIC_0_DEVICE_ID 0U
#define XPAR_PS7_SCUGIC_0_BASEADDR 0xF8F00100U
#define XPAR_PS7_SCUGIC_0_HIGHADDR 0xF8F001FFU
#define XPAR_PS7_SCUGIC_0_DIST_BASEADDR 0xF8F01000U

开手册可以看到,确实是配置中断触发的寄存器。

 至于mask怎么移的,移多少,你怂管,反正是通用函数,你只用知道1是高电平触发,3是上升沿触发就OJ8K了。

XScuGic_Enable()明摆着中断使能函数,使能中断ID号的中断。

void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{
	u32 Mask;

	/*
	 * Assert the arguments
	 */
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * The Int_Id is used to create the appropriate mask for the
	 * desired bit position. Int_Id currently limited to 0 - 31
	 */
	Mask = 0x00000001U << (Int_Id % 32U);

	/*
	 * Enable the selected interrupt source by setting the
	 * corresponding bit in the Enable Set register.
	 */
	XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +
						((Int_Id / 32U) * 4U), Mask);
}

所以在最后总结一下PL产生中断,PS处理中断,在SDK中大致的流程:

通过XGpioPs_LookupConfig函数,找到所设备的基址------------>

通过XGpioPs_CfgInitialize函数,初始化设备配置------------>

通过Xil_ExceptionRegisterHandler函数,进行中断异常注册------------>

通过Xil_ExceptionEnable函数,使能中断异常注册------------>

通过XScuGic_Connect函数,链接中断号、中断处理程序、回调参数------------>

通过IntcTypeSetup函数,设置中断触发模式------------>

通过XScuGic_Enable函数,中断使能。

 

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值