How do I configure an IPC interrupt?

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
euggersh
Level 5
Level 5
5 sign-ins First solution authored 50 replies posted

For my application, I need to send a 32-bit value (a pointer, to be specific) for the M4 core to the M0+ core.  I need to lock access to the memory pointed to by this pointer until the receiving end has processed the data.  The acquire and release mechanism in the IPC subsystem on the PSoC 6 seems like a perfect way to do that.  Unfortunately, as far as I can tell from the documentation, the IPC Pipe APIs provided in the PDL ignore this mechanism, so to achieve what I need to do, I need to use the IPC DRV APIs.

What those APIs are missing, though, is the ability to actually configure an IPC interrupt and set an ISR address for it.  I've spent a lot of time looking through the PDL code and can't figure out what needs to be in the structure that's passed to Cy_SysInt_Init().

1 Solution
MarkA_91
Employee
Employee
25 replies posted 10 replies posted 5 replies posted

For the lock / release problem, I developed a code example CE216795, PSoC 6 Dual-CPU Basics. Please take a look at that and let me know if it helps.

For the IPC interrupt problem, I am in the process of developing a code example. In the meantime, if the Cortex-M4 is the CPU being interrupted, use this code:

/* IPC defines. */

#define CM02CM4_IPC_CHAN_INDEX  (8UL) /* CM0 uses this channel to notify CM4 */

#define CM02CM4_IPC_INTR_INDEX  (8UL) /* Interrupt CM4 on notify from CM0 */

#define CM02CM4_IPC_INTR_MASK   (1UL << CM02CM4_IPC_INTR_INDEX)

/* Configuration structure for the IPC interrupt for CM4. */

const cy_stc_sysint_t IPC_IRQ_cfg = {

    .intrSrc        = cpuss_interrupts_ipc_8_IRQn, /* the 8 has to match the above channel # */

  #if (CY_CPU_CORTEX_M0P)

    .cm0pSrc            = 0,

  #endif

    .intrPriority    = 2

};

/* Pointers to IPC register structures. */

IPC_STRUCT_Type *ipcCm02cm4;

IPC_INTR_STRUCT_Type *ipcIntrCm02cm4;

in main():

    /* Initialize IPC channels and interrupt. */

    /* Get the IPC registers base addresses for the two IPC channels. */

    ipcCm02cm4 = Cy_IPC_Drv_GetIpcBaseAddress(CM02CM4_IPC_CHAN_INDEX);

    /* Get the IPC interrupt registers base address for the specified IPC channel. */

    ipcIntrCm02cm4 = Cy_IPC_Drv_GetIntrBaseAddr(CM02CM4_IPC_INTR_INDEX);

    /* Enable that interrupt source, as a notify from the other CPU. */

    Cy_IPC_Drv_SetInterruptMask(ipcIntrCm02cm4, CY_IPC_NO_NOTIFICATION, CM02CM4_IPC_INTR_MASK);

    /* Initialize the IPC interrupt with the IPC interrupt handler address. */

    (void)Cy_SysInt_Init(&IPC_IRQ_cfg, IPCIntrHand);

    NVIC_EnableIRQ(IPC_IRQ_cfg.intrSrc);

    /* Clear any pending interrupt sources */

    Cy_IPC_Drv_ClearInterrupt(ipcIntrCm02cm4, 0xFFFF, 0xFFFF); /* clear all possible IPC interrupt sources */

    __enable_irq(); /* Enable global interrupts. */

/* Come here on interrupt when CM0 notifies CM4, through CM02CM4_IPC_CHAN_INDEX */

void IPCIntrHand(void)

{

   /* Optionally take a look at the interrupt status, before and/or after clearing the interrupt. */

//    volatile uint32_t intrs = Cy_IPC_Drv_GetInterruptStatus(ipcIntrCm02cm4);

//    volatile uint32_t intrsm = Cy_IPC_Drv_GetInterruptStatusMasked(ipcIntrCm02cm4);

    /* Clear the interrupt source: IPC notify from the other CPU. */

    Cy_IPC_Drv_ClearInterrupt(ipcIntrCm02cm4, CY_IPC_NO_NOTIFICATION, CM02CM4_IPC_INTR_MASK);

//    intrs = Cy_IPC_Drv_GetInterruptStatus(ipcIntrCm02cm4);

//    intrsm = Cy_IPC_Drv_GetInterruptStatusMasked(ipcIntrCm02cm4);

}

