Search This Blog

Wednesday, September 16, 2015

Testing Dual-Core Microcontroller - LPC54102

LPC54102J512 MCU မှာ Cortex M4 Master တစ်ခုနဲ့ Cortex M0+ Slave ဆိုပြီး core နှစ်ခုပါပါတယ်။ LPCXpresso IDE v7.9.0 ကိုသုံးပြီး multicore systems တစ်ခုကို ဖန်တီးတာ၊ debug လုပ်တာတွေကို နမူနာ တစ်ခုရေးပြီး စမ်းကြည့်တဲ့ အတွေ့အကြုံကို ပြောချင်ပါတယ်။ LPCOpen ကို ဒေါင်းလုပ်လုပ်ထားပြီး Quickstart -> Import project(s) ကို နှိပ်ပြီး import လုပ် ထားဖို့လိုပါတယ်။




Figure. OM13077 Board with LCP54102 Microcontroller.

Creating a Slave Project

Slave project တစ်ခုကို ဖန်တီးဖို့ Quickstart -> New project -> LPC54100 -> LPC5410x Multicore (M0+ slave) -> LPCOpen - C Project ကို ရွေးပြီး Next ခလုပ်ကို နှိပ်ပါမယ်။


Figure. Creating a slave M0+ project.

Project name ပေးဖို့တောင်းတဲ့ အခါ နမူနာအနေနဲ့ DualCore_Slave လို့ ပေးလိုက်ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါတယ်။
Target MCU ကို ရွေးဖို့ ပြောတဲ့ အခါ ကျွန်တော့် chip ပေါ်မှာ ရေးထားတဲ့ အတိုင်း LPC54102J512 အတွက် M0 ကို ရွေးပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါတယ်။
LPCOpen Library Project Selection လုပ်ခိုင်းတဲ့ အခါ lpc_chip_5410x_m0 ရော၊ lpc_board_lpcxpresso_54102_m0 ရော ကို ရွေးလိုက်ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။


Figure. Library selection.

CMSIS DSP Library Project Selection မှာတော့ None ကိုပဲရွေးပြီး Next ခလုပ်ကို ထပ်နှိပ်နိုင်ပါတယ်။
Other options မှာ Code Read Protect (CRP) ကိုမှတ် မထားဖို့ (untick) သတိပြုပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။
Memory Configuration Editor မှာ M0 သုံးဖို့အတွက် Ram1_32 တစ်ခုပဲ ချန်ထားပြီး ကျန်တာတွေကို delete လုပ်ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။


Figure. Memory Configuration.

ပြီးရင် Finish ခလုပ်ကို ထပ်နှိပ်လိုက်ရင် Slave project ဖန်တီးတာ ပြီးပါပြီ။

Creating a Master Project

Master project တစ်ခုကို ဖန်တီးဖို့ Quickstart -> New project -> LPC54100 -> LPC5410x Multicore (M4 Master) -> LPCOpen - C Project ကို ရွေးပြီး Next ခလုပ်ကို နှိပ်ပါမယ်။


Figure. Creating a master M4 project.

Project name ပေးဖို့တောင်းတဲ့ အခါ နမူနာအနေနဲ့ DualCore_Master လို့ ပေးလိုက်ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါတယ်။
Target MCU ကို ရွေးဖို့ ပြောတဲ့ အခါ LPC54102J512 ကို ရွေးပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါတယ်။
LPCOpen Library Project Selection လုပ်ခိုင်းတဲ့ အခါ lpc_chip_5410x ရော၊ lpc_board_lpcxpresso_54102 ရော ကို ရွေးလိုက်ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။
CMSIS DSP Library Project Selection မှာတော့ None ကိုပဲရွေးပြီး Next ခလုပ်ကို ထပ်နှိပ်နိုင်ပါတယ်။
Disabled RAM options မှာ သူ့မူလအတိုင်း Enable RAM banks disabled by default ကို မှတ် (tick) ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။
SWV Trace options မှာလဲ သူ့မူလအတိုင်း Enable SWV trace clock ကို မှတ် (tick) ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။
Part specific options မှာလဲ သူ့မူလအတိုင်း Enabled_SoftABI ထားပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။
Other options မှာတော့ Code Read Protect (CRP) ကိုမှတ် (tick) ပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။
Memory Configuration Editor မှာလဲ သူ့မူလအတိုင်း လေးခုလုံး ဒီအတိုင်းထားပြီး Next ခလုပ်ကို ထပ်နှိပ်ပါမယ်။
Multicore Project Setup မှာ slave project ကို browse ခလုပ်နှိပ်ပြီး ခုနက ဖန်တီးထားတဲ့ DualCore_Slave ကိုရွေးပါမယ်။
ပြီးရင် Printf options မှာ Finish ခလုပ်ကို ထပ်နှိပ်လိုက်ရင် Master project ဖန်တီးတာ ပြီးပါပြီ။

