ZYNQ PL 中断PS测试 以及重复中断问题的解决

在测试PL中断PS的过程中,本以为只要复制下代码就能顺利测出,结果在测试中断的过程中,总是会有重复中断的事情发生,郁闷至极,折腾了一天才找到问题。
ZYNQ PL 中断PS测试 以及重复中断问题的解决_第1张图片
ps 部分框图搭建如上, pl_led_4这里不会用到,我们主要是为了测试中断,因此 只有 输入 IRQ_F2P[3:0]会有作用,分别代表4个中断输入从PL过来的。

那么PL处的测试代码如下:

wire [3:0]  IRQ_F2P;
reg [31:0]temp = 0;
always @(posedge broad_clk)    
begin
    if(temp > 20000000 + 500 )
    begin
        temp <= 0;
    end
    else
    begin
        temp <= temp + 1;
    end
end
            //4个中断测试
assign    IRQ_F2P[0] = (temp >= 5000000 ) && (temp < 5000000  + 70);
assign    IRQ_F2P[1] = (temp >= 10000000) && (temp < 10000000 + 70);
assign    IRQ_F2P[2] = (temp >= 15000000) && (temp < 15000000 + 70);
assign    IRQ_F2P[3] = (temp >= 20000000) && (temp < 20000000 + 70);

cpu_wrapper u_cpu_wrapper (
        .DDR_addr(DDR_addr),
        .DDR_ba(DDR_ba),
        .DDR_cas_n(DDR_cas_n),
        .DDR_ck_n(DDR_ck_n),
        .DDR_ck_p(DDR_ck_p),
        .DDR_cke(DDR_cke),
        .DDR_cs_n(DDR_cs_n),
        .DDR_dm(DDR_dm),
        .DDR_dq(DDR_dq),
        .DDR_dqs_n(DDR_dqs_n),
        .DDR_dqs_p(DDR_dqs_p),
        .DDR_odt(DDR_odt),
        .DDR_ras_n(DDR_ras_n),
        .DDR_reset_n(DDR_reset_n),
        .DDR_we_n(DDR_we_n),
        .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
        .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
        .FIXED_IO_mio(FIXED_IO_mio),
        .FIXED_IO_ps_clk(FIXED_IO_ps_clk),
        .FIXED_IO_ps_porb(FIXED_IO_ps_porb),
        .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),
        .pl_led_4_tri_o(pl_led_4_tri_o[3:0]),
        .IRQ_F2P(IRQ_F2P[0])
);    

以上例子很简单,就是产生4个中断激励,依次触发四个中断。(上面代码有一个坑,看能不能看出来,后面在做解答)

然后添加软件部分代码:

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


#define INT_CFG0_OFFSET 0x00000C00

#define SW1_INT_ID            61
#define SW2_INT_ID            62
#define SW3_INT_ID            63
#define SW4_INT_ID            64

#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

XScuGic INTCInst;

static void SW_intr_Handler1(void *param){
	XScuGic_Disable(&INTCInst, SW1_INT_ID);
	int sw_id = (int)param;
	printf("SW1 int  ");
	XScuGic_Enable(&INTCInst, SW1_INT_ID);
}

static void SW_intr_Handler2(void *param){
	XScuGic_Disable(&INTCInst, SW2_INT_ID);
	int sw_id = (int)param;
	printf("SW2 int  ");
	XScuGic_Enable(&INTCInst, SW2_INT_ID);

}


static void SW_intr_Handler3(void *param){
	XScuGic_Disable(&INTCInst, SW3_INT_ID);
	int sw_id = (int)param;
	printf("SW3 int  ");
	XScuGic_Enable(&INTCInst, SW3_INT_ID);
}


static void SW_intr_Handler4(void *param){
	XScuGic_Disable(&INTCInst, SW4_INT_ID);

	int sw_id = (int)param;
	printf("SW4 int\n");
	XScuGic_Enable(&INTCInst, SW4_INT_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_Handler1,(void *)1);
    if(status != XST_SUCCESS) return XST_FAILURE;

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

    status = XScuGic_Connect(&INTCInst,SW3_INT_ID,(Xil_ExceptionHandler)SW_intr_Handler3,(void *)3);
    if(status != XST_SUCCESS) return XST_FAILURE;

    status = XScuGic_Connect(&INTCInst,SW4_INT_ID,(Xil_ExceptionHandler)SW_intr_Handler4,(void *)4);
    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);
    IntcTypeSetup(&INTCInst, SW3_INT_ID, INT_TYPE_RISING_EDGE);
    IntcTypeSetup(&INTCInst, SW4_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);
    XScuGic_Enable(&INTCInst, SW3_INT_ID);
    XScuGic_Enable(&INTCInst, SW4_INT_ID);

//    XScuGic_Disable(&INTCInst, SW_INT_ID);
    return XST_SUCCESS;
}

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

运行结果发现,

理想情况下应该是
SW1 int SW2 int SW3 int SW4 int
SW1 int SW2 int SW3 int SW4 int

这样一行行的打印下去,结果发现有时候会出现中断两次触发的情况
SW1 int SW2 int SW3 int SW3 int SW4 int
SW1 int SW2 int SW3 int SW4 int

例如上面这样。

一开始百思不得其解,还改成了电平触发试试,结果总是不对。折腾了一天后,突然想起了,是不是组合逻辑输入中断管脚引起的。

把下面代码

assign    IRQ_F2P[0] = (temp >= 5000000 ) && (temp < 5000000  + 70);
assign    IRQ_F2P[1] = (temp >= 10000000) && (temp < 10000000 + 70);
assign    IRQ_F2P[2] = (temp >= 15000000) && (temp < 15000000 + 70);
assign    IRQ_F2P[3] = (temp >= 20000000) && (temp < 20000000 + 70);

改成

always @(posedge broad_clk)    
begin
    IRQ_F2P[0] <= (temp >= 5000000 ) && (temp < 5000000  + 70);
    IRQ_F2P[1] <= (temp >= 10000000) && (temp < 10000000 + 70);
    IRQ_F2P[2] <= (temp >= 15000000) && (temp < 15000000 + 70);
    IRQ_F2P[3] <= (temp >= 20000000) && (temp < 20000000 + 70);
end

问题得到解决。

问题分析:

PL给PS的中断激励输入实际上也是一个跨时钟域的输出。此时不能使用组合逻辑作为输出,这样会产生毛刺,这样对于之前那种一个中断激励被触发了两次就可以解释为什么了,是因为毛刺引发的额外中断。

后面设计一定要避免这种情况。

你可能感兴趣的:(ZYNQ PL 中断PS测试 以及重复中断问题的解决)