If the Cortex-M0+ is the CPU being interrupted, the code is almost the same. The configuration structure is slightly different, because the CM0+ supports only 32 interrupt inputs. So PSoC 6 has a set of N:1 multiplexers between all of the interrupt sources and each of the CM0+ interrupt inputs:

/* Configuration structure for the IPC interrupt for CM0+. */

const cy_stc_sysint_t IPC_IRQ_cfg = {

    .intrSrc        = NvicMux8_IRQn,

  #if (CY_CPU_CORTEX_M0P)

    .cm0pSrc            = cpuss_interrupts_ipc_8_IRQn, /* the 8 has to match the above channel # */,

  #endif

    .intrPriority    = 2

};

Hope this helps,

Mark

View solution in original post

10 Replies
GeonaP_26
Moderator
Moderator
Moderator
250 solutions authored 100 solutions authored 50 solutions authored

Pipe layer supports callbacks for user intervention. User need to call Cy_IPC_Pipe_RegisterCallback function to register the user-supplied callback functions/handlers.

CE223820 PSoC 6 MCU IPC Pipes implements message passing using pipe layer. Please get started with the explanation provided in section: 5.4 Message Passing of Architecture TRM. Later, you can follow the same approach used in code example. ie, configure two endpoints, one on each CPU. Register user-supplied callbacks; call Cy_IPC_Pipe_RegisterCallback() once per client to register each client's callback function. Please note that the client ID is just the index in the callback array. Each endpoint contains a dedicated interrupt, which executes registered callbacks.  Once the IPC message is sent, the receiving endpoint's interrupt handler is called. This ISR calls Cy_IPC_Pipe_ExecCallback. For feedback, there is a mechanism to invoke the release callback function defined by the sender of the message. This occurs once the callback function is returned by the receiver.

Let me know if it helps.

0 Likes

No, this does not help because:

1:  My original question wasn't answered.  How do I configure an IPC interrupt.

2:  From your description, I still can't release the lock when the receiving side is done with the data.  "This occurs once the callback function is returned by the receiver".  I'm not processing a large buffer of data in an interrupt context.  I need to release the lock once the data has been processed.

0 Likes
MarkA_91
Employee
Employee
25 replies posted 10 replies posted 5 replies posted

For the lock / release problem, I developed a code example CE216795, PSoC 6 Dual-CPU Basics. Please take a look at that and let me know if it helps.

For the IPC interrupt problem, I am in the process of developing a code example. In the meantime, if the Cortex-M4 is the CPU being interrupted, use this code:

/* IPC defines. */

#define CM02CM4_IPC_CHAN_INDEX  (8UL) /* CM0 uses this channel to notify CM4 */

#define CM02CM4_IPC_INTR_INDEX  (8UL) /* Interrupt CM4 on notify from CM0 */

#define CM02CM4_IPC_INTR_MASK   (1UL << CM02CM4_IPC_INTR_INDEX)

/* Configuration structure for the IPC interrupt for CM4. */

const cy_stc_sysint_t IPC_IRQ_cfg = {

    .intrSrc        = cpuss_interrupts_ipc_8_IRQn, /* the 8 has to match the above channel # */

  #if (CY_CPU_CORTEX_M0P)

    .cm0pSrc            = 0,

  #endif

    .intrPriority    = 2

};

/* Pointers to IPC register structures. */

IPC_STRUCT_Type *ipcCm02cm4;

IPC_INTR_STRUCT_Type *ipcIntrCm02cm4;

in main():

    /* Initialize IPC channels and interrupt. */

    /* Get the IPC registers base addresses for the two IPC channels. */

    ipcCm02cm4 = Cy_IPC_Drv_GetIpcBaseAddress(CM02CM4_IPC_CHAN_INDEX);

    /* Get the IPC interrupt registers base address for the specified IPC channel. */

    ipcIntrCm02cm4 = Cy_IPC_Drv_GetIntrBaseAddr(CM02CM4_IPC_INTR_INDEX);

    /* Enable that interrupt source, as a notify from the other CPU. */

    Cy_IPC_Drv_SetInterruptMask(ipcIntrCm02cm4, CY_IPC_NO_NOTIFICATION, CM02CM4_IPC_INTR_MASK);

    /* Initialize the IPC interrupt with the IPC interrupt handler address. */

    (void)Cy_SysInt_Init(&IPC_IRQ_cfg, IPCIntrHand);

    NVIC_EnableIRQ(IPC_IRQ_cfg.intrSrc);

    /* Clear any pending interrupt sources */

    Cy_IPC_Drv_ClearInterrupt(ipcIntrCm02cm4, 0xFFFF, 0xFFFF); /* clear all possible IPC interrupt sources */

    __enable_irq(); /* Enable global interrupts. */

