Dual Core Debugging Hints on LPC43XX

Comments

6 comments

  • Avatar
    Philip Ouellette

    Slight modification. Just stop debugging after running the debugger on the M4 processor. Then set the M0 as the active project. This will automatically disconnect the debugger from the M4 core.

    Then attach the debugger (twice) to debug the M0 app.

    0
    Comment actions Permalink
  • Avatar
    Philip Ouellette

    Follow up. This dual core functionality really rocks. I have the M0 core accepting individual data measurements at a 5000 Hz rate and sending groups of measurements to the M4 core for further processing. NXP is really on to something with this multi-core model.

    0
    Comment actions Permalink
  • Avatar
    Bill Fleming

    Any chance you can post the project files and some code examples for this?

    0
    Comment actions Permalink
  • Avatar
    Philip Ouellette

    That will take some time. I would have to create a pair of dummy hello world projects since my code is proprietary and project creation is somewhat complicated. Perhaps after I get past the next deliverable I am will look at doing this.

    Hints:

    • Create a solution with two projects inside it. I happen to be using Rowley Crossworks ARM.
    • The M4 project is the IPC Host and is a normal project that runs from Page A. The M4 project must be the active project when you compile and load code into the processors flash memory
    • The M0 project is the IPC Client as is treated as an additional load item into Page B when the M4 project is loaded.
    • Inter processor communication is via shared memory buffers. A 2K Byte buffer for each core to write into and the other core only reads from the other cores buffer.

    Code snippets below:

    Note: The app is running on top of the uEZ-GUI SDK from Future Design Inc, so there is some specific stuff for their environment. I was also having issues with giving semaphores before they had been taken (resulting in hard faults). That is why I check to see if the previous semaphore has been consumed before giving it again.

    The IPC.c file is part of both the M4 and the M0 project to manage communication.

    /*-------------------------------------------------------------------------*
    * File: IPC.c Copyright (C) 2018 Interface Inc
    *-------------------------------------------------------------------------*
    * Description:
    *
    *-------------------------------------------------------------------------*/

    /*--------------------------------------------------------------------------
    * uEZ(R) - Copyright (C) 2007-2018 Future Designs, Inc.
    *--------------------------------------------------------------------------
    * This file is part of the uEZ(R) distribution. See the included
    * uEZLicense.txt or visit http://www.teamfdi.com/uez for details.
    *
    * *===============================================================*
    * | Future Designs, Inc. can port uEZ(R) to your own hardware! |
    * | We can get you up and running fast! |
    * | See http://www.teamfdi.com/uez for more details. |
    * *===============================================================*
    *
    *-------------------------------------------------------------------------*/

    /*-------------------------------------------------------------------------*
    * Includes:
    *-------------------------------------------------------------------------*/
    #if CORE_M0
    #else
    #include <uEZ.h>
    #endif
    #include <string.h>
    #include <UEZProcessor.h>
    #include <HAL/HAL.h>
    #include <HAL/Interrupt.h>
    #include "IPC.h"
    #include <uEZRTOS.h>
    #if (UEZ_PROCESSOR == NXP_LPC4357)
    #include <Source/Processor/NXP/LPC43xx/LPC43xx_GPIO.h>
    #else
    #error "MCU not set!"
    #endif
    #include <HAL/GPIO.h>
    #include "FreeRTOS.h"
    #include "task.h"
    #include "semphr.h"


    /*-------------------------------------------------------------------------*
    * Constants:
    *-------------------------------------------------------------------------*/

    #define IPC_BUFFER_SIZE  512
    #define M4_MESSAGE_RECEIVE_ADDR 0x10004000
    #define M4_MESSAGE_SEND_ADDR 0x10004800

    #define M0_MESSAGE_RECEIVE_ADDR M4_MESSAGE_SEND_ADDR
    #define M0_MESSAGE_SEND_ADDR M4_MESSAGE_RECEIVE_ADDR

    #define MESSAGE_CLEAR_VALUE 0xFF

    #define IPC_RETURN_VALUE_NONE 0
    #define IPC_RETURN_VALUE_BUSY 1
    #define IPC_RETURN_VALUE_TOO_LONG 2


    /*-------------------------------------------------------------------------*
    * Types:
    *-------------------------------------------------------------------------*/
    typedef struct {
    TUInt32 iSize;
    TUInt32 iData[IPC_BUFFER_SIZE];
    }T_IPC_Message;

    /*-------------------------------------------------------------------------*
    * Globals:
    *-------------------------------------------------------------------------*/
    //Note each core will create its own version of each of these global variables
    static T_IPC_Message *G_Send;
    static T_IPC_Message *G_Receive;
    T_uezSemaphore G_MessageSem = 0;
    static TBool volatile G_MessageSem_taken = EFalse;

    //Data used by each cores app.
    struct IPC_MESSAGE IPC_RX_Msg;
    struct IPC_MESSAGE IPC_TX_Msg;

    //static TaskHandle_t xHandlerTask = NULL;


    /*-------------------------------------------------------------------------*
    * Prototypes:
    *-------------------------------------------------------------------------*/
    #if CORE_M0
    void Client_IPC_RxD(void);
    #else
    void Host_IPC_RxD(void);
    #endif
    /*---------------------------------------------------------------------------*
    * Routine: Interrupt routines for processor communication
    *---------------------------------------------------------------------------*/
    /** Handle interrupts from the other core
    *
    * @param [in] p_choice void
    */
    /*---------------------------------------------------------------------------*/
    #if CORE_M0
    IRQ_ROUTINE(IM0_M4CORE_IRQn)
    {
    LPC_CREG->M4TXEVENT = 0;
    if(G_Receive->iSize == 0xFFFFFFFF)
    {
    //Invalid message
    return;
    }

    //store the buffer data
    IPC_RX_Msg.iSize = G_Receive->iSize;
    memcpy(IPC_RX_Msg.iData, G_Receive->iData, IPC_RX_Msg.iSize);
    G_Receive->iSize = 0xFFFFFFFF;
    //Let app know the data is here
    G_MessageSem_taken = ETrue;
    }
    #else
    IRQ_ROUTINE(IM0APP_IRQ)
    {
    LPC_CREG->M0APPTXEVENT = 0;
    if(G_Receive->iSize == 0xFFFFFFFF)
    {
    //Invalid message
    return;
    }
    //store the buffer data
    IPC_RX_Msg.iSize = G_Receive->iSize;
    memcpy(IPC_RX_Msg.iData, G_Receive->iData, IPC_RX_Msg.iSize);
    G_Receive->iSize = 0xFFFFFFFF;

    //Let app know the data is here
    G_MessageSem_taken = ETrue;
    }
    #endif


    static TUInt32 IPC_RX_MsgTask(T_uezTask aMyTask, void *aParams)
    {
    //uint32_t ulEventsToProcess;

    while(1)
    {
    // G_MessageSem_taken = ETrue;
    // if(UEZSemaphoreGrab(G_MessageSem, UEZ_TIMEOUT_INFINITE) == UEZ_ERROR_NONE)
    while (G_MessageSem_taken == EFalse)
    {
    UEZTaskDelay(1);
    }
    G_MessageSem_taken = EFalse;
    {
    //Do something with the data in IPC_RX_Msg
    //Copy it to a ring buffer for the application to use
    #if CORE_M0
    //Process_M0_data();
    Client_IPC_RxD();
    #else
    Host_IPC_RxD();
    #endif
    }
    }
    return 0;
    }

     

    TUInt32 IPC_SendMessage(struct IPC_MESSAGE * ipc_msg)
    {
    TUInt32 return_value = IPC_RETURN_VALUE_NONE;

    if (G_Send->iSize != 0xFFFFFFFF)
    {
    //Previous message has not been read yet
    return_value = IPC_RETURN_VALUE_BUSY;
    }
    else if (ipc_msg->iSize > (sizeof(TUInt32) * IPC_BUFFER_SIZE))
    {
    //Message sent is too long. This should never happen, but check anyways
    return_value = IPC_RETURN_VALUE_TOO_LONG;
    }
    else
    {
    //Load shared variables with message
    G_Send->iSize = ipc_msg->iSize;
    memcpy(G_Send->iData, ipc_msg->iData, G_Send->iSize);
    //Trigger the interrupt in the other core
    __SEV();
    }
    return return_value;
    }


    /*---------------------------------------------------------------------------*
    * Routine: IPC_Init
    *---------------------------------------------------------------------------*/
    /** Setup the IPC memory and interrupts
    *
    * @param [in] p_choice void
    */
    /*---------------------------------------------------------------------------*/
    void IPC_Init(void)
    {
    static TBool haveRun = EFalse;

    if(!haveRun){
    haveRun = ETrue;

    #if CORE_M0
    G_Send = (T_IPC_Message*)M0_MESSAGE_SEND_ADDR;
    G_Receive = (T_IPC_Message*)M0_MESSAGE_RECEIVE_ADDR;
    #else
    G_Send = (T_IPC_Message*)M4_MESSAGE_SEND_ADDR;
    G_Receive = (T_IPC_Message*)M4_MESSAGE_RECEIVE_ADDR;
    #endif
    IPC_RX_Msg.iSize = 0xFFFFFFFF;
    IPC_TX_Msg.iSize = 0xFFFFFFFF;

    memset((void*)G_Send, MESSAGE_CLEAR_VALUE, sizeof(T_IPC_Message));
    memset((void*)G_Receive, MESSAGE_CLEAR_VALUE, sizeof(T_IPC_Message));

    UEZSemaphoreCreateBinary(&G_MessageSem);
    UEZSemaphoreGrab(G_MessageSem, 0);
    UEZTaskCreate((T_uezTaskFunction)IPC_RX_MsgTask, "IPC_RXTask",UEZ_TASK_STACK_BYTES(1024), (void *)0, UEZ_PRIORITY_NORMAL, 0);


    #if CORE_M0
    InterruptRegister(M0_M4CORE_IRQn, IM0_M4CORE_IRQn, INTERRUPT_PRIORITY_HIGH, "M4 App");
    NVIC_EnableIRQ (M0_M4CORE_IRQn);
    #else
    InterruptRegister(M0APP_IRQn, IM0APP_IRQ, INTERRUPT_PRIORITY_HIGH, "M0 App");
    NVIC_EnableIRQ (M0APP_IRQn);
    #endif
    }
    }

     

    Finally: This is how the M4 app starts the M0 code running. Add this to your M4 startup initialization code.

    /* Set start address for CM0 application */
    LPC_CREG->M0APPMEMMAP = 0x1B000000;

    /* Start CM0 core */
    LPC_RGU->RESET_CTRL1 = 0; // in principle just set bit 24 to 0, but doing it with the whole register doesn't hurt

    IPC_Init();

     

     

    0
    Comment actions Permalink
  • Avatar
    Bill Fleming

    Philip,

    So I just realized I already have access to your project anyway. (small world)

    After you guys are done with this update, and after we release uEZ 2.10, then I can look at putting out an example hello world project for this later using our project maker if we have time. I'm still in the middle of making normal fixes and updates to some of the projects for the current release.

    Bill

    0
    Comment actions Permalink
  • Avatar
    Philip Ouellette

    Right. Talk to Christopher, he did the heavy lifting at getting dual core working on uEZ-Gui.

    0
    Comment actions Permalink

Please sign in to leave a comment.