နောက် Project Explorer ထဲက DualCore_Master ကို select လုပ်ပြီး၊ Quickstart panel ထဲက Build 'DualCore_Master'ကို နှိပ်လိုက်ရင်၊ slave project ကို အရင် build လုပ်တာကို တွေ့ရမှာဖြစ်ပြီး၊ master project ကိုနောက်ထပ် build လုပ်ပါမယ်။ ပြီးရင် ထွက်တာတဲ့ executable မှာ slave project ရဲ့ executable ကို ပေါင်းထည့်ပေးသွားပါတယ်။


Figure. Building executable.


Debugging

နောက် Project Explorer ထဲက DualCore_Master ကို select လုပ်ထားရင်းမှာပဲ Quickstart panel ထဲက Debug 'DualCore_Master'ကို နှိပ်လိုက်ပါမယ်။ Emulator ကိုရွေးခိုင်းရင် LPC-Link_II ကိုရွေးပြီး၊ target ကိုရွေးခိုင်းရင် Cortex-M4 ကိုရွေးလိုက်ပါမယ်။ အဲဒီအခါ Debug mode မှာ သူ့ရဲ့ default breakpoint ကို ရောက်သွားပါမယ်။


Figure. Debugging - Select emulator.




Figure. Default breakpoint.

နောက်ပြီးရင် Project Explorer ထဲက DualCore_Slave ကို select သွားလုပ်ပြီး Quickstart panel ထဲက Debug 'DualCore_Slave'ကို နှိပ်လိုက်ပါမယ်။ Emulator ကိုရွေးခိုင်းရင် LPC-Link_II ကိုရွေးပြီး၊ target ကိုရွေးခိုင်းတဲ့အခါ Cortex-M0 ကိုပဲ ရွေးစရာရှိပါတော့မယ်။


Figure. Debugging slave project.

ပြီးတဲ့အခါ LPCXpresso က အဲဒီ project ဟာ slave မှန်းသိတဲ့ အတွက် ကုဒ်ကို download လုပ်မှာမဟုတ်ပဲ attach only connection ပဲ လုပ်မှာပါ။ အပေါ်က Debug view ထဲမှာလည်း နှစ်ခု ပေါ်လာပါမယ်။

Master project ကို စပြီး debug လုပ်တဲ့အခါ boot_multicore_slave(); ဆိုတဲ့ function ကို step over လုပ်ပြီးတာနဲ့ slave project ရဲ့ default breakpoint ကို ရောက်သွားပါမယ်။ အဲဒီကစပြီး core နှစ်ခုကို ကြိုက်တဲ့ debug view ကို ကြိုက်သလို ပြောင်းရွေးပြီး ပြိုင်တူ debug လုပ်လို့ရပါပြီ။


Figure. Debugging both cores in parallel.

အပေါ်က Debug view ထဲမှာ ကိုယ် လုပ်ချင်တဲ့ project ရဲ့ Thread ကို select မှတ်ပြီး run ဒါမှမဟုတ် suspend လိုသလို လုပ်လို့ရပါတယ်။ ပြိုင်တူ run ဒါမှမဟုတ် suspend ဒါမှမဟုတ် step လုပ်ချင်ရင် Ctrl+Click နှိပ်ပြီး နှစ်ခုလုံးကို select မှတ်ထားနိုင်ပါတယ်။ နှစ်ခုလုံးကို debug လုပ်ရာကနေ ပြိုင်တူ disconnect လုပ်စေချင်ရင် 'Terminate all debug sessions' ဆိုတဲ့ခလုပ်ကို နှိပ်ပြီးရပ်နိုင်ပါတယ်။

Core-to-Core Communication

Core အချင်းချင်း ဆက်သွယ်ဖို့ အတွက် mailbox တခုတည်ဆောက် ပြီး အသုံးချနိုင်ပါတယ်။ Mailbox တည်ဆောက်ဖို့ အတွက် အောက်က ယန္တရားသုံးခုကို သုံးနိုင်ပါတယ်။
  • Core-to-core interrupts
  • Mutex register
  • Equal access to SRAM memory areas