/* Come here on interrupt when CM0 notifies CM4, through CM02CM4_IPC_CHAN_INDEX */

void IPCIntrHand(void)

{

   /* Optionally take a look at the interrupt status, before and/or after clearing the interrupt. */

//    volatile uint32_t intrs = Cy_IPC_Drv_GetInterruptStatus(ipcIntrCm02cm4);

//    volatile uint32_t intrsm = Cy_IPC_Drv_GetInterruptStatusMasked(ipcIntrCm02cm4);

    /* Clear the interrupt source: IPC notify from the other CPU. */

    Cy_IPC_Drv_ClearInterrupt(ipcIntrCm02cm4, CY_IPC_NO_NOTIFICATION, CM02CM4_IPC_INTR_MASK);

//    intrs = Cy_IPC_Drv_GetInterruptStatus(ipcIntrCm02cm4);

//    intrsm = Cy_IPC_Drv_GetInterruptStatusMasked(ipcIntrCm02cm4);

}

If the Cortex-M0+ is the CPU being interrupted, the code is almost the same. The configuration structure is slightly different, because the CM0+ supports only 32 interrupt inputs. So PSoC 6 has a set of N:1 multiplexers between all of the interrupt sources and each of the CM0+ interrupt inputs:

/* Configuration structure for the IPC interrupt for CM0+. */

const cy_stc_sysint_t IPC_IRQ_cfg = {

    .intrSrc        = NvicMux8_IRQn,

  #if (CY_CPU_CORTEX_M0P)

    .cm0pSrc            = cpuss_interrupts_ipc_8_IRQn, /* the 8 has to match the above channel # */,

  #endif

    .intrPriority    = 2

};

Hope this helps,

Mark

MarkA_91​, thanks, this is extremely helpful and exactly what I needed.  The missing piece for me was the "cpuss_interrupts_ipc_8_IRQn" (IRQ33).  I couldn't find that documented anywhere, and now that I have that bit of info, I see why:

pastedImage_0.png

Thanks again!

0 Likes

On a related note, it's unfortunate that the IPC IRQs aren't available in some sort of schematic component so that the fitter could allocate CM0+ IRQs for them.  Right now I have to make sure that I don't collide with anything allocated by the fitter and hope that it doesn't change in a way that'll cause collisions later.

0 Likes

And one more question... I assume I need to use different IPC IRQs for the notify interrupt on one core and the release interrupt on the other core for the same IPC channel?  Unless I'm missing something, I assume that using the same IRQ would result in both cores getting both interrupts?

0 Likes
MarkA_91
Employee
Employee
25 replies posted 10 replies posted 5 replies posted

I haven't tried it (I will for the code example I'm developing), but I think it's just one interrupt for each IPC channel. And I believe that for your application the interrupt would indeed go to both CPUs, in the following manner:

  • CPU1 initializes IPC channel and interrupt for release from the other CPU
  • CPU2 initializes the same channel and interrupt for notify from the other CPU
  • CPU1 does a notify. CPU2 is interrupted and processes the notify.

        CPU1 is also interrupted but ignores the interrupt

  • CPU2 does a release. CPU1 is interrupted and processes the release.

        CPU2 is also interrupted but ignores the interrupt

Again, I would have to try this out to confirm that it works as expected.

0 Likes

Can you elaborate on what you mean by "ignores the interrupt"?  Do you mean in software, or do you mean that the hardware might somehow know not to go actually handle the interrupt because it's not the originator?

0 Likes
MarkA_91
Employee
Employee
25 replies posted 10 replies posted 5 replies posted

Yes. The handler could simply read the status, see that is not the expected notify or release, and just return. You could disable the interrupt when you're not expecting it to happen but I think that would be more complex and unwieldy.

0 Likes

I think the problem with that approach would be that unless you clear the interrupt, it'll stay asserted and keep returning to the ISR.  And if you clear it, you may be clearing it before the other core has a chance to handle it.

0 Likes