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.

TMS320F28388D: About the problem that CM4 to CPU1 cannot enter the interrupt when using the IPC message queue

Part Number: TMS320F28388D

Hi Team,

There's an issue from the customer need your help:

Original code content:

//#############################################################################
//
// FILE:   ipc_ex2_msgqueue_cm.c
//
// TITLE:  IPC example with interrupt and message queue
//
//! \addtogroup driver_cm_c28x_dual_example_list
//! <h1> IPC message passing example with interrupt and message queue </h1>
//!
//! This example demonstrates how to configure IPC and pass information from
//! C28x to CM core with message queues.
//! It is recommended to run the C28x1 core first, followed by the CM core.
//!
//! \b External \b Connections \n
//!  - None.
//!
//! \b Watch \b Variables \n
//!  - None.
//!
//
//#############################################################################
// $Copyright:
// Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//###########################################################################

//
// Included Files
//
#include "cm.h"
#include "ipc.h"

//
// Defines
//
#define IPC_CMD_READ_MEM   0x1001
#define IPC_CMD_RESP       0x2001

#define TEST_PASS          0x5555
#define TEST_FAIL          0xAAAA

IPC_MessageQueue_t messageQueue;

#pragma DATA_SECTION(CMData, "MSGRAM_CM_TO_CPU1")
uint32_t CMData[10];


//
// IPC ISR for Flag 1
// C28x core sends data with message queue using Flag 0
//
__interrupt void IPC_ISR1()
{
    int i;
    IPC_Message_t TxMsg, RxMsg;
    bool status = false;

    //
    // Read the message from the message queue
    //
    IPC_readMessageFromQueue(IPC_CM_L_CPU1_R, &messageQueue, IPC_ADDR_CORRECTION_ENABLE,
                             &RxMsg, IPC_NONBLOCKING_CALL);

    if(RxMsg.command == IPC_CMD_READ_MEM)
    {
        status = true;

        //
        // Read and compare data
        //
        for(i=0; i<RxMsg.dataw1; i++)
        {
            if((*(uint32_t *)RxMsg.address + i) != i)
                status = false;
        }
    }

    //
    // Send response message
    //
    TxMsg.command = IPC_CMD_RESP;
    TxMsg.address = (uint32_t)CMData;
    TxMsg.dataw1  = 10;
    TxMsg.dataw2  = 1;

    IPC_sendMessageToQueue(IPC_CM_L_CPU1_R, &messageQueue, IPC_ADDR_CORRECTION_ENABLE,
                           &TxMsg, IPC_NONBLOCKING_CALL);

    //
    // Acknowledge the flag
    //
    IPC_ackFlagRtoL(IPC_CM_L_CPU1_R, IPC_FLAG1);
}

//
// Main
//
void main(void)
{
    //
    // Initialize device clock and peripherals
    //
    CM_init();

    //
    // Clear any IPC flags if set already
    //
    IPC_clearFlagLtoR(IPC_CM_L_CPU1_R, IPC_FLAG_ALL);

    //
    // Enable IPC interrupts
    //
    IPC_registerInterrupt(IPC_CM_L_CPU1_R, IPC_INT1, IPC_ISR1);

    //
    // Initialize message queue
    //
    IPC_initMessageQueue(IPC_CM_L_CPU1_R, &messageQueue, IPC_INT1, IPC_INT1);

    //
    // Synchronize both the cores.
    //
    IPC_sync(IPC_CM_L_CPU1_R, IPC_FLAG31);

    int i;
    for(i=0; i<10; i++)
    {
        CMData[i] = i+11;
    }

    //
    // Loop forever. Wait for IPC interrupt
    //
    while(1);
}


//
// End of File
//

//#############################################################################
//
// FILE:   ipc_ex2_msgqueue_c28x1.c
//
// TITLE:  IPC example with interrupt and message queue
//
//! \addtogroup driver_cm_c28x_dual_example_list
//! <h1> IPC message passing example with interrupt and message queue </h1>
//!
//! This example demonstrates how to configure IPC and pass information from
//! C28x to CM core with message queues.
//! It is recommended to run the C28x1 core first, followed by the CM core.
//!
//! \b External \b Connections \n
//!  - None.
//!
//! \b Watch \b Variables \n
//!  - pass
//!
//
//#############################################################################
// $Copyright:
// Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//###########################################################################