Core တခုကနေ တခြားတခုကို interrupt ၃၂ ခုလုပ်နိုင်ပြီး အဲဒီအတွက် IRQ0 နဲ့ IRQ1 ဆိုတဲ့ 32 bit R/W register နှစ်ခုရှိပါတယ်။ သူ့တို့မှာ write only ဖြစ်တဲ့ Clear နဲ့ Set register တွေအသီးသီးရှိကြပါတယ်။ IRQ0 ရဲ့ bit တခုခုကို 1 လုပ်လိုက်ရင် Cortex-M0+ ကို interrupt လုပ်မှာဖြစ်ပြီး၊ IRQ1 ရဲ့ bit တခုခုကို 1 လုပ်ရင်တော့ Cortex-M4F ကို interrupt လုပ်ပါမယ်။


Figure. IRQx, IRQxCLR and IRQxSET registers.


Mutual exclusion အတွက် core နှစ်ခုလုံးက atomic operation နဲ့ သုံးလို့ရတဲ့ MUTEX register ရှိပါတယ်။ သူ့ထဲက တန်ဖိုးကို ဖတ်ပြီးတာနဲ့ အဲဒီ bit တန်ဖိုးက အလိုအလျှောက် 0 ဖြစ်သွားပါမယ်။ Mutex ကို ပြန်ပြီး free လုပ်ဖို့ အတွက် 1 ကို ပြန်ရေးပေးဖို့ လိုပါတယ်။
Chip ရဲ့ resources တွေကို core နှစ်ခုလုံးက ညီတူမျှတူ သုံးလို့ရတဲ့ အတွက် အထဲက SRAM ကိုသုံးပြီး အချင်းချင်း အပြန်အလှန် ဆက်သွယ်ပေးပို့လို့ရပါတယ်။ Resource တွေကို သုံးတဲ့ master တွေအချင်းချင်း အသုံးပြုချိန်တိုက်ဆိုင်တဲ့ အခါမှာတော့ AHB (Advanced High Performance Bus) Matrix ကနေဦးစားပေး အစီအစဉ် အလိုက် အသုံးပြုခွင့်ကို ဆုံးဖြတ်ပေးပါလိမ့်မယ်။

Example

နမူနာ multi-core applicaiton တခုအနေနဲ့ 50 ms ကြာတိုင်း LED မီးလုံးလေးကို အဖွင့်အပိတ်လုပ်ပေးတဲ့ ပရိုဂရမ် တခုရေးကြည့်ပါမယ်။ Master ရဲ့ Program အစမှာ TICKRATE_HZ ကို 20 လို့ define လုပ်ပြီး master ရဲ့ main ထဲမှာ system tick timer ကို config လုပ်ပါမယ်။
#define TICKRATE_HZ (20) //10 ticks per second

SysTick_Config(SystemCoreClock / TICKRATE_HZ);//Enable SysTick Timer only on the master
Timer အချိန်ပြည့်တိုင်း လုပ်မယ့် အလုပ်တွေကို အောက်ကတိုင်း list ချရေးထားပါတယ်။
  • Master core ကနေ slave core ကို လက်ရှိ LED ရဲ့ တန်ဖိုးကို Mailbox ကနေ ပို့လိုက်ပါမယ်။
  • Slave core ကနေ အဲဒီတန်ဖိုးကို toggle လုပ်ပြီး Mailbox ကနေပဲ master core ကို အသိပေးပါမယ်။
  • Master core က slave core ကနေ ပြင်ထားတဲ့ တန်ဖိုးအတိုင်း LED ကို အဖွင့်အပိတ် လုပ်ပေးပါမယ်။

စစချင်း master ရဲ့ main ထဲမှာ mail box ကို အောက်ကအတိုင်း initialize လုပ်ပြီး၊ slave ကပို့တာတွေပါ ပြန်လက်ခံဖို့ သူ့ရဲ့ mailbox interrupt ကို လည်း ဖွင့်လိုက်ပါမယ်။
Chip_MBOX_Init(LPC_MBOX);//Initialize mailbox with initial mutex free (master core only)
Chip_MBOX_SetMutex(LPC_MBOX);//Give mutex
NVIC_EnableIRQ(MAILBOX_IRQn); //Enable mailbox interrupt

