1 / 30

A data-centric event-oriented RTOS for MCUs

A data-centric event-oriented RTOS for MCUs. Simplify multithreaded programming And Boost Performance. by Dirk Braun. The Concept of the Data-Centric RTOS 3 Ideas combined SW-Interrupts switch tasks – performance up, overhead down

laban
Télécharger la présentation

A data-centric event-oriented RTOS for MCUs

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. A data-centric event-oriented RTOS for MCUs Simplify multithreaded programming And Boost Performance by Dirk Braun

  2. The Concept of the Data-Centric RTOS 3 Ideas combined SW-Interrupts switch tasks – performance up, overhead down Publisher-Subscriber Mechanism distributes data – easy & safe data use in multithreaded environment Design Task-Group Priorities – avoid need for synchronization Contents • The DCEO-RTOS in practise • Example Application • Define Task-Groups, Mini-Tasks & Data Objects • Performance Measurements • Summary & Outlook

  3. Real-Time Reaction – objective: fast & deterministic Goal 1: Fast Real Time Reaction • Cause of a Reaction is an Event • Event • change of state of connected HW • Time has elapsed • New: Data item has changed Event • Reaction • SW reaction • Ultimately – HW reaction Reaction

  4. Reaction & Task Switches • DCEO – RTOS directly uses the processors built in way to React: the Interrupt • Processor status saved on interrupt entry automatically • Interrupt controllers priority management used • Mini-tasks (reactions) called directly from designated ISRs. They are implemented by the application programmer and execute at interrupt priority. • A „task switch“ involves backing up the current task. (copy processor state vars once) • Only one stack - no stack management during task switch • Benefits / costs • No. of save/restore cycles for processor status reduced • Task priority management done by HW (instead of software) • Number of separate priorities is limited by interrupt-hardware

  5. Common Event-Reaction Implementation Goal 2: Reduce Programming Overhead – use SW Interrupts Event-Reaction Implementation using SW-Interrupts 1 main() 2 { 3 CreateTask(MyTask); 4 ... 5 while (TRUE); 6 } 7 8 MyTask() 9 { // implements reaction 10 while (TRUE) 11 { 12 WaitForEvent(&myEvent); 13 // react to myEvent here 14 // todo: sync access to g_input 15 reaction = g_input * xxx 16 }; 17 } 18 19 MyISR() __irq 20 { // do some HW stuff to 21 // retrieve input 22 // todo: sync access to g_input 23 g_input = HW; 24 SetEvent(&myEvent); 25 } 1 main() 2 { 3 // setup ISRs 4 while (TRUE); 5 } 6 7 MyTask() __irq 8 { // implements reaction 9 // react to myEvent here 10 reaction = g_input * xxx 11 } 12 13 MyISR() __irq 14 { // do some HW stuff to 15 // retrieve input 16 g_input = HW; 17 myTaskTrigger = 1; 18 } • No infinite loop • No “active” waiting • No synchronization

  6. Data triggers itself through the application The changing of data itself is and replaces the event Scaling range ADC- ISR Generic Scaler raw AD value Device Reaction Realize Output output scaled AD value Bus Network Display DCEO-RTOS Data-Publisher change data object safe data copy passed into callbacks Subscriber 1 (callback function) at low priority Subscriber n (callback function) at priority m Goal 3: Publisher-Subscriber Mechanism puts Focus on Data Publisher Subscriber engine

  7. Scaler Test App Reuse Modules Random Generator Data Logger Scaling range ADC- ISR Scaling range Generic Scaler raw value ADC Test App Ref. scaled value scaled value Generic Scaler Check scaling raw AD value ADC- ISR scaled AD value Data Logger writes to file Check value raw AD value Goal 4: Module Independency Increased by Data Centric View …

  8. // adc.h // ADC value, a WORD extern DWORD obIdAdcVal; void AdcInit(); void AdcConvStart(); … by using simpler interfaces … Interface using Data-Object Simple Interface Interface using global data & event // adc.h // ADC value, a WORD WORD g_adcVal; EVENT g_adcValAvailEvent; MUTEX g_adcValAccMutex; void AdcInit(); void AdcConvStart(); Module independency easier to maintain

  9. … by avoiding global variables „Avoid global variables in interfaces, i.e. header files. Provide access functions instead.“ (Rule for modular programming) Separate get/set access functions for each variable Universal access function for all data-objects + notification // myCode.c void UseVarValueFunction() { MyType myVar; myVar = GetVar(); } void WriteVarValueFunction() { MyType myVar; ... SetVar(myVar); } // myCode.c void UseVarValueFunction() { MyType myVar; GetDataObject(&myVar, obIdMyVar); } void WriteVarValueFunction() { MyType myVar; ... SetDataObject(&myVar, obIdMyVar); } void OnMyVarChanged(BYTE* pData, …) { MyType* pNewMyVar = pData; ... } Instead of providing a separate set of access functions just publish a Data-Objects ID (a handle).

  10. Module MyCode1 Module MyCode2 // myCode1.h #include “myCode2.h” Extern WORD myVar1; ... // myCode2.h #include “myCode2.h” Extern WORD myVar2; ... // myCode1.c #include “myCode2.h” WORD myVar1; ... // myCode2.c WORD myVar2; ... #include “myCode2.h” Mutual Dependency -> non reusable code One module cannot compile without the other • modules cannot be reused without each other • cannot be used in a layered model

  11. Synchronization • Purpose • Safety measure to avoid inconsistent data and possible crashes when two process threads interrupt each other at the wrong moment • Unneccessary most often • This simultaneous occurence is unlikely and rare • Costly • Entering and leaving synchronization objects involve function calls and consume processor performance. • Causes expensive task-switches when really needed • Multithreaded SW-development more difficult • Often difficult to identify the resources that require synchronization

  12. 1 void Task1() { // do something EnterMutex(g_mutexA); // use shared resource . . . . LeaveMutex(g_mutexA); . . // do something } void Task2() { // do something EnterMutex(g_mutexA); // use shared resource LeaveMutex(g_mutexA); } 2 suspended 3 suspended 4 suspended 5 Task 2 3 4 high prio 5 OS calls higher prio task 2 Task 2 finished Process flow Task 2 leaves Mutex A 2 low prio 1 Task 2 tries to enter Mutex A. This causes a priority inversion and Task 1 is resumed at HIGH priority. Task 1 Task 1 finished Task 1 enters Mutex A Section 1 Task 1 leaves Mutex A and is suspended to low priority again. Task 2 is resumed. spare processor time ;-) Critical Section 1 running Task suspended Task inactive Task Mutexes rarely cause task switches

  13. Supersede the need for Synchronization • Idea • Tasks with same priority cannot interrupt each other -> so they cannot execute concurrently • Other tasks at higher priority still pre-empt lower priority tasks • Realization: • Timed-Mini-Tasks and Event-Mini-Tasks (Subscribers) grouped into Task-Groups. • A Task-Group has a single priority for all of its mini-tasks.

  14. Scaling range ADC- ISR Generic Scaler raw AD value Device Reaction Realize Output scaled AD value output Bus Network Display High Prio Generic Scaler Device Reaction Realize Output Generic Scaler Device Reaction Realize Output HW IRQ ADC- ISR ADC- ISR Med Prio Bus Network Bus Network Low Prio Display Display pre-empted Display continued Time Tasks at same priority Three tasks „Generic Scaler“ „Device Reaction“ and „Realize Output“ are subscriber mini-tasks of one task-group at a single priority.

  15. Low Prio Med Prio High Prio Software - Interrupt Software - Interrupt Software - Interrupt • Publisher – Subscriber • Manages Data-Objects and subcriber mini-tasks • Subscribers invoked when data-of-interest changed • Publisher – Subscriber • Manages Data-Objects and subcriber mini-tasks • Subscribers invoked when data-of-interest changed • Publisher – Subscriber • Manages Data-Objects and subcriber mini-tasks • Subscribers invoked when data-of-interest changed • Time – Engine • Timed mini-task invoked when time has come • Cyclic and one-time (time out) timed tasks supported • Time – Engine • Timed mini-task invoked when time has come • Cyclic and one-time (time out) timed tasks supported • Time – Engine • Timed mini-task invoked when time has come • Cyclic and one-time (time out) timed tasks supported Timed & Subscriber Mini-Tasks • Multiple Timer Reactions and multiple Data Event Reactions can share the same priority. • All mini-tasks are called from Interrupt Service Routines, run at predefined priorities and use the processors preemptive interrupt logic.

  16. End of Theory

  17. Objective: Create an analog-to-frequency converter with user-interface. This involves doing cyclic AD-conversions with an adjustable cycle period to be set via user-interface (RS232 command) setting the output frequency depending on the current analogue input reporting AD-values via RS232 (which represents the user interface) implementing a simple clock to report time to RS232 Example Application Todo’s: • Design modules, their relations, interfaces, data-objects • Plan Tasks – timed and subscriber mini-tasks • Identify Synchronization Requirements – Group mini-tasks into task-groups

  18. Modules Description Interface Module ADC • Function for triggering conversions • Provide Data-Object for conversion result // adc.h // ADC value, a WORD extern DWORD obIdAdcVal; void AdcInit(); void AdcConvStart(); ADC AscString • Send complete strings via RS232 using function call • Provide Data-Object for new received string • Provide Data-Object for event when string-sending has completed // ascString.h extern DWORD obIdRecvString; extern DWORD obIdSentString; void AscStrInit(DWORD gapTime, char endChar); BOOL AscStrSend(BYTE* pSendString, WORD len); AscString Main application • React on new AD values • Set Output Reaction • React to UI • Time Output Main Empty

  19. More Modules Description Interface Module ASC • Byte-wise RS232 communication // adc.h void AscInit(DWORD baudrate); void AscSendByte(BYTE sendByte); // register callbacks BOOL AscSetRxCallBack( AscRXCbPtr pRxCallback); BOOL AscSetTxCallBack( AscTXCbPtr pTxCallback); ASC CmdInterpreter (helper module) • Provides functions to interpret commands in a string // CmdInterpreter.h typedef enum CmdCodeEnum { CmdHelp, // general CmdPeriod, CmdCodeInvalid, CmdNull } CmdCode; CmdCode CiGetCommand(char* cmdString, int len); Cmd Interpreter FrequencyOutput • HW-Output for frequency • Provide Data-Object for frequency // freqOut.h // frequency, a WORD extern DWORD obIdFrequency; void FreqOutInit(); Frequency Output

  20. Application Layer Main Cmd Interpreter HW-Logic Layer DCEO - RTOS ADC Frequency Out AscString HW- Abstraction Layer Timer0 ASC Function call Call Callback Data Notification Module Relations & Data Objects Data Objects: • AD value • passing from ADC to scaling (Main) • passing from ADC to RS232 • AscStrRX • received command via RS232 • Frequency Out • passing from scaling (Main) to Frequency Out

  21. Task-group-priorities ARE interrupt-priorities, so task-group-priorities & interrupt-priorities belong into the same priority list Reaction Realizing Output ADC ISR Set Data Object High Priority Task Group Cyclic Mini- Task Triggering Conversions ADC Interrupt Priority ASC ISR Medium Priority Task Group Write to UI ASC Buffer & Control Cyclic Mini- Task incrementing & writing clock ASC (UART) Interrupt Priority Sync required Low Priority Task Group Task Groups & Priorities • Data-objects are safely shared across priority-boundaries • Priority plan identifies synchronization requirements

  22. Defining Priorities Setup - code Setting up Mini-Tasks // priorities.h // define your Task Groups Priorities here const TaskGroup g_taskGroups[] = { // ilvl vicChannel { 7, 25}, // high (0) { 11, 26}, // medium (1) { 14, 28} // low (2) }; // define your own interrupts here ILVL VIC channel #define ADC_ILVL 8 #define ADC_CHANNEL 18 #define ASC_ILVL 12 #define ASC_CHANNEL 7 #define TASK_T_ILVL 4 #define TASK_T_CHANNEL 5 int main (void) { ... Task_Init(); // init DCEO-RTOS AscInit(115200); // init ASC AscStrInit(0, '\r'); // init AscString AdcInit(); // init ADC // enter callbacks for task-group prioss DataAddNotifyCB(OnAdValChangedHigh, obIdAdcVal, 0); TaskT_CallBackCyclicAt(30, OnCyclicAdcConvTimer, 1, 1); DataAddNotifyCB(OnRxStrRecv, obIdRecvString, 2); DataAddNotifyCB(OnAdValChangedLow, obIdAdcVal, 2); INTERRUPT_ENABLE() while (TRUE); } Interrupt levels & task priorities shown in red Mini-Task creation shown in red

  23. Interface Implementing Module ADC // implementation continued void AdcConversionStart() { // trigger conversion AD0CR |= 0x01200000; } void adcIsr (void) __irq { // ISR, pick up conversion result & set // data object WORD adVal; // ARM7 specific, re-enable interrupts, so higher priority IRQ get through again IENABLE // Read A/D Data Register adVal = (AD0DR & 0xffc0) >> 6; ... if (adVal != g_lastAdVal) { DataSetObject((BYTE*)&adVal, sizeof(adVal), obIdAdcVal); g_lastAdVal = adVal; } IDISABLE // Acknowledge Interrupt VICVectAddr = 0; } // adc.h // ad converted value, a WORD, OUT extern DWORD obIdAdcVal; void AdcInit(); void AdcConversionStart(); Implementation // adc.c DWORD obIdAdcVal; void adcIsr (void) __irq ; WORD g_lastAdVal; void AdcInit() { // set up HW ... IsrCreate((unsigned long)adcIsr, ADC_ILVL, ADC_CHANNEL); obIdAdcVal = DataCreateObject(sizeof(g_lastAdVal)); g_lastAdVal = 0; DataSetObject((BYTE*)&g_lastAdVal, sizeof(g_lastAdVal), obIdAdcVal); }

  24. Interface Implementing Module ascString void ascStr_OnRxRecv(BYTE recvByte) { // byte received callback from lower layer ... Add byte to buffer // set timeout if not set yet if (ascOnTimeoutId != (DWORD)NULL) // modify timeout time to NOW + allowed gaptime TaskT_ChangeCallbackTime(TaskT_Now() + g_ascStrMaxRecvInterByteGapTime, ascOnTimeoutId, 1); else { // first byte of new string coming in // set timeout to now + max. gaptime ascOnTimeoutId = TaskT_CallBackAt( TaskT_Now() + g_ascStrMaxRecvInterByteGapTime, ascStr_OnRxComplete, 1, 1); } ... } // timeout mini-task void ascStr_OnRxComplete() // { // set data object with received string DataSetObject((BYTE*)&g_ascStrRecvStr, sizeof(g_ascStrRecvStr), obIdRecvString); ... } // ascString.h - excerpt of // Reception of a string is complete when the // gapTime between two bytes has expired extern DWORD obIdRecvString; ... void AscStrInit(DWORD gapTime, char endChar); BOOL AscStrSend(BYTE* pSendString, WORD len); Implementation // ascString.c // ringbuffer containing received bytes DWORD g_ascStrMaxRecvInterByteGapTime; // max. time allowed between any two bytes DWORD obIdRecvString; ... RecvStr g_ascStrRecvStr; // forward declaration of callbacks void ascStr_OnRxRecv(BYTE recvByte); void ascStr_OnRxComplete(int);

  25. Interface Implementing Module FreqOut // freqOut.h extern DWORD obIdFrequency; void FreqOutInit(); void FreqOutInit() { WORD freq; obIdFrequency = DataCreateObject(sizeof(freq)); freq = 1; // initialized to 1 Hz DataSetObject((BYTE*)&freq, sizeof(freq), obIdFrequency); g_foHalfPeriod = 10000 / freq / 2; DataAddNotifyCB(OnFreqChanged, obIdFrequency, 0); g_foTaskId = TaskT_CallBackCyclicAt(g_foHalfPeriod, OnFreqOutHalfPeriod, 0, 0); } // Subcriber mini-task void OnFreqChanged(BYTE* pData, DWORD objectId) { // calculate half period g_foHalfPeriod = 10000 / (*(WORD*)pData) / 2; TaskT_ChangeCallbackTime(g_foHalfPeriod, g_foTaskId, 0); } // timed mini-task void OnFreqOutHalfPeriod(int unusedInt){ unusedInt = 0; LED_TOGGLE(1) } Implementation //freqOut.c // includes DWORD obIdFrequency; // Data Object ID WORD g_foHalfPeriod; // in 100 us DWORD g_foTaskId; // forward declaration of mini-tasks void OnFreqChanged(BYTE* pData, DWORD objectId); void OnFreqOutHalfPeriod(int timerVal);

  26. Initialization Implement Subscribers & Reaction in Main Module Task - Implementation // forward declaration of mini-tasks void OnCyclicAdcConvTimer(int i); void OnAdValChangedHigh(BYTE* pData, DWORD objectId); void OnAdValChangedLow(BYTE* pData, DWORD objectId); void OnRxStrRecv(BYTE* pData, DWORD objectId); int main (void) { ... Task_Init(); // init DCEO-RTOS AscInit(115200); // init modules AscStrInit(0, '\r'); AdcInit(); FreqOutInit(); // create mini-tasks DataAddNotifyCB(OnAdValChangedHigh, obIdAdcVal, 0); TaskT_CallBackCyclicAt(300, OnCyclicAdcConvTimer, 1, 1); DataAddNotifyCB(OnRxStrRecv, obIdRecvString, 2); DataAddNotifyCB(OnAdValChangedLow, obIdAdcVal, 2); ... while (TRUE) { } // loop forever } void OnCyclicAdcConvTimer(int i){ // start AD-conversion AdcConversionStart(); } void OnAdValChangedHigh(BYTE* pData, DWORD objectId){ ... UpdateVar(pData, objectId, obIdAdcVal, (BYTE*)&adcValue, sizeof(adcValue)); outFreq = algorythm(adcValue) if (outFreq != g_freq) { g_freq = outFreq; DataSetObject((BYTE*)&g_freq, sizeof(g_freq), obIdFrequency); } } void OnAdValChangedLow(BYTE* pData, DWORD objectId) { ... UpdateVar(pData, objectId, obIdAdcVal, (BYTE*)&adcValue, sizeof(adcValue)); sprintf(adValStr, "AD Val = %04d\r\n", adcValue); AscStrSend(adValStr, strlen(adValStr)); } void OnRxStrRecv(BYTE* pData, DWORD objectId) ...

  27. Performance Measurement 1 - toggles with 2 2 - trigger AD conversion 3 - ADC ISR (start – end) 4 - High prio reaction (intercepts ISR) 5 - Low prio report to UI 6 - 3 minus 4 7 - time base 100µs 8 - not idle • Reaction Times (for ARM7 60MHz) • Timed mini-task < 8 s • (time-base-tick to start of mini-task including context switch, 7 low to 2 high) • Subscriber mini-task < 16 s • (set data object to invocation of subscriber including context switch, 6 high (1st) to 6 low (1st) • Total reaction time > 20 s • (external HW-event to external HW-reaction)

  28. Main differences between traditional RTOS and DCEO-RTOS Summary • Stop thinking in terms of processes -> Start thinking about Data, Events and Reactions • Gain flexibility – use data from all priority mini-tasks without worrying about synchronization • Gain performance – use 100% processor time and still have a deterministic reaction of higher priority tasks. Use processors interrupt priorization.

  29. Process vs. Data Centric View

  30. DCEO-RTOS is well suited for Embedded Software Development Multi-Core Support -> assign a task-group to a core, only means of communication is through data-objects Distributed Computing -> many nodes of a network react to global data-ojects using a real time communication network Next development steps of DCEO-RTOS will address Support more processors (currently ARM7) Bus-Interfaces (with HW-abstraction layer) UML-Tool Integration Idea Development-Tool for easy linking of data-objects to UI-controls Outlook & Ideas Contact Email dbraun@cleversoftware.de Skype rtosdeveloper Web cleversoftware.de

More Related