//
// Included Files
//
#include "driverlib.h"
#include "device.h"

//
// Defines
//
#define IPC_CMD_READ_MEM   0x1001
#define IPC_CMD_RESP       0x2001

#define TEST_PASS          0x5555
#define TEST_FAIL          0xAAAA


#pragma DATA_SECTION(CPU1Data, "MSGRAM_CPU_TO_CM")
uint32_t CPU1Data[10];

IPC_MessageQueue_t messageQueue;
IPC_Message_t      TxMsg, RxMsg;

uint32_t pass;

__interrupt void IPC_ISR1()
{
    int i;
    bool status = false;

    //
    // Read the message from the message queue
    //
    IPC_readMessageFromQueue(IPC_CPU1_L_CM_R, &messageQueue, IPC_ADDR_CORRECTION_ENABLE,
                             &RxMsg, IPC_NONBLOCKING_CALL);

    if(RxMsg.command == IPC_CMD_RESP)
    {
        status = true;

        for(i=0; i<RxMsg.dataw1; i++)
        {
           if((*(uint32_t *)RxMsg.address + i) != i+11)
                       status = false;
        }
    }


    //
    // Send response message
    //
    TxMsg.command = IPC_CMD_READ_MEM;
    TxMsg.address = (uint32_t)CPU1Data;
    TxMsg.dataw1  = 10;  // Using dataw1 as data length
    TxMsg.dataw2  = 1;   // Message identifier

    IPC_sendMessageToQueue(IPC_CPU1_L_CM_R, &messageQueue, IPC_ADDR_CORRECTION_ENABLE,
                           &TxMsg, IPC_NONBLOCKING_CALL);

    //
    // Acknowledge the flag
    //
    IPC_ackFlagRtoL(IPC_CPU1_L_CM_R, IPC_FLAG1);

    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP11);
}

//
// Main
//
void main(void)
{
    int i;


    //
    // Initialize device clock and peripherals
    //
    Device_init();

    //
    // Boot CM core
    //
#ifdef _FLASH
    Device_bootCM(BOOTMODE_BOOT_TO_FLASH_SECTOR0);
#else
    Device_bootCM(BOOTMODE_BOOT_TO_S0RAM);
#endif

    //
    // Initialize PIE and clear PIE registers. Disables CPU interrupts.
    //
    Interrupt_initModule();

    //
    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    //
    Interrupt_initVectorTable();

    //
    // Clear any IPC flags if set already
    //
    IPC_clearFlagLtoR(IPC_CPU1_L_CM_R, IPC_FLAG_ALL);

    //
    // Enable IPC interrupts
    //
    IPC_registerInterrupt(IPC_CPU1_L_CM_R, IPC_INT1, IPC_ISR1);

    //
    // Initialize message queue
    //
    IPC_initMessageQueue(IPC_CPU1_L_CM_R, &messageQueue, IPC_INT1, IPC_INT1);

    //
    // Synchronize both the cores
    //
    IPC_sync(IPC_CPU1_L_CM_R, IPC_FLAG31);

    //
    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
    //
    EINT;
    ERTM;

    //
    // Fill in the data to be sent
    //
    for(i=0; i<10; i++)
    {
        CPU1Data[i] = i;
    }

    //
    // Update the message
    //
    TxMsg.command = IPC_CMD_READ_MEM;
    TxMsg.address = (uint32_t)CPU1Data;
    TxMsg.dataw1  = 10;  // Using dataw1 as data length
    TxMsg.dataw2  = 1;   // Message identifier

    //
    // Send message to the queue
    // Since C28x and CM does not share the same address space for shared RAM,
    // ADDRESS_CORRECTION is enabled
    //
    IPC_sendMessageToQueue(IPC_CPU1_L_CM_R, &messageQueue, IPC_ADDR_CORRECTION_ENABLE,
                           &TxMsg, IPC_NONBLOCKING_CALL);

    //
    // End of example. Loop forever
    //
    while(1);
}


//
// End of File
//

Modifications:

1. I continuously send messages to CPU1 in the while loop of CM4 core, and found that CPU1 cannot repeatedly enter the interrupt. What is the reason?

