How to program multi-core microcontrollers



Over the past ten years, the processors in almost all of our devices have become multi-core. They are followed by multi-core microcontrollers. In the catalogs of major manufacturers, you can already find general-purpose microcontrollers with several cores at reasonable prices. So it looks like it's time to start using multi-core microcontrollers in your own devices. Naturally, in cases where it is justified.



. , . , . . .



-. ARM Cortex M NXP (LPC), STMicroelectronics Infineon (Cypress).





, .



  • . , , . - , , .
  • . ( , , ), . , ( Cortex M0) (, Cortex M4). , . , .
  • . , . , . , , . .




.



. . . .



, (Master Core). . , (Slave Core), , reset. . System Control ( ).





, . . .



, , . , (Cortex M0), , (Cortex M4 / M7).





, . , – , . . . , .





, . (, LPC55S6x Cypress CY8C6xx5) . . (bus matrix). , . , , .







, STM32H747. , . , . .







, . , . , , . , , .



, , . , .



. . () FLASH- SRAM. . .



, (AHB1, AHB2, APB1, APB2…). , , «» .







(NVIC). . , , , NVIC. , NVIC . , : , , / . , , . , GPIO , , , .







. -. STM32 Hardware Semaphore (HSEM), NXP – Inter-CPU mailbox, Cypress – Inter-Processor Communication Block (IPC). , .



. Β« Β» (mailbox). , . NVIC . , , , - ( ).





– , . . . , , . , . , . , , . , . , . .





. .





. (Debug Access Port). - . ( ) . . .







. , , .



LPC55S69-EVK NXP LPC55S69, Cortex M33. IAR Embedded Workbench 8. , , . , .





CMSIS. . , .



0



. CPU0.



.



  1. C: Project > Create New Project. C / main. , 0, . CPU0.
  2. : File > Save Workspace.
  3. General Options > Target .

    : , , .

  4. General Options > Library Configuration Use CMSIS.
  5. (C/C++ Compiler > Preprocessor) : < SDK>\devices\LPC55S69. Defined symbols CPU_LPC55S69JBD100_cm33_core0 ( , ).
  6. Linker > Config Override default LPC55S69_cm33_core0_flash.icf SDK ( < SDK>\devices\LPC55S69\iar).
  7. Debugger > Setup (Driver) CMSIS DAP.
  8. , Debugger > Download Verify Download Use flash loader(s).
  9. system_LPC55S69_cm33_core0.c startup_LPC55S69_cm33_core0.s ( < SDK>\devices\LPC55S69 < SDK>\devices\LPC55S69\iar ). , .
  10. , GPIO1_7.





    #include "LPC55S69_cm33_core0.h"
    
    #define LED_GPIO_BLOCK 1
    #define LED_GPIO_PIN 7
    
    static inline void Delay(void)
    {
     for(int i = 0; i < 8000000; i++);
    }
    
    int main()
    {  
     /**** LED setup ****/
     // Enable clocking for GPIO1 (located on AHB bus)
     SYSCON->AHBCLKCTRLX[0] |= SYSCON_AHBCLKCTRL0_GPIO1_MASK;
    
     // Set direction for GPIO1 7 as output
     GPIO->DIR[LED_GPIO_BLOCK] |= GPIO_DIR_DIRP(1 << LED_GPIO_PIN);
    
     while(1)
     {
       /**** LED blinking ****/
       GPIO->B[LED_GPIO_BLOCK][LED_GPIO_PIN] = 0;  // Turn led On
       Delay();
       GPIO->B[LED_GPIO_BLOCK][LED_GPIO_PIN] = 1;  // Turn led off
       Delay();
     }
    }


  11. , , , .


1



, . . , .



, 3, 5, 6, 9, 10 core0 core1.



, , , GPIO1_4:



#define LED_GPIO_PIN 7  -->  #define LED_GPIO_PIN 4


. … Reading CPU status failed.





, , LPC55S69 . 0 , 1 β€” . 1 0 :



  • 1.
  • , 1 .
  • 1.
  • reset 1.


0 :





// Sequence required to be written
// to SYSCON->CPUCTRL register ORed with other bits
// to applay settings
// See UM11126 4.5.70 for more details
#define SYSCON_CPUCTRL_ENABLING_SEQ 0xC0C40000