ပြီးရင် timer ထဲမှာ အချိန်ပြည့်တိုင်း slave ဆီကို လက်ရှိ LED ရဲ့ တန်ဖိုးကို ပို့ဖို့ master ရဲ့ ပရိုဂရမ်အစမှာ အောက်ကအတိုင်း ကြေညာပါမယ်။ အဲဒီမှာ sharedVal က core နှစ်ခုလုံးက သုံးနေတာ ဖြစ်တဲ့ အတွက် mutex ကို ရမှ သုံးပြီး၊ အသုံးပြုပြီးတဲ့ အခါလည်း mutex ကို ပြန်လွှတ်ပေးဖို့ လိုပါတယ်။
volatile uint32_t sharedVal=0;
void SysTick_Handler(void)
{
while (Chip_MBOX_GetMutex(LPC_MBOX) == 0) {} //Get mutex
 Chip_MBOX_SetValue(LPC_MBOX, MAILBOX_CM0PLUS, (uint32_t) &sharedVal);
 //Give address of shared value in IRQ reg, raising interrupt
Chip_MBOX_SetMutex(LPC_MBOX);//Give mutex
}

Master ကပို့လိုက်တဲ့ တန်ဖိုးကို slave ထဲမှာ ဖတ်ဖို့အတွက် slave ရဲ့ mailbox interrupt ကို slave ရဲ့ main ထဲမှာ အောက်ကအတိုင်း ဖွင့်လိုက်ပါမယ်။
NVIC_EnableIRQ(MAILBOX_IRQn); //Enable mailbox interrupt

ပြီးတဲ့အခါ master ကပို့လိုက်တဲ့ တန်ဖိုးကို ပြောင်းပေးဖို့ interrupt handler ကို slave program ရဲ့အစမှာ အောက်ကအတိုင်းကြေညာပါမယ်။ mutex ကို ရမှ ပြင်လိုက်ပြီး၊ mutex ကို ပြန်လွှတ်ပေးပြီးတဲ့ အခါ master ကို သူ့ရဲ့ mailbox ကနေတဆင့် signal ပို့ပါတယ်။
void MAILBOX_IRQHandler(void)
{
 uint32_t *psharedVal = (uint32_t *) Chip_MBOX_GetValue(LPC_MBOX, MAILBOX_CM0PLUS);
 while (Chip_MBOX_GetMutex(LPC_MBOX) == 0) {} //get mutex
 *psharedVal^=1; //toggle the value of LED state
 Chip_MBOX_SetMutex(LPC_MBOX); //give mutex
 Chip_MBOX_ClearValueBits(LPC_MBOX, MAILBOX_CM0PLUS, 0xFFFFFFFF); //Clear its mailbox
 Chip_MBOX_SetValue(LPC_MBOX, MAILBOX_CM4, 1); //Signal master about the change
}

Slave က interrupt လုပ်တဲ့ အခါ ပြင်ထားတဲ့ တန်ဖို့အတိုင်း LED ကို အဖွင့်အပိတ်လုပ်ပေးဖို့ master ရဲ့ interrupt handler ကို master ရဲ့ ပရိုဂရမ်ထဲမှာ အောက်ကအတိုင်း ကြေညာပါမယ်။
void MAILBOX_IRQHandler(void)
{
 Board_LED_Set(0, sharedVal);
 Chip_MBOX_ClearValueBits(LPC_MBOX, MAILBOX_CM4, 0xFFFFFFFF);//Clear mailbox
}

ပြီးတဲ့ အခါ အထက်မှာ ပြောခဲ့အတိုင်း build လုပ်ပြီး၊ debug လုပ်ကြည့်လိုက်မယ် ဆိုရင် LED မီးလုံးက မှိတ်တုပ်မှိတ်တုပ် ဖြစ်နေတာကို တွေ့ရပါလိမ့်မယ်။ နောက်ဆုံးရလာတဲ့ DualCore_Master.c နဲ့ DualCore_Slave.c တို့ကို အောက်မှာ ပြထားပါတယ်။
//Name: DualCore_Master.c

#if defined (__USE_LPCOPEN)
#if defined(NO_BOARD_LIB)
#include "chip.h"
#else
#include "board.h"
#endif
#endif

#include <cr_section_macros.h>

#if defined (__MULTICORE_MASTER_SLAVE_M0SLAVE) || \
    defined (__MULTICORE_MASTER_SLAVE_M4SLAVE)
