This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/AM6548: How to clear level interrupt on R5F

Part Number: AM6548
Other Parts Discussed in Thread: SYSBIOS

Tool/software: TI-RTOS

Hi,

I generate a system event from ICSSG0 PRU0 and routed this via MAIN2MCU_LVL_INTRTR0 to the VIM of R5_0. I registered a ISR on this HWI and it gets executed when I generate the event once with PRU0.
However, the ISR gets executed again and again, even if I cleared the interrupt with OSAL-functions or with the PRUICSS driver. I must miss something but I don't know what..

I've studied the PRUICCS driver example /home/thomas/ti/pdk_am65xx_1_0_3/packages/ti/drv/pruss/test/src
I've worked through the OSAL functions descriptions but it didn't helped.

Here is my code:

1. Initialize board and configure INTC of ICSSG

/*
 *  ======== main ========
 */
Int main()
{ 
    //Initialize the board and the required peripheral for blinking LED
    /* The UART_STDIO initializes the default UART port on the board
     *  and support stdio like UART_printf which is used by appPrint
     *
     *
     */

    Board_STATUS cfg = BOARD_INIT_UART_STDIO | BOARD_INIT_PINMUX_CONFIG | BOARD_INIT_MODULE_CLOCK;
    int status = Board_init(cfg);
    if (status != BOARD_SOK) {
         appPrint("\n Error: Board_init failed: error %d", status);
    }
    appPrint("\n Board Init complete");

    //Set CTRLMMR_PADCONFIG136 Register, to configure PIN AB25 as PRU0_GPIO0_GPO11
    HW_WR_REG32(0x0011C220, 0x00010000);

    peripheralInit();

    //Try to get interrupt up and running to trigger/kick the PRU
    PRUICSS_Config *pruIcssCfg;

    int32_t ret;
    ret = PRUICSS_socGetInitCfg(&pruIcssCfg);
    if(ret != PRUICSS_RETURN_SUCCESS)
    {
        appPrint("\n PRU Get init config was not sucessfull!");
        return 0;
    }

    PRUICSS_Handle pruHandle = PRUICSS_create(pruIcssCfg, PRUICCSS_INSTANCE_ONE);

    //Now lets configure the INTC of PRU: Channel 0 should be Mapped to Host Int 0.
    PRUICSS_IntcInitData pruss_intc_config =
    {
         /*Enabled SYSEVTs. -1 indicates end of list*/
         /*I want to enable SYSEVT 16*/
         {ARM_PRU0_EVENT, PRU0_ARM_EVENT, 0xFF},

         /*SysEvt to Channel map. SYSEVTs - Range:0..63 Channels -Range: 0..9
        {-1, -1} indicates end of list*/
         /*I want to map SYSEVT 16 to Channel 0*/
         {
              {ARM_PRU0_EVENT, CHANNEL0, SYS_EVT_POLARITY_HIGH, SYS_EVT_TYPE_PULSE},
              {PRU0_ARM_EVENT, CHANNEL2, SYS_EVT_POLARITY_HIGH, SYS_EVT_TYPE_PULSE},
              {0xFF, 0xFF, 0xFF, 0xFF}
         },

         /*Channel to Host map.Channels -Range: 0..9  HOSTs - Range:0..9
         {-1, -1} indicates end of list*/
         /*I want to map Channel 0 to Host 0 */
         {
              {CHANNEL0, PRU0}, //This is for ARM_PRU0_EVENT
              {CHANNEL2, 2}, //This is for PRU0_ARM_EVENT
              {0xFF, 0xFF}
         },

         /*10-bit mask - Enable Host0-Host9 {Host0/1:PRU0/1, Host2..9 : PRUEVT_OUT0..7)*/
         /*I want to enable Interrupts for Host 0 and Host 2*/
         (PRU0_HOSTEN_MASK | PRU1_HOSTEN_MASK | PRU_EVTOUT0_HOSTEN_MASK)
    };

    ret = PRUICSS_pruIntcInit(pruHandle, &pruss_intc_config);
    if(ret != PRUICSS_RETURN_SUCCESS)
    {
        appPrint("\n PRU Interrupt config was not sucessfull!");
        return 0;
    }

    //Generate System Interrupt 0
    ret = PRUICSS_pruSendEvent(pruHandle, ARM_PRU0_EVENT);
    if(ret != PRUICSS_RETURN_SUCCESS)
    {
        appPrint("\n System Event 0 was not send successfull to PRU!");
        return 0;
    }


    configureInterruptRouter();
    registerISROnPRUEventViaOsal();
    //registerISROnPRUEventViaPRUICSSDriver(pruHandle);

    BIOS_start();

    /*
     *  normal BIOS programs, would call BIOS_start() to enable interrupts
     *  and start the scheduler and kick BIOS into gear.  But, this program
     *  is a simple sanity test and calls BIOS_exit() instead.
     */
    BIOS_exit(0);  /* terminates program and dumps SysMin output */
    return(0);
}