// Warning!!! IAR specific code. 
// core1_image_start is defined in 
// LPC55S69_cm33_core0_flash.icf linker config file
extern unsigned char core1_image_start[];
#define CORE1_IMAGE_START core1_image_start


main() 1:



/**** CPU1 (slave) setup ****/
// Enable CPU1
SYSCON->CPUCFG |= SYSCON_CPUCFG_CPU1ENABLE_MASK;

// Setup firmware boot address
SYSCON->CPBOOT = SYSCON_CPBOOT_CPBOOT(CORE1_IMAGE_START);

// Enable clock and reset CPU1
SYSCON->CPUCTRL = SYSCON->CPUCTRL | SYSCON_CPUCTRL_ENABLING_SEQ | 
                  SYSCON_CPUCTRL_CPU1RSTEN_MASK | SYSCON_CPUCTRL_CPU1CLKEN_MASK;

// Release reset for CPU1
SYSCON->CPUCTRL = (SYSCON->CPUCTRL | SYSCON_CPUCTRL_ENABLING_SEQ) & 
                  (~SYSCON_CPUCTRL_CPU1RSTEN_MASK);


, 0.



1 . .





. . .



, :



  1. ( 1).
  2. . Converter > Output, Generate Additional Output Raw binary.

  3. Debugger > Download Verify Download. .

  4. .
  5. ( 0).
  6. Debugger > Multicore, Simple .



  7. Linker > Input . ( ). _CPU1_image, , 1, __sec_core 4.



  8. . , . , .







    IAR , .







Failed to synch with multicore partner, . CPU1 CPU0.







The debugging session could not be started.
Either the debugger initialization filed, or else the file "iar_all_modules_loaded" was missing, corrupt or of an unsupported format.




, Verify download 3. , , .



: , , , , . . , 6 7. ( , ), , , . , .



SRAM



FLASH-. . . SRAM. FLASH-, SRAM . :



  1. ( 1).
  2. (Linker > Config) LPC55S69_cm33_core1_flash.icf LPC55S69_cm33_core1_ram.icf.
  3. .
  4. ( 0).
  5. main.c:



    • #include "string.h" // for memcpy.
    • SRAM, 1.



      // Address of RAM, where the image for CPU1 should be copied
      // According to UM11126 2.1.5 it is SRAM 3 on CM33 data bus
      #define CORE1_BOOT_ADDRESS (void *)0x20033000
    • .



      // Firmware image size calculation
      uint32_t get_core1_image_size(void)
      {
        uint32_t core1_image_size;
      #pragma section = "__sec_core"
        core1_image_size = (uint32_t)__section_end("__sec_core") - 
                           (uint32_t)&core1_image_start;
        return core1_image_size;
      }
  6. 1 FLASH- SRAM.



    //  Copy CPU1 image to an other SRAM block
    memcpy(CORE1_BOOT_ADDRESS, (void *)CORE1_IMAGE_START, get_core1_image_size());
  7. ( , ).



    // Setup boot address
    SYSCON->CPBOOT = SYSCON_CPBOOT_CPBOOT(CORE1_BOOT_ADDRESS);
  8. 0, , .


, ( 2 7 ).





, . - MAILBOX.



MAILBOX, . .



/**** Mailbox setup for mutex ****/
// Enable mailbox clocking
SYSCON->AHBCLKCTRLX[0] |=  SYSCON_AHBCLKCTRL0_MAILBOX_MASK;


- , , .



β€” , . .



// Try to acquire mutex
while (!(MAILBOX->MUTEX & MAILBOX_MUTEX_EX_MASK));


.



// Release mutex
MAILBOX->MUTEX = MAILBOX_MUTEX_EX_MASK;


.



, .





.



0 ( )
#include "LPC55S69_cm33_core0.h"
#include "string.h"  // for memcpy

#define LED_GPIO_BLOCK 1
#define LED_GPIO_PIN 7

// Address of RAM, where the image for CPU1 should be copied
// According to UM11126 2.1.5 it is SRAM 3 on CM33 data bus
#define CORE1_BOOT_ADDRESS (void *) 0x20033000

// Sequence required to be written
// to SYSCON->CPUCTRL register ORed with other bits
// to applay settings
// See UM11126 4.5.70 for more details
#define SYSCON_CPUCTRL_ENABLING_SEQ 0xC0C40000

