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.

AM6442: AM64x R5F code MCU interrupt hander set up

Part Number: AM6442
Other Parts Discussed in Thread: SYSCONFIG

Hi,

I am trying to set up an interrupt handler on AM64x R5F MCU but without success. This is the page I followed: https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/KERNEL_DPL_HWI_PAGE.html#autotoc_md2217

Suppose I would like to set up an interrupt handler for IEP0 pr1_sync0_out_pend.
First, I set up the "ICSSG1 INTC Internal Signals Mapping" in SysConfig GUI:

image.png

Second, I register the interrupt handler by calling RegisterISR() like below: 

void my_handler(EnetMod_Handle hMod)
{
    uintptr_t iep0Regs = (uintptr_t)hMod->virtAddr;
    ...
    HWREG(iep0Regs + CSL_ICSS_PR1_IEP0_SLV_CMP_STATUS_REG) &= ~0x03U;
}

void RegisterISR(EnetMod_Handle hMod)
{
    HwiP_Params hwiParams;
    HwiP_Object hwiObj;
    
    HwiP_Params_init(&hwiParams);

    hwiParams.args = (void *)hMod;
    hwiParams.callback = (HwiP_FxnCallback)my_handler;
    hwiParams.eventId = 14;
    hwiParams.intNum = 9;
    hwiParams.isPulse = 1U;   // pulse
    hwiParams.priority = 9;

    HwiP_construct(&hwiObj, &hwiParams);
}

However, the function my_handler() is not invoked.

Questions:
1. How to set up an interrupt handler on R5F core using AM64x MCU SDK? Is there anything I missed or did incorrectly?
2. It seems to me there is no API for the application to do this type of low level configuration. I need to add this code snippet in TI SDK source code library.
Is there an API that the application can use to set up an ISR (not to modify the code in the TI SDK)? Please recommend a way that fits in TI SDK's architecure.