2. Configure MAIN2MCU_LVL_INTRTR0

void configureInterruptRouter()
{
    CSL_IntrRouterCfg intrRouterMain2MCUCfg;


    /* Initialize Main to MCU Interrupt Router config structure */
    intrRouterMain2MCUCfg.pIntrRouterRegs = (CSL_intr_router_cfgRegs *)(uintptr_t)(CSL_MAIN2MCU_LVL_INTRTR0_CFG_BASE);
    intrRouterMain2MCUCfg.pIntdRegs       = (CSL_intr_router_intd_cfgRegs *)(uintptr_t)NULL;
    intrRouterMain2MCUCfg.numInputIntrs   = 192;
    intrRouterMain2MCUCfg.numOutputIntrs  = 64;

    /* Route PRU int router output to MAIN2MCU int router output, MAIN2MCU int router output int number
     * is derived from the MCU GIC interrupt number.*/
    /* 32: ICSSG_0_HOST_INT0 PRU_ICSSG0 host interrupt 2, see TRM 9.4.10 MAIN2MCU_LVL_INTRTR0 Interrupt Map */
    int pruIntRtrInIntNum = 32;

    /* See TRM 9.3.3.2 MAIN2MCU_LVL_INTRTR0 Integration:
     * MAIN2MCU_RTR_LVL_MUX_INTR[63:0] is connected to MCU_R5_CORE0_INT_IN[223:160]
     * Therefore we have a offset of 160. [0] <-> [160]
     * */
    int pruIntRtrOutIntNum = CSL_MCU0_INTR_MAIN2MCU_LVL_INTR0_OUTL_2 - 160;
    CSL_intrRouterCfgMux(&intrRouterMain2MCUCfg, pruIntRtrInIntNum, pruIntRtrOutIntNum);
}

3a. Register ISR via Osal functions to the interrupt

/*
 * This registers an ISR to an event from PRU via Osal functionality.
 * It's required, that the component MAIN2MCU_LVL_INTRTR0 is already configured.
 */
void registerISROnPRUEventViaOsal()
{
    /*Register on the interrupt from pru*/
    HwiP_Handle hwiHandle = NULL;
    OsalInterruptRetCode_e retCode;

    OsalRegisterIntrParams_t interruptRegParams;
    /* Initialize with defaults */
    Osal_RegisterInterrupt_initParams(&interruptRegParams);
    interruptRegParams.corepacConfig.isrRoutine = (&dummyIrqHandler);
    interruptRegParams.corepacConfig.priority = 0x20U;
    interruptRegParams.corepacConfig.name=NULL;
    interruptRegParams.corepacConfig.corepacEventNum=CSL_MCU0_INTR_MAIN2MCU_LVL_INTR0_OUTL_2;
    interruptRegParams.corepacConfig.intVecNum=CSL_MCU0_INTR_MAIN2MCU_LVL_INTR0_OUTL_2; /* Host Interrupt vector */
    /* Register interrupts */
    retCode = Osal_RegisterInterrupt(&interruptRegParams,&(hwiHandle));

    //Enable the interrupt
    //Osal_EnableInterrupt(CSL_MCU0_INTR_MAIN2MCU_LVL_INTR0_OUTL_2, CSL_MCU0_INTR_MAIN2MCU_LVL_INTR0_OUTL_2);
    if(retCode != osal_OK)
    {
        appPrint("\n Error: Osal register int failed: error %d", retCode);
    }
}