// Warning!!! IAR specific code. 
// core1_image_start is defined in 
// LPC55S69_cm33_core0_flash.icf linker config file
extern unsigned char core1_image_start[];
#define CORE1_IMAGE_START core1_image_start

static inline void Delay(void)
{
  for(int i = 0; i < 8000000; i++);
}

// Firmware image size calculation
uint32_t get_core1_image_size(void)
{
    uint32_t core1_image_size;
#pragma section = "__sec_core"
    core1_image_size = (uint32_t)__section_end("__sec_core") - 
                       (uint32_t)&core1_image_start;
    return core1_image_size;
}

int main()
{  
  /**** LED setup ****/
  // Enable clocking for GPIO1 (located on AHB bus)
  SYSCON->AHBCLKCTRLX[0] |= SYSCON_AHBCLKCTRL0_GPIO1_MASK;

  // Set direction for GPIO1 7 as output
  GPIO->DIR[LED_GPIO_BLOCK] |= GPIO_DIR_DIRP(1 << LED_GPIO_PIN);

  /**** Mailbox setup for mutex ****/
  // Enable mailbox clocking
  SYSCON->AHBCLKCTRLX[0] |=  SYSCON_AHBCLKCTRL0_MAILBOX_MASK;

  /**** CPU1 (slave) setup ****/
  //  Copy CPU1 image to an other SRAM block
  memcpy(CORE1_BOOT_ADDRESS, (void *)CORE1_IMAGE_START, get_core1_image_size());

  // Enable CPU1
  SYSCON->CPUCFG |= SYSCON_CPUCFG_CPU1ENABLE_MASK;

  // Setup firmware boot address
  SYSCON->CPBOOT = SYSCON_CPBOOT_CPBOOT(CORE1_BOOT_ADDRESS);

  // Enable clock and reset CPU1
  SYSCON->CPUCTRL = SYSCON->CPUCTRL | SYSCON_CPUCTRL_ENABLING_SEQ | 
                    SYSCON_CPUCTRL_CPU1RSTEN_MASK | SYSCON_CPUCTRL_CPU1CLKEN_MASK;

  // Release reset for CPU1
  SYSCON->CPUCTRL = (SYSCON->CPUCTRL | SYSCON_CPUCTRL_ENABLING_SEQ) & 
                    (~SYSCON_CPUCTRL_CPU1RSTEN_MASK);

  while(1)
  {
    /**** LED blinking ****/
    while (!(MAILBOX->MUTEX & MAILBOX_MUTEX_EX_MASK));  // Try to acquire mutex
    GPIO->B[LED_GPIO_BLOCK][LED_GPIO_PIN] = 0;          // Turn led On

    Delay();

    GPIO->B[LED_GPIO_BLOCK][LED_GPIO_PIN] = 1;  // Turn led off
    MAILBOX->MUTEX = MAILBOX_MUTEX_EX_MASK;     // Release mutex

    Delay();
  }
}


1 ( )
#include "LPC55S69_cm33_core1.h"

#define LED_GPIO_BLOCK 1
#define LED_GPIO_PIN 4

static inline void Delay(void)
{
  for(int i = 0; i < 8000000; i++);
}

int main()
{  
  /**** LED setup ****/
  // Enable clocking for GPIO1 (located on AHB bus)
  SYSCON->AHBCLKCTRLX[0] |= SYSCON_AHBCLKCTRL0_GPIO1_MASK;

  // Set direction for GPIO1 7 as output
  GPIO->DIR[LED_GPIO_BLOCK] |= GPIO_DIR_DIRP(1 << LED_GPIO_PIN);

  /**** Mailbox setup for mutex ****/
  // Enable mailbox clocking
  SYSCON->AHBCLKCTRLX[0] |=  SYSCON_AHBCLKCTRL0_MAILBOX_MASK;

  while(1)
  {
    /**** LED blinking ****/
    while (!(MAILBOX->MUTEX & MAILBOX_MUTEX_EX_MASK)); //  Try to acquire mutex
    GPIO->B[LED_GPIO_BLOCK][LED_GPIO_PIN] = 0;         // Turn led On

    Delay();

    GPIO->B[LED_GPIO_BLOCK][LED_GPIO_PIN] = 1;  // Turn led off
    MAILBOX->MUTEX = MAILBOX_MUTEX_EX_MASK;     // Release mutex

    Delay();
  }
}


GitHub.





:






All Articles