Thank you for your help!

 

  • Hi,

    The interrupt number which you are using above seems to be incorrect. The interrupt lines that are going from PRU_ICSSG0 to R5FSS0-0 is given in the TRM.

    Please refer below image.

    Regards,

    Tushar

  • Hi Tushar,

    I modified my settings/code:

    void my_handler(EnetMod_Handle hMod)
    {
        uintptr_t iep0Regs = (uintptr_t)hMod->virtAddr;
        ...
        HWREG(iep0Regs + CSL_ICSS_PR1_IEP0_SLV_CMP_STATUS_REG) &= ~0x01U;
    }

    void RegisterISR(EnetMod_Handle hMod)
    {
        HwiP_Params hwiParams;
        HwiP_Object hwiObj;
        
        HwiP_Params_init(&hwiParams);

        hwiParams.args = (void *)hMod;
        hwiParams.callback = (HwiP_FxnCallback)my_handler;
        hwiParams.eventId = 14;
        hwiParams.intNum = CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_2;      // 250
        hwiParams.isPulse = 1U;   // pulse
        hwiParams.priority = 2;

        HwiP_construct(&hwiObj, &hwiParams);       
    }

    HwiP_enableInt(CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_2);

    I still do not see my_handler() invoked.

    Can you please explain why the callback is still not called? How do I make it work?

    Thank you!

  • Can you please tell the method how are you confirming that the callback function didn't hit?

    Are you using Debug logs inside the ISR?

    Regards,

    Tushar

  • Hi Tushar,

    Ok, I originally put DebugP_log(() in the ISR and a counter to increment by 1. Now I took the DebugP_log() out.

    The counter value is 0 all the time.

    Not sure if this matters, but I added my code on top the TI MCU SDK example: mcu_plus_sdk_am64x_11_01_00_17\source\networking\enet\core\examples\tsn\gptp_icssg_app\gptp_icssg_switch

    I suppose "Host Interrupt 2" in SysConfig "INTC Host Interrupt" maps to CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_2. If this is correct, what else needs to be done.

    Thank you!

  • Hi Matt,

    Please try once keeping the hwiParams.isPulse = 0U;hwiParams.eventId = 0U; while setting up the interrupt.

    Regards,

    Tushar

  • Hi Tushar,
    Setting isPulse and eventId to 0 did not help.

    Let me describe what I tried to do again:
    I would like to set up an interrupt service routine on R5F Core 0 when the ICSSG1 IEP0 sync_out0 happens (PRG1_IEP0_EDC_SYNC_OUT0, or equivalently compare event of IEP_CMP0_REG0/IEP_CMP0_REG1 registers ?).

    Here are what I did:
    First, start with TI MCU SDK gptp_icssg_switch example in [SDK-installed-path]\source\networking\enet\core\examples\tsn\gptp_icssg_app\gptp_icssg_switch/

    Second, use SySconfig to set the event/channel/host interrupt mapping:

    Last, call RegisterISR() below to set up the ISR.

    volatile uint32_t count = 0;
    static void myISRHandler()
    {
        count++;
    }

    void RegisterISR()
    {
        HwiP_Params hwiParams;
        HwiP_Object hwiObj;
        int32_t status;
        
        HwiP_Params_init(&hwiParams);

        hwiParams.args = (void *)NULL;
        hwiParams.callback = (HwiP_FxnCallback)myISRHandler;
        hwiParams.eventId = 0; //14;
        hwiParams.intNum = CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_6;
        hwiParams.isPulse = 0; //1U;
        hwiParams.priority = 0;
        hwiParams.isFIQ = 0;

        status = HwiP_construct(&hwiObj, &hwiParams);    
        
        if (status != SystemP_SUCCESS)
        {
            DebugP_log("*** HwiP_construct() error.\r\n");
        }
        HwiP_enableInt(CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_6);
    }

    Can you please help identify what I did incorrectly and what needs to be done to achieve this?
    Also, I wonder if you have a chance to try to do this on AM64x EVM or AM243x EVM. See if you can see the ISR gets invoked successfully?

    Thank you for your help!

  • Hi Matt,

    The above interrupt configuration looks correct. You should get an interrupt with this config.

    You can try one more experiment , keep the eventId = 0 and then after enabling the interrupt, call the HwiP_post(CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_6) API to trigger the interrupt manually to verify the configuration is correct.

    similarly you can do with changing the eventId = 14 also.

    Regards,

    Tushar

  • Hi Tushar,

    Thank you for the suggestion. I was able to see the ISR gets triggered by manually posting the interrupt (HwiP_post()) - at least one small step... Slight smile Both eventId =  0 and 14 work.

    So this means the ISR was set up correctly within R5F Core 0, but somehow the interrupt from ICSSG1 was not routed to R5F Core 0?

    My understanding is we need to configure this routing of sync_out0 (PRG1_IEP0_EDC_SYNC_OUT0), or compare event of IEP_CMP0_REG0/IEP_CMP0_REG1 registers on ICSSG1 IEP0 to connect to "Host Interrupt 6" on R5F Core 0 from sysConfig:

    Can you please advise what else is still missing, since I still did not get any sync_out0 (or compare event of IEP_CMP0 registers) using the above set up in sysConfig?

    Thank you for your help!

  • Hi Matt,

    Thanks for providing the above results. 

    So this means the ISR was set up correctly within R5F Core 0, but somehow the interrupt from ICSSG1 was not routed to R5F Core 0?

    Yes, this means the interrupt is configured properly on R5F core but is not getting any interrupt from PRU.

    I am routing your thread to one of my team member to help with PRU configuration.

    Please expect response in few business days.

    Regards,

    Tushar

  • Hi Tushar,

    Thanks for the quick response, I wonder if you or any of your team members had a chance to try this on an AM243x or AM64x EVM?

    If you have done this successfully, then I should be able to follow what you did.

    Anyway, I look forward to the response from you or your team member.

    Thank you!

  • Hello Matt,

    What firmware is running on the PRU cores? Have you actually turned on the IEP counter and configured it to generate a sync signal?

    For general documentation about how to configure the PRU's INTC (interrupt controller) from the R5F, refer to the PRUICSS driver documentation:
    https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/DRIVERS_PRUICSS_PAGE.html

    I do not have a simple example that does exactly what you are looking for, but here is some code for configuring the IEP counter in C on AM335x. There will be some differences, like not needing to enable the OCP master port on AM64x:
    https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/examples/am335x/PRU_IEP/PRU_IEP.c 

    For future readers 

    There is a general example for sending interrupts back and forth between an MCU+ core and the PRU cores in the new OpenPRU repo & PRU Academy. The projects currently build for AM261x, AM263x, AM263Px, and we will port the project to AM64x & AM243x sometime soon:
    https://github.com/TexasInstruments/open-pru/tree/main/academy/intc/intc_mcu

    Where documentation for that project will be in the AM64x PRU Academy:
    https://dev.ti.com/tirex/explore/node?node=A__AUQarf9kGpfOq9t2tseoXw__AM64-ACADEMY__WI1KRXP__LATEST

    Regards,

    Nick

  • Hi Nick,
    Thank you for the quick response.

    I just used the PRU firmware that comes with AM64x SDK 11.01.

    And yes, I believe the IEP counter is turned on and sync out signal is generated. I was able to see sync_out0 signal on J18 pin 1 (PRG1_IEP0_EDC_SYNC_OUT0).

    Like I mentioned, I first started with TI MCU SDK gptp_icssg_switch example in [SDK-installed-path]\source\networking\enet\core\examples\tsn\gptp_icssg_app\gptp_icssg_switch. My understanding is this example turns on IEP counter and generates sync_out0 signal.

    Then I added the SySconfig similar to what is described here: software-dl.ti.com/.../DRIVERS_PRUICSS_PAGE.html. My understanding is this sets up the routing of PRU interrupt events to R5F Core 0. 

    Last, I added the ISR set up code, which Tushar and I confirmed it is correct - because the ISR was invoked when HwiP_post() is called.

    According to Tushar, it seems the interrupt (Event 14: pr1_sync0_out_pend) from PRU was not routed to R5F Core 0 correctly.

    Can you please help identify what is not done correctly in my SysConfig setting, or what else needs to be done?


    Thank you for you help!

  • Hello Matt,

    The PRU's interrupt controller needs to be configured, and I would assume that should be done by the R5F with PRUICSS API calls. My suspicion is that the SysConfig settings are just configuring the R5F drivers, not actually configuring the PRU_ICSSG's INTC settings. You could double-check by reading the INTC registers to see if you have actually routed event 14 to channel 9, and then routed channel 9 to (from the PRU's view) host interrupt 9.

    I am not an MCU+ networking expert, but I would expect that you would want to make sure your settings were getting applied with PRUICSS_intcInit(). Looks like that is called in /mcu_plus_sdk_am64x_11_01_00_17/source/networking/enet/core/src/per/icssg.c, but I am not sure if that is the correct file to modify or if you would modify something else.

    I am sending your thread to another team member who has more experience with MCU+ networking.

    Regards,

    Nick

  • Hi Nick,

    Thanks for the quick response, I think what you suspected makes sense. I will dig more on the PRU_ICSSG INTC settings from R5F Core.

    In the meantime, please send this thread to your team member who has more experience in this area. It is definitely desirable to get a definite solution from TI. Although a lot of documents are available, I did not seem to find good instructions for doing things that need to integrate R5F Core and PRU ICSSG features. We will need your and your team member's help on this. Otherwise, our project will turn into a research project of TI EVM... Slight smile

    Thank you!

  • Hi Nick,

    I did find SysConfig only sets up the PRUICSS_IntcInitData struct, but it does not generate the calls to PRUICSS_registerIrqHandler() and PRUICSS_intcInit(). So I did that at the end of Icssg_open() in icssg.c:

    PRUICSS_registerIrqHandler(hIcssg->pruss->hPruss,
                               14,
                               CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_6,
                               1,                      /*  eventNum     */
                               1,                      /*  wait_enable  */
                               my_handler);
    PRUICSS_intcInit(hIcssg->pruss->hPruss, icssgCfg->mdioLinkIntCfg.prussIntcInitData);

    However, this still does not work.

    I wonder if there is any update on your end or from your team members. Please let me know. Thank you!

  • Hi Matt,

    There is a correction in the configuration that you are using. The interrupts ICSSG1_PR1_HOST_INTR_PEND_0-7 are mapped to Host interrupts 2-9 in sysconfig. So, if you are using host interrupt 6 in sysconfig gui, you would have to use _ICSSG1_PR1_HOST_INTR_PEND_8 whiule registering the HwiP interrupt.

    Please let us know if this is working for you.

    Thanks and regards,
    Teja.

  • Thank you, Teja and Nick!

    So it turned out to be the other way around: 
    In SysConfig GUI, I used Host Interrupt 7:

    Plus, added the following code in icssg.c Icssg_open():
    PRUICSS_registerIrqHandler(hIcssg->pruss->hPruss,
                               14,
                               CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_5,
                               1,                      /*  eventNum     */
                               1,                      /*  wait_enable  */
                               my_handler);    
    HwiP_disableInt(CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_5);
    PRUICSS_intcInit(hIcssg->pruss->hPruss, icssgCfg->mdioLinkIntCfg.prussIntcInitData);


    With these, I was able to make some small progress. However, the number of interrupts I am getting is in the order of 10^6 per second. So the program is bombarded with interrupts and cannot run to the while loop in tsnapp_icssg_main.c, as soon as interrupt is enabled:
    if (EnetApp_initTsn())
    {
        DebugP_log("EnetApp_initTsn failed\r\n");
    }
    else
    {
        HwiP_enableInt(CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_5);
        while (true)        // program does not enter the while loop here
        {
            // Print CPU load
            ClockP_usleep(1000);
            App_printCpuLoad();
            TaskP_yield();
        }
        EnetApp_stopTsn();
        EnetApp_deInitTsn();
    }

    The difference of ClockP_getTimeUsec() between the 1000th ISR and 2000th ISR calls is about 956. If ClockP_getTimeUsec() is correct, then there are 10^9 / 956 ~= 1.046 * 10^6 interrupts in one second.

    My understanding is sync_out0 frequency is 1kHz. So the sync_out0 interrupt should happen 1000 times in one second. Can you please advise what is still not done correctly?

    Thank you for your help!

  • Hi Matt,

    Sorry for the slip-up in the indicated mapping. 

    Regarding the frequency of interrupts, can you please check the values of the following registers?

    1. PRU_ICSSG1_IEP0_PR1_IEP0__SLV__REGS_sync_ctrl_reg
    2. PRU_ICSSG1_IEP0_PR1_IEP0__SLV__REGS_sync_pwidth_reg
    3. PRU_ICSSG1_IEP0_PR1_IEP0__SLV__REGS_sync0_period_reg

    These control the interval at which the sync out interrupt is generated. Please share these values to confirm at which frequency the sync0 out is configured to be. 

    Thanks and regards,
    Teja.

  • Hi Teja,

    These are the register values you requested (CMP0 registers also included):
    *** IEP0 reg base:    0x300ae000
    *** SYNC_CTRL_REG:    0x00000003
    *** SYNC_PWIDTH_REG:  0x0001e847
    *** SYNC0_PERIOD_REG: 0x00000001
    *** CMP0_REG0:        0x000f423c
    *** CMP0_REG1:        0x000f423c

    I can see the sync out0 signal from the scope on J18 pin 1 of the AM243x EVM. The frequency is 1kHz; then goes high for about 370 microseconds, then low for about 630 microseconds.  The SYNC_PWIDTH_REG value looks to correspond to the 630 (maybe some inverse logic). The frequency 1kHz looks to be determined by what is programmed on CMP0 registers.

    void DumpRegisters()
    {
        DebugP_log("*** IEP0 reg base:    0x%08x\r\n", iep0Regs);
        DebugP_log("*** SYNC_CTRL_REG:    0x%08x\r\n", HWREG(iep0Regs + CSL_ICSS_G_PR1_IEP0_SLV_SYNC_CTRL_REG));
        DebugP_log("*** SYNC_PWIDTH_REG:  0x%08x\r\n", HWREG(iep0Regs + CSL_ICSS_G_PR1_IEP0_SLV_SYNC_PWIDTH_REG));
        DebugP_log("*** SYNC0_PERIOD_REG: 0x%08x\r\n", HWREG(iep0Regs + CSL_ICSS_G_PR1_IEP0_SLV_SYNC0_PERIOD_REG));     
        DebugP_log("*** CMP0_REG0:        0x%08x\r\n", HWREG(iep0Regs + CSL_ICSS_G_PR1_IEP0_SLV_CMP0_REG0));
        DebugP_log("*** CMP0_REG1:        0x%08x\r\n", HWREG(iep0Regs + CSL_ICSS_G_PR1_IEP0_SLV_CMP0_REG1));       
    }

    if (EnetApp_initTsn())
    {
        DebugP_log("EnetApp_initTsn failed\r\n");
    }
    else
    {
        void DumpRegisters();
        DumpRegisters();
        HwiP_enableInt(CSLR_R5FSS0_CORE0_INTR_PRU_ICSSG1_PR1_HOST_INTR_PEND_5);
        while (true)
        {
            // Print CPU load
            ClockP_usleep(1000);
            App_printCpuLoad();
            TaskP_yield();
        }
        EnetApp_stopTsn();
        EnetApp_deInitTsn();
    }

    Hope these help solve the excessive interrupts mystery.

    Thank you!

  • Hi Matt,

    The SYNC_PWIDTH_REG is supposed to control the frequency, and the operation is done by the firmware updating the CMP0_REGx value periodically. The current configuration indicates that the pulse width is 0.5 ms, which at 50% duty cycle corresponds to 1KHz frequency. 

    Let me check with the ICSSG firmware team to get more insights on the behavior of interrupt frequency, and the duty cycle being different than 50%. Please give me 2 days to get the details on this.

    Thanks and regards,
    Teja.

  • Hi Teja,

    Yes, please help find out the behavior of the sync_out0 interrupt.

    Just to share my observation of the TI ptp implementation:

    The SYNC_PWIDTH_REG value looks to correspond to this setting in SysConfig:

    Thank you.

  • Hi Matt,

    Yes, this observation is correct. The pwidth value takes this syscfg value as input. But we need to understand why the interrupt is being triggered much more than expected, when the sync0_out signal is still at 1 kHz.

    I am checking it with our experts, and will let you know the results by EoD tomorrow.

    Thanks and regards,
    Teja.

  • Hi Matt, 

    I think root cause of the excessive interrupt rate (~1 million/second instead of 1,000/second) is that the interrupt event is not being cleared in your ISR. When a PRU-ICSSG interrupt is not acknowledged/cleared, it continuously re-triggers, flooding the CPU.

    Are you clearing the event? You need to call PRUICSS_clearEvent() in the ISR API to clear the system event?

    void my_handler(void *args)
    {
        // Your ISR code here
        count++;
        
        // Clear the interrupt event - THIS IS REQUIRED
        // sysEventNum = 14 for pr1_sync0_out_pend
        PRUICSS_clearEvent(gPruIcssHandle, 14);
    }

    BR
    JC

  • Thank you JC and Teja for the reminder.

    I missed the clearing event call. After adding PRUICSS_clearEvent() call as you suggested, the application is able to run to the while loop in EnetApp_mainTask(). However, the number of interrupts still does not seem right to me. It is reduced to around 335,000 ~ 340,000 per second. If my understanding is correct,  we should be seeing 1000 interrupts per second, since sync_out0 frequency is 1kHz and we should be getting 1 interrupt per sync_out0 period.

    Please advise what else needs to be done?

    Thank you for your help!

  • Hi Matt, 

    Can you reduce the pulse width (may be <10us?) and try again? Looks like level triggered.

    BR
    JC 

  • Hi JC,

    I changed the pulse width to 9 micro seconds. So the number of interrupts per second becomes around 6,000. Here is the time sync setting:

    Not sure if related, you mentioned the interrupt looks like level triggered, but the interrupt type is set to "pulse" (not level):

    So hopefully this helps to identify the cause of the issue.

    Thank you for your help!

  • Hi Matt,

    Please use edge instead of pulse.

    BR
    JC

  • Thank you, JC!

    It is my bad. My brain and eyes must have not worked together well; somehow I thought "pulse" is edge triggered the other day... :)

    Yes, after I changed the type from pulse to edge. I was able to get 1000 interrupts per second as expected.

    One last question before I close this thread:

    Is there an API you recommend to get the PRU handle from the application? Basically I would like to add my modification in the application, not the TI library.

    This is what I use now in EnetApp_mainTask() of tsnapp_icssg_main.c:

    for (i = 0U; i < gEnetAppCfg.numPerCtxts; i++)
    {
        status = EnetApp_driverOpen(gEnetAppCfg.perCtxt[i].enetType, gEnetAppCfg.perCtxt[i].instId);
        ...
        EnetPer_Handle hPer = Enet_getPerHandle(gEnetAppCfg.perCtxt[i].hEnet);
        Icssg_Handle hIcssg = (Icssg_Handle)hPer;
        PRUICSS_registerIrqHandler(hIcssg->pruss->hPruss, ...);
        ...
    }

    It is a bit cumbersome. So I wonder if you have a better solution?

    Thank you for your help!

  • Hi Matt,

    Accessing the PRUICSS_Handle through the ICSSG peripheral handle (hIcssg->pruss->hPruss) is the recommended way to program PRU-ICSS registers and use driver-specific APIs like PRUICSS_registerIrqHandler().

    Note: In Dual MAC configurations, you can use either perCtxt[0] or perCtxt[1] when calling Enet_getPerHandle(), as both ports share the same underlying PRU-ICSS instance.

    BR
    JC

  • Hi JC,

    Thanks for the quick response, so it looks like you suggested the same method as I did?

    Thank you!