ipc_ex2_msgqueue_cm.c

    //
    // End of example. Loop forever
    //
    while(1)
    {
        //
        // Send response message
        //
        TxMsg.command = IPC_CMD_RESP;
        TxMsg.address = (uint32_t)CMData;
        TxMsg.dataw1  = 10;
        TxMsg.dataw2  = 1;
    
        IPC_sendMessageToQueue(IPC_CM_L_CPU1_R, &messageQueue, IPC_ADDR_CORRECTION_ENABLE,
                               &TxMsg, IPC_NONBLOCKING_CALL);
    }

2. I would like to ask how to understand the blocking and non-blocking methods of message queues? That is to say, I don't understand the two ways of IPC_NONBLOCKING_CALL and IPC_BLOCKING_CALL according to what circumstances to choose?

Thanks & Regards,

Ben

  • Hi,

    Is the ISR triggered once, but not again? I'll try to give more information about the way IPC Message Queue works tomorrow. 

    Best Regards,

    Ben Collier

  • Hi Benjamin,

    Is there any updates?

    Best Regards,

    Ben

  • Hello Ben,

    Benjamin is currently out-of-office, but he should be able to respond to you by Wednesday of next week. Apologies for the delay.

    Best regards,

    Omer Amir

  • Hi Ben,

    I apologize for the delay. Were you able to confirm if the customer was able to enter the ISR at least once? 

    About the 'block' argument, I think the best way to understand it would be to read the IPC_sendMessageToQueue() and IPC_readMessageFromQueue() functions in ipc.c : 

     

    It looks like a nonblocking call will make the IPC_sendMessageToQueue() function fail immediately if the put buffer is full. The blocking argument will make the function wait until a put buffer slot is free. 

    I think what this means in practice is that the IPC_sendMessageToQueue() cannot be used multiple times without IPC_readMessageFromQueue(), or else the queue will be full. A blocking call will wait (possibly forever) for the queue to have space, while the nonblocking call will fail immediately when it sees that the queue is full. 

    Best Regards,

    Ben Collier

  • Hi Benjamin,

    Thanks for your reply!

    Were you able to confirm if the customer was able to enter the ISR at least once? 

    Yes, but only able to enter the first ISR.

    Best Regards,

    Ben

  • Ben,

    Is the customer able to check the contents of the put buffer for the cm's message Queue in their memory browser, after the ISR has already been triggerred once? Also are they able to step into their IPC_sendMessageToQueue() function to see what is going on inside the function? Are they making it past the while loop in my above screenshot? Is anything different between the first time the function runs and subsequent times? 

    Best Regards,

    Ben Collier

  • Hi Benjamin,

    I now only do cm to send ipc messages, and cpu1 interrupts to read the message, looking at writeIndex and readIndex by breaking points on both cores together.

    Verification process:

    1. After CM executes the IPC_sendMessageToQueue() function for the first time, CPU1 enters the ipc interrupt but does not IPC_readMessageToQueue(). 

    As you can see in the above two figures, both PutWriteIndex of cm and GetWriteIndex of cpu1 add 1.

    2. After CM executes the IPC_sendMessageToQueue() function for the first time, CPU1 enters the ipc interrupt and executes IPC_readMessageToQueue(). 

    When cpu1 executes IPC_readMessageToQueue(), both cm's PutReadIndex and cpu1's GetReadIndex are incremented by 1

    From the above two verification results, the message queue of IPC can be executed repeatedly, and there is no problem with the code.

    The reason why I couldn't repeat the interrupt before was that I only set a breakpoint in the ipc interrupt of cpu1, and CM did not set a breakpoint, which caused CM to keep sending IPC messages, and cpu1 did not read until PutBuffer was full and executed in the while loop of the IPC_sendMessageToQueue() function, so cpu1 could not repeatedly enter the interrupt to read messages.

    For blocking and non-blocking calls, if the sender uses a blocking call to continuously send data and the receiver uses a blocking reception, when the PutBuffer of the sender is full, the sender and receiver will enter a While loop causing both to work unnormally, and if one of the ends uses a non-blocking call, this end can continue to perform other tasks. Is this my understanding correct?

    Best Regards,

    Ben

  • Hi Ben,

    Your understanding is correct. Please let me know if you have any more issues.

    Best Regards,

    Ben Collier