在测试PL中断PS的过程中,本以为只要复制下代码就能顺利测出,结果在测试中断的过程中,总是会有重复中断的事情发生,郁闷至极,折腾了一天才找到问题。
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的中断激励输入实际上也是一个跨时钟域的输出。此时不能使用组合逻辑作为输出,这样会产生毛刺,这样对于之前那种一个中断激励被触发了两次就可以解释为什么了,是因为毛刺引发的额外中断。
后面设计一定要避免这种情况。