4a. ISR handler which "should" also reset the level interrupt

void dummyIrqHandler(uintptr_t foobar)
{
    Osal_ClearInterrupt(CSL_MCU0_INTR_MAIN2MCU_LVL_INTR0_OUTL_2, CSL_MCU0_INTR_MAIN2MCU_LVL_INTR0_OUTL_2);
    GPIO_toggle(1);
}

I also tried step 3 and 4 with PRUICSS driver functions (PRUICSS_registerIrqHandler and PRUICSS_pruClearEvent), register works but ISR will be called over and over again too. I can provide that code too if required.

5. Create event on PRU which is routed to R5F

volatile register uint32_t __R31;

void main(void)
{
    __R31 = 0x21;
}

Additional Information: Registered ISR via Osal functions. I debugged into the callstack when the ISR gets executed.
Hwi.c Hwi_dispatchIRQC(Hwi_Irp irp)

Log_write5(Hwi_LM_begin, (IArg)hwi, (IArg)hwi->fxn,
               (IArg)prevThreadType, (IArg)intNum, hwi->irp);

    if (hwi->triggerType == Hwi_TriggerType_PULSE) {
        Hwi_vim.GROUP[intNum >> 5].ENABLEDSTATUSCLEAR =
            (UInt)(1 << (intNum & 0x1f));
    }

    if (Hwi_dispatcherAutoNestingSupport) {
        Hwi_enable();

        /* call the user's isr */
        (hwi->fxn)(hwi->arg);

        Hwi_disable();
    }
    else {
        /* call the user's isr */
        (hwi->fxn)(hwi->arg);
    }

    if (hwi->triggerType == Hwi_TriggerType_LEVEL) {
        Hwi_vim.GROUP[intNum >> 5].ENABLEDSTATUSCLEAR =
            (UInt)(1 << (intNum & 0x1f));
    }

    /* Ack end of interrupt */
    Hwi_vim.IRQVECADDRESS = intNum;

    Log_write1(Hwi_LD_end, (IArg)hwi);

This function should clear the level interrupt, right? So should I even call the clearInterrupt-function from Osal/PRUICCS?

I hope you can direct me into the right direction.

Best regards,
Thomas



  • Thomas,

    I will look into your code tomorrow. Yes, it would be great if you can send us your project so I can try to reproduce.

    Regards,
    Garrett
  • Thank you very much Garrett. Sure I can give you the projects. Is it possible to do it non public way?
  • Thomas,

    Can you try to follow the VIM IRQ procedure in TRM section - 6.3.3.6.9.1 Servicing IRQ Through Vector Interface

    --------
    3.
    b. Level
    i. Clear the interrupt at the source
    ii. Clear the status by writing a '1' to the appropriate bit in the VIM_IRQSTS_j register, or
    VIM_STS_j register. This way, the level should be gone at the input to the VIM, it will avoid
    falsely re-calling the interrupt. If the source maintains the level, then it means there is another
    interrupt
    4. Write any value to the VIM_IRQVEC register
    a. This will clear the priority mask and will cause all interrupts to be re-evaluated for the new highest
    priority interrupt
    b. This will also clear the VIM_ACTIRQ[31] VALID bit

    Yes, you may send the project to a TI FAE who you work with and ask him forward to me. Thanks.

    Regards,
    Garrett
  • Hi Garrett,

    I could find finally the solution: The interrupt was not cleared at the source. Writing 17d (for ICSSG event 17) to 0xb020024 clears the interrupt on INTC of ICSSG and it seems that the Osal or SysBios is doing the rest properly on the VIM side.

    Thanks again & have a nice weekend!
    Thomas