#include "boot_multicore_slave.h"
#endif

#define TICKRATE_HZ (20) //10 ticks per second
volatile uint32_t sharedVal=0;
void SysTick_Handler(void)
{
 while (Chip_MBOX_GetMutex(LPC_MBOX) == 0) {} //Get mutex
   Chip_MBOX_SetValue(LPC_MBOX, MAILBOX_CM0PLUS, (uint32_t) &sharedVal);
   //Give address of shared value in IRQ reg, raising interrupt
 Chip_MBOX_SetMutex(LPC_MBOX);//Give mutex
}
void MAILBOX_IRQHandler(void)
{
 Board_LED_Set(0, sharedVal);
 Chip_MBOX_ClearValueBits(LPC_MBOX, MAILBOX_CM4, 0xFFFFFFFF);//Clear mailbox
}

int main(void) {

#if defined (__USE_LPCOPEN)
    // Read clock settings and update SystemCoreClock variable
    SystemCoreClockUpdate();
#if !defined(NO_BOARD_LIB)
#if defined (__MULTICORE_MASTER) || defined (__MULTICORE_NONE)
    // Set up and initialize all required blocks and
    // functions related to the board hardware
    Board_Init();
#endif
    // Set the LED to the state of "On"
    //Board_LED_Set(0, true);
#endif
#endif

#if defined (__MULTICORE_MASTER_SLAVE_M0SLAVE) || \
    defined (__MULTICORE_MASTER_SLAVE_M4SLAVE)
    boot_multicore_slave();
#endif

    Chip_MBOX_Init(LPC_MBOX);//Initialize mailbox with initial mutex free (master core only)
    Chip_MBOX_SetMutex(LPC_MBOX);//Give mutex
    NVIC_EnableIRQ(MAILBOX_IRQn); //Enable mailbox interrupt
    SysTick_Config(SystemCoreClock / TICKRATE_HZ);
    while(1) {
     __WFI();
    }
    return 0 ;
}



//Name: DualCore_Slave.c
#if defined (__USE_LPCOPEN)
#if defined(NO_BOARD_LIB)
#include "chip.h"
#else
#include "board.h"
#endif
#endif

#include <cr_section_macros.h>

#if defined (__MULTICORE_MASTER_SLAVE_M0SLAVE) || \
    defined (__MULTICORE_MASTER_SLAVE_M4SLAVE)
#include "boot_multicore_slave.h"
#endif

void MAILBOX_IRQHandler(void)
{
 uint32_t *psharedVal = (uint32_t *) Chip_MBOX_GetValue(LPC_MBOX, MAILBOX_CM0PLUS);
 while (Chip_MBOX_GetMutex(LPC_MBOX) == 0) {} //get mutex
 *psharedVal^=1; //toggle the value of LED state
 Chip_MBOX_SetMutex(LPC_MBOX); //give mutex
 Chip_MBOX_ClearValueBits(LPC_MBOX, MAILBOX_CM0PLUS, 0xFFFFFFFF); //Clear its mailbox
 Chip_MBOX_SetValue(LPC_MBOX, MAILBOX_CM4, 1); //Signal master about the change
}

int main(void) {

#if defined (__USE_LPCOPEN)
    // Read clock settings and update SystemCoreClock variable
    SystemCoreClockUpdate();
#if !defined(NO_BOARD_LIB)
#if defined (__MULTICORE_MASTER) || defined (__MULTICORE_NONE)
    // Set up and initialize all required blocks and
    // functions related to the board hardware
    Board_Init();
#endif
    // Set the LED to the state of "On"
    Board_LED_Set(2, true); //build error without it
#endif
#endif

#if defined (__MULTICORE_MASTER_SLAVE_M0SLAVE) || \
    defined (__MULTICORE_MASTER_SLAVE_M4SLAVE)
    boot_multicore_slave();
#endif
    Board_LED_Set(2,false); //turn off LED
    NVIC_EnableIRQ(MAILBOX_IRQn); //Enable mailbox interrupt
    while(1) {
     __WFI();
    }
    return 0 ;
}




Reference:
[1]https://www.lpcware.com/content/faq/lpcxpresso/lpc541xx-multicore-apps
[2]http://www.nxp.com/products/microcontrollers/product_series/lpc54100/LPC54102J512BD64.html#documentation

No comments:

Post a Comment