RTX5. Теоретические основы

Многие аспекты ядра могут настраиваться, и параметры конфигурации упоминаются там, где это применимо.

Запуск системы

Поскольку основная функция main() больше не является потоком, RTX5 не мешает запуску системы до тех пор, пока не будет достигнута основная функция main(). Как только запуск достигнет основной функции main(), рекомендуется выполнить инициализацию аппаратного обеспечения и запустить ядро. Это также отражено в файле шаблона кода пользователя «Основная функция» CMSIS-RTOS2, поставляемом с компонентом RTX5.

Основная функция  main() в вашем приложении должна реализовать как минимум следующие действия:

  1. Инициализация и настройка аппаратных средств включающая периферию, память, пины микроконтроллера, систему тактирования и прерывания.
  2. Обновить систему тактирования ядра используя соотвествующую  CMSIS-Core (Cortex-M) или CMSIS-Core (Cortex-A) функцию.
  3. Инициализировать ядро CMSIS-RTOS используя osKernelInitialize.
  4. Опционально, создать новый поток app_main, который используется как главный поток используя osThreadNew. Альтернативно, потоки можно создавать в  main() напрямую.
  5. Запустите планировщик RTOS используя функци osKernelStart. Эта функция не возвращается в случае успешного выполнения. Любой код приложения написанный после osKernelStart не будет исполнен, кроме случая когда osKernelStart не запуститься.
Примечание
Прерывания (например SVC) используемые ядром инициализируются в  osKernelInitialize. Если приоритеты или группы NVIC будут изменены приложением после указанной выше последовательности, может потребоваться новый вызов фукнции  osKernelInitialize. Вы можете это наблюдать например получая события osRtxErrorNotify или события вызывающие серьезные ошибки типо HARD FAULT.
Таймер тиков настраивается в функции osKernelStart.
Интервал тиков рассчитывается на основе переменной SystemCoreClock.

Scheduler (планировщик)

RTX5 реализует вытесняющий планировщик с минимальной задержкой (низкая латентность). Основные части RTX5 выполняются в режиме обработчика, такие как:

  • SysTick_Handler используется для планирования времени.
  • SVC_Handler используется для планирования на основе блокировки.
  • PendSV_Handler используется для планирования на основе прерываний.

In order to be low-latency with respect to ISR execution those system exceptions are configured to use the lowest priority groups available. The priorities are configured such that no preemption happens between them. Thus no interrupt critical sections (i.e. interrupt locks) are needed to protect the scheduler.

scheduling.png

Планирование потоков и выполнение прерываний

Планировщик объединяет приоритетное и циклическое переключение контекста.

Пример,  на изображении выше, содержит четыре потока (1, 2, 3 и 4). Поток 1 и 2 имеют одинаковый приоритет, поток 3 имеет более высокий приоритет и поток 4 имет самый высокий приоритет (osThreadAttr_t::priority). Пока потоки 3 и 4 заблокированы, планировщик переключается между потоками 1 и 2 циклически выделяя потокам тайм-слоты. Тайм-слот для циклического переключения можно настроить, смотрите параметр Round-Robin Timeout в System Configuration.
Поток 2 разблокирует поток 3 произвольным вызовом RTOS (выполняется в режиме обработчика SVC) это можно увидеть на маркере 2 на оси Time. Планировщик переключается на поток 3 потому что имеет более высокий приоритет. Поток 4 по-прежнему заблокирован.

На маркере 4 происходит прерывание (ISR) и вытесняет SysTick_Handler. RTX не добавляет задержки на выполнение службы прерывания. The ISR routine uses an RTOS-call that unblocks thread 4. Instead of switching to thread 4 immediately the PendSV flag is set to defer the context switching. The PendSV_Handler is executed right after the SysTick_Handler returns and the defered context switch to thread 4 is carried out. As soon as highest priority thread 4 blocks again by using a blocking RTOS-call execution is switched back to thread 3 immidiately during time index 5.

At time index 5 thread 3 uses a blocking RTOS-call as well. Thus the scheduler switches back to thread 2 for time index 6. At time index 7 the scheduler uses the round-robin mechanism to switch to thread 1 and so on.

Memory Allocation (выделение памяти)

Объекты RTX5 (поток, мьютекс, семафор, таймер, очередь сообщений, флаги потоков и событий, а также пул памяти) требуют выделения оперативной памяти ОЗУ. Объекты могут быть созданы с помощью вызова osObjectNew() и удалены с помощью вызова osObjectDelete(). Выделенная память объекта должна быть доступна в течение всего жизненного цикла объекта.

RTX5 предлагает три различных способа выделения памяти для объектов:

  • Глобальный пул памяти использует один глобальный пул памяти для всех объектов. Его легко настроить, но может иметь недостаток в фрагментации памяти, когда объекты с разными размерами создаются и уничтожаются.
  • Обьектно-ориентированный пул памяти использует пул памяти фиксированного размера для каждого типа объекта. Метод является детерминированным по времени и позволяет избежать фрагментации памяти.
  • Static Object Memory резервирует память во время компиляции и полностью избегает того, что системе может не хватить памяти. Обычно это требуется для безопасности некоторых критически важных систем.

Можно смешивать все методы выделения памяти в одном приложении.

Global Memory Pool (глобальный пул памяти)

Глобальный пул памяти выделяет все объекты из области памяти. Этот метод выделения памяти является настройкой конфигурации RTX5 по умолчанию.

MemAllocGlob.png

Глобальный пул памяти для всех объектов

Когда пул памяти не обеспечивает достаточную память, создание объекта завершается сбоем, а связанная с ним функция osObjectNew() возвращает NULL.

Включено в  Настройка системы.

Object-specific Memory Pools (обьектно-ориентированный пул памяти)

Объектно-ориентированные пулы памяти позволяют избежать фрагментации памяти с помощью специального управления памятью фиксированного размера для каждого типа объекта. Этот тип пула памяти полностью детерминирован по времени, это означает, что создание и уничтожение объектов всегда имеют одинаковую фиксированную величину времени. Поскольку пул памяти фиксированного размера специфичен для типа объекта, упрощается обработка ситуаций с нехваткой памяти.

MemAllocSpec.png

Один пул памяти для каждого типа объекта

Объектно-ориентированные пулы памяти выборочно активируются для каждого типа объекта, например: мьютекс или поток, используя файл конфигурации RTX:

  • Enabled in Thread Configuration for thread objects.
  • Enabled in Timer Configuration for timer objects.
  • Enabled in Event Flags Configuration for event objects.
  • Enabled in Mutex Configuration for mutex objects.
  • Enabled in Semaphore Configuration for semaphore.
  • Enabled in Memory Pool Configuration for memory pools.
  • Enabled in Message Queue Configuration for message objects.

Когда пул памяти не обеспечивает достаточную память, создание объекта завершается сбоем, а связанная с ним функция osObjectNew() возвращает NULL.

Static Object Memory (статическая память)

В отличии от динамической памяти, статическая память требует выделения памяти во время компиляции.

MemAllocStat.png

Статически выделенная память для всех объектов

In order to allow RTX5 aware debugging, i.e. Component Viewer, to recognize control blocks these needs to be placed in individual memory sections, i.e. using __attribute__((section(...))).

RTX Object Linker Section
Thread .bss.os.thread.cb
Timer .bss.os.timer.cb
Event Flags .bss.os.evflags.cb
Mutex .bss.os.mutex.cb
Semaphore .bss.os.semaphore.cb
Memory Pool .bss.os.mempool.cb
Message Queue .bss.os.msgqueue.cb

В следующем примере кода показано, как создать объект ОС с использованием статической памяти.

Пример кода:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/*----------------------------------------------------------------------------
 * CMSIS-RTOS 'main' function template
 *---------------------------------------------------------------------------*/
#include "RTE_Components.h"
#include  CMSIS_device_header
#include "cmsis_os2.h"
 
//include rtx_os.h for types of RTX objects
#include "rtx_os.h"
 
//The thread function instanced in this example
void worker(void *arg)
{
  while(1) 
  {
    //work
    osDelay(10000);
  }  
}
 
// Define objects that are statically allocated for worker thread 1
__attribute__((section(".bss.os.thread.cb")))
osRtxThread_t worker_thread_tcb_1;
 
// Reserve two areas for the stacks of worker thread 1
// uint64_t makes sure the memory alignment is 8
uint64_t worker_thread_stk_1[64];
 
// Define the attributes which are used for thread creation
// Optional const saves RAM memory and includes the values in periodic ROM tests 
const osThreadAttr_t worker_attr_1 = {
  "wrk1",
  osThreadJoinable,
  &worker_thread_tcb_1,
  sizeof(worker_thread_tcb_1),
  &worker_thread_stk_1[0],
  sizeof(worker_thread_stk_1),
  osPriorityAboveNormal,
  0
};
 
// Define ID object for thread
osThreadId_t th1;
 
/*----------------------------------------------------------------------------
 * Application main thread
 *---------------------------------------------------------------------------*/
void app_main (void *argument) {
  uint32_t param = NULL;
 
  // Create an instance of the worker thread with static resources (TCB and stack)
  th1 = osThreadNew(worker, &param, &worker_attr_1);
 
  for (;;) {}
}
 
int main (void) {
  // System Initialization
  SystemCoreClockUpdate();
  // ...
  osKernelInitialize();                 // Initialize CMSIS-RTOS
  osThreadNew(app_main, NULL, NULL);    // Create application main thread
  osKernelStart();                      // Start thread execution
  for (;;) {}
}

 

Thread Stack Management (управление стеком потока)

Для процессоров Cortex-M без блока с плавающей точкой контекст потока требует 64 байта в локальном стеке.

Примечание
Для Cortex-M4 / M7 с плавающей точкой контекст потока требует 200 байт в локальном стеке. Для этих устройств пространство стека по умолчанию должно быть увеличено до минимума 300 байтов.

 

Каждый поток имеет отдельный стек, который содержит контекст потока и пространство стека для автоматических переменных и возвращаемые адреса для вложенных функций. Размеры стека для потоков RTX гибко настраивается, как описано в разделе Конфигурация потоков. RTX предлагает настраиваемую проверку переполнения стека и использования стека.

Low-Power Operation (работа с низким энергопотреблением)

Системный поток osRtxIdleThread может использоваться для переключения системы в режим с низким энергопотреблением. Самый простой способ войти в режим с низким энергопотреблением — это выполнение функции __WFE, которая переводит процессор в спящий режим, где он ждет события.

Пример кода:

1
2
3
4
5
6
7
8
9
10
11
#include "RTE_Components.h"
#include CMSIS_device_header            /* Device definitions                 */
 
void osRtxIdleThread (void) {
  /* The idle demon is a system thread, running when no other thread is       */
  /* ready to run.                                                            */
 
  for (;;) {
    __WFE();                            /* Enter sleep mode                   */
  }
}
Примечание
__WFE() доступен не для каждой реализации Cortex-M. Проверьте доступность в референс мануале.

RTX Kernel Timer Tick

RTX uses the generic OS Tick API to configure and control its periodic Kernel Tick.

To use an alternative timer as the Kernel Tick Timer one simply needs to implement a custom version of the OS Tick API.

Note
The OS Tick implementation provided must asure that the used timer interrupt uses the same (low) priority group as the service interrupts, i.e. interrupts used by RTX must not preempt each other. Refer to the Scheduler section for more details.

Tick-less Low-Power Operation

RTX5 provides extension for tick-less operation which is useful for applications that use extensively low-power modes where the SysTick timer is also disabled. To provide a time-tick in such power-saving modes, a wake-up timer is used to derive timer intervals. The CMSIS-RTOS2 functions osKernelSuspend and osKernelResume control the tick-less operation.

Using this functions allows the RTX5 thread scheduler to stop the periodic kernel tick interrupt. When all active threads are suspended, the system enters power-down and calculates how long it can stay in this power-down mode. In the power-down mode the processor and peripherals can be switched off. Only a wake-up timer must remain powered, because this timer is responsible to wake-up the system after the power-down period expires.

The tick-less operation is controlled from the osRtxIdleThread thread. The wake-up timeout value is set before the system enters the power-down mode. The function osKernelSuspend calculates the wake-up timeout measured in RTX Timer Ticks; this value is used to setup the wake-up timer that runs during the power-down mode of the system.

Once the system resumes operation (either by a wake-up time out or other interrupts) the RTX5 thread scheduler is started with the function osKernelResume. The parameter sleep_time specifies the time (in RTX Timer Ticks) that the system was in power-down mode.

Code Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "msp.h"                        // Device header
/*----------------------------------------------------------------------------
 *      MSP432 Low-Power Extension Functions
 *---------------------------------------------------------------------------*/
static void MSP432_LP_Entry(void) {
  /* Enable PCM rude mode, which allows to device to enter LPM3 without waiting for peripherals */
  PCM->CTL1 = PCM_CTL1_KEY_VAL | PCM_CTL1_FORCE_LPM_ENTRY;       
  /* Enable all SRAM bank retentions prior to going to LPM3  */
  SYSCTL->SRAM_BANKRET |= SYSCTL_SRAM_BANKRET_BNK7_RET;
  __enable_interrupt();
  NVIC_EnableIRQ(RTC_C_IRQn);
  /* Do not wake up on exit from ISR */
  SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;
  /* Setting the sleep deep bit */
  SCB->SCR |= (SCB_SCR_SLEEPDEEP_Msk);  
}
 
static volatile unsigned int tc;
static volatile unsigned int tc_wakeup;
 
void RTC_C_IRQHandler(void)
{
  if (tc++ > tc_wakeup) 
  {
    SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;    
    NVIC_DisableIRQ(RTC_C_IRQn);
    NVIC_ClearPendingIRQ(RTC_C_IRQn);
    return;
  }
  if (RTC_C->PS0CTL & RTC_C_PS0CTL_RT0PSIFG)
  {
    RTC_C->CTL0 = RTC_C_KEY_VAL;                 // Unlock RTC key protected registers
    RTC_C->PS0CTL &= ~RTC_C_PS0CTL_RT0PSIFG;
    RTC_C->CTL0 = 0;
    SCB->SCR |= (SCB_SCR_SLEEPDEEP_Msk);
  }
}
 
uint32_t g_enable_sleep = 0;
 
void osRtxIdleThread (void) {
 
  for (;;) {
    tc_wakeup = osKernelSuspend();
    /* Is there some time to sleep? */
    if (tc_wakeup > 0) {
      tc = 0;
      /* Enter the low power state */
      MSP432_LP_Entry();
      __WFE();
    }
    /* Adjust the kernel ticks with the amount of ticks slept */
    osKernelResume (tc);
  }
}
Note
__WFE() is not available in every Arm Cortex-M implementation. Check device manuals for availability. The alternative using __WFI() has other issues, please take note of http://www.keil.com/support/docs/3591.htm as well.

RTX5 Header File

Every implementation of the CMSIS-RTOS2 API can bring its own additional features. RTX5 adds a couple of functions for the idle more, for error notifications, and special system timer functions. It also is using macros for control block and memory sizes.

If you require some of the RTX specific functions in your application code, #include the header file rtx_os.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
/*
 * Copyright (c) 2013-2018 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * -----------------------------------------------------------------------------
 *
 * Project:     CMSIS-RTOS RTX
 * Title:       RTX OS definitions
 *
 * -----------------------------------------------------------------------------
 */
 
#ifndef RTX_OS_H_
#define RTX_OS_H_
 
#include 
#include 
#include "cmsis_os2.h"
 
#ifdef  __cplusplus
extern "C"
{
#endif
 
 
/// Kernel Information
#define osRtxVersionAPI      20010002   ///
#define osRtxVersionKernel   50030000   ///
#define osRtxKernelId     "RTX V5.3.0"  ///
 
 
//  ==== Common definitions ====
 
/// Object Identifier definitions
#define osRtxIdInvalid          0x00U
#define osRtxIdThread           0x01U
#define osRtxIdTimer            0x02U
#define osRtxIdEventFlags       0x03U
#define osRtxIdMutex            0x04U
#define osRtxIdSemaphore        0x05U
#define osRtxIdMemoryPool       0x06U
#define osRtxIdMessage          0x07U
#define osRtxIdMessageQueue     0x08U
 
/// Object State definitions (except for Threads and Timers)
#define osRtxObjectInactive     0x00U
#define osRtxObjectActive       0x01U
 
/// Object Flags definitions
#define osRtxFlagSystemObject   0x01U
#define osRtxFlagSystemMemory   0x02U
 
 
//  ==== Kernel definitions ====
 
/// Kernel State definitions
#define osRtxKernelInactive             ((uint8_t)osKernelInactive)
#define osRtxKernelReady                ((uint8_t)osKernelReady)
#define osRtxKernelRunning              ((uint8_t)osKernelRunning)
#define osRtxKernelLocked               ((uint8_t)osKernelLocked)
#define osRtxKernelSuspended            ((uint8_t)osKernelSuspended)
 
 
//  ==== Thread definitions ====
 
/// Thread State definitions (extending osThreadState)
#define osRtxThreadStateMask            0x0FU
 
#define osRtxThreadInactive             ((uint8_t)osThreadInactive)
#define osRtxThreadReady                ((uint8_t)osThreadReady)
#define osRtxThreadRunning              ((uint8_t)osThreadRunning)
#define osRtxThreadBlocked              ((uint8_t)osThreadBlocked)
#define osRtxThreadTerminated           ((uint8_t)osThreadTerminated)
 
#define osRtxThreadWaitingDelay         ((uint8_t)(osRtxThreadBlocked | 0x10U))
#define osRtxThreadWaitingJoin          ((uint8_t)(osRtxThreadBlocked | 0x20U))
#define osRtxThreadWaitingThreadFlags   ((uint8_t)(osRtxThreadBlocked | 0x30U))
#define osRtxThreadWaitingEventFlags    ((uint8_t)(osRtxThreadBlocked | 0x40U))
#define osRtxThreadWaitingMutex         ((uint8_t)(osRtxThreadBlocked | 0x50U))
#define osRtxThreadWaitingSemaphore     ((uint8_t)(osRtxThreadBlocked | 0x60U))
#define osRtxThreadWaitingMemoryPool    ((uint8_t)(osRtxThreadBlocked | 0x70U))
#define osRtxThreadWaitingMessageGet    ((uint8_t)(osRtxThreadBlocked | 0x80U))
#define osRtxThreadWaitingMessagePut    ((uint8_t)(osRtxThreadBlocked | 0x90U))
 
/// Thread Flags definitions
#define osRtxThreadFlagDefStack 0x10U   ///
 
/// Stack Marker definitions
#define osRtxStackMagicWord     0xE25A2EA5U ///
#define osRtxStackFillPattern   0xCCCCCCCCU ///
 
/// Thread Control Block
typedef struct osRtxThread_s {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                        attr;  ///
  const char                    *name;  ///
  struct osRtxThread_s   *thread_next;  ///
  struct osRtxThread_s   *thread_prev;  ///
  struct osRtxThread_s    *delay_next;  ///
  struct osRtxThread_s    *delay_prev;  ///
  struct osRtxThread_s   *thread_join;  ///
  uint32_t                      delay;  ///
  int8_t                     priority;  ///
  int8_t                priority_base;  ///
  uint8_t                 stack_frame;  ///
  uint8_t               flags_options;  ///
  uint32_t                 wait_flags;  ///
  uint32_t               thread_flags;  ///
  struct osRtxMutex_s     *mutex_list;  ///
  void                     *stack_mem;  ///
  uint32_t                 stack_size;  ///
  uint32_t                         sp;  ///
  uint32_t                thread_addr;  ///
  uint32_t                  tz_memory;  ///
} osRtxThread_t;
 
 
//  ==== Timer definitions ====
 
/// Timer State definitions
#define osRtxTimerInactive      0x00U   ///
#define osRtxTimerStopped       0x01U   ///
#define osRtxTimerRunning       0x02U   ///
 
/// Timer Type definitions
#define osRtxTimerPeriodic      ((uint8_t)osTimerPeriodic)
 
/// Timer Function Information
typedef struct {
  osTimerFunc_t                  func;  ///
  void                           *arg;  ///
} osRtxTimerFinfo_t;
 
/// Timer Control Block
typedef struct osRtxTimer_s {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                        type;  ///
  const char                    *name;  ///
  struct osRtxTimer_s           *prev;  ///
  struct osRtxTimer_s           *next;  ///
  uint32_t                       tick;  ///
  uint32_t                       load;  ///
  osRtxTimerFinfo_t             finfo;  ///
} osRtxTimer_t;
 
 
//  ==== Event Flags definitions ====
 
/// Event Flags Control Block
typedef struct {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                    reserved;
  const char                    *name;  ///
  osRtxThread_t          *thread_list;  ///
  uint32_t                event_flags;  ///
} osRtxEventFlags_t;
 
 
//  ==== Mutex definitions ====
 
/// Mutex Control Block
typedef struct osRtxMutex_s {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                        attr;  ///
  const char                    *name;  ///
  osRtxThread_t          *thread_list;  ///
  osRtxThread_t         *owner_thread;  ///
  struct osRtxMutex_s     *owner_prev;  ///
  struct osRtxMutex_s     *owner_next;  ///
  uint8_t                        lock;  ///
  uint8_t                  padding[3];
} osRtxMutex_t;
 
 
//  ==== Semaphore definitions ====
 
/// Semaphore Control Block
typedef struct {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                    reserved;
  const char                    *name;  ///
  osRtxThread_t          *thread_list;  ///
  uint16_t                     tokens;  ///
  uint16_t                 max_tokens;  ///
} osRtxSemaphore_t;
 
 
//  ==== Memory Pool definitions ====
 
/// Memory Pool Information
typedef struct {
  uint32_t                 max_blocks;  ///
  uint32_t                used_blocks;  ///
  uint32_t                 block_size;  ///
  void                    *block_base;  ///
  void                     *block_lim;  ///
  void                    *block_free;  ///
} osRtxMpInfo_t;
 
/// Memory Pool Control Block
typedef struct {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                    reserved;
  const char                    *name;  ///
  osRtxThread_t          *thread_list;  ///
  osRtxMpInfo_t               mp_info;  ///
} osRtxMemoryPool_t;
 
 
//  ==== Message Queue definitions ====
 
/// Message Control Block
typedef struct osRtxMessage_s {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                    priority;  ///
  struct osRtxMessage_s         *prev;  ///
  struct osRtxMessage_s         *next;  ///
} osRtxMessage_t;
 
/// Message Queue Control Block
typedef struct {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                    reserved;
  const char                    *name;  ///
  osRtxThread_t          *thread_list;  ///
  osRtxMpInfo_t               mp_info;  ///
  uint32_t                   msg_size;  ///
  uint32_t                  msg_count;  ///
  osRtxMessage_t           *msg_first;  ///
  osRtxMessage_t            *msg_last;  ///
} osRtxMessageQueue_t;
 
 
//  ==== Generic Object definitions ====
 
/// Generic Object Control Block
typedef struct {
  uint8_t                          id;  ///
  uint8_t                       state;  ///
  uint8_t                       flags;  ///
  uint8_t                    reserved;
  const char                    *name;  ///
  osRtxThread_t          *thread_list;  ///
} osRtxObject_t;
 
 
//  ==== OS Runtime Information definitions ====
 
/// OS Runtime Information structure
typedef struct {
  const char                   *os_id;  ///
  uint32_t                    version;  ///
  struct {                              ///
    uint8_t                     state;  ///
    volatile uint8_t          blocked;  ///
    uint8_t                    pendSV;  ///
    uint8_t                  reserved;
    uint32_t                     tick;  ///
  } kernel;
  int32_t                   tick_irqn;  ///
  struct {                              ///
    struct {                            ///
      osRtxThread_t             *curr;  ///
      osRtxThread_t             *next;  ///
    } run;
    osRtxObject_t               ready;  ///
    osRtxThread_t               *idle;  ///
    osRtxThread_t         *delay_list;  ///
    osRtxThread_t          *wait_list;  ///
    osRtxThread_t     *terminate_list;  ///
    struct {                            ///
      osRtxThread_t           *thread;  ///
      uint32_t                   tick;  ///
      uint32_t                timeout;  ///
    } robin;
  } thread;
  struct {                              ///
    osRtxTimer_t                *list;  ///
    osRtxThread_t             *thread;  ///
    osRtxMessageQueue_t           *mq;  ///
    void                (*tick)(void);  ///
  } timer;
  struct {                              ///
    uint16_t                      max;  ///
    uint16_t                      cnt;  ///
    uint16_t                       in;  ///
    uint16_t                      out;  ///
    void                       **data;  ///
  } isr_queue;
  struct {                                      ///
    void          (*thread)(osRtxThread_t*);    ///
    void (*event_flags)(osRtxEventFlags_t*);    ///
    void    (*semaphore)(osRtxSemaphore_t*);    ///
    void (*memory_pool)(osRtxMemoryPool_t*);    ///
    void        (*message)(osRtxMessage_t*);    ///
  } post_process;
  struct {                              ///
    void                       *stack;  ///
    void                     *mp_data;  ///
    void                     *mq_data;  ///
    void                      *common;  ///
  } mem;
  struct {                              ///
    osRtxMpInfo_t              *stack;  ///
    osRtxMpInfo_t             *thread;  ///
    osRtxMpInfo_t              *timer;  ///
    osRtxMpInfo_t        *event_flags;  ///
    osRtxMpInfo_t              *mutex;  ///
    osRtxMpInfo_t          *semaphore;  ///
    osRtxMpInfo_t        *memory_pool;  ///
    osRtxMpInfo_t      *message_queue;  ///
  } mpi;
} osRtxInfo_t;
 
extern osRtxInfo_t osRtxInfo;           ///
 
/// OS Runtime Object Memory Usage structure
typedef struct {
  uint32_t cnt_alloc;                   ///
  uint32_t cnt_free;                    ///
  uint32_t max_used;                    ///
} osRtxObjectMemUsage_t;
 
/// OS Runtime Object Memory Usage variables
extern osRtxObjectMemUsage_t osRtxThreadMemUsage;
extern osRtxObjectMemUsage_t osRtxTimerMemUsage;
extern osRtxObjectMemUsage_t osRtxEventFlagsMemUsage;
extern osRtxObjectMemUsage_t osRtxMutexMemUsage;
extern osRtxObjectMemUsage_t osRtxSemaphoreMemUsage;
extern osRtxObjectMemUsage_t osRtxMemoryPoolMemUsage;
extern osRtxObjectMemUsage_t osRtxMessageQueueMemUsage;
 
 
//  ==== OS API definitions ====
 
/// Object Limits definitions
#define osRtxThreadFlagsLimit    31U    ///
#define osRtxEventFlagsLimit     31U    ///
#define osRtxMutexLockLimit      255U   ///
#define osRtxSemaphoreTokenLimit 65535U ///
 
/// Control Block sizes
#define osRtxThreadCbSize        sizeof(osRtxThread_t)
#define osRtxTimerCbSize         sizeof(osRtxTimer_t)
#define osRtxEventFlagsCbSize    sizeof(osRtxEventFlags_t)
#define osRtxMutexCbSize         sizeof(osRtxMutex_t)
#define osRtxSemaphoreCbSize     sizeof(osRtxSemaphore_t)
#define osRtxMemoryPoolCbSize    sizeof(osRtxMemoryPool_t)
#define osRtxMessageQueueCbSize  sizeof(osRtxMessageQueue_t)
 
/// Memory size in bytes for Memory Pool storage.
/// \param         block_count   maximum number of memory blocks in memory pool.
/// \param         block_size    memory block size in bytes.
#define osRtxMemoryPoolMemSize(block_count, block_size) \
  (4*(block_count)*(((block_size)+3)/4))
 
/// Memory size in bytes for Message Queue storage.
/// \param         msg_count     maximum number of messages in queue.
/// \param         msg_size      maximum message size in bytes.
#define osRtxMessageQueueMemSize(msg_count, msg_size) \
  (4*(msg_count)*(3+(((msg_size)+3)/4)))
 
 
//  ==== OS External Functions ====
 
/// OS Error Codes
#define osRtxErrorStackUnderflow        1U  ///
#define osRtxErrorISRQueueOverflow      2U  ///
#define osRtxErrorTimerQueueOverflow    3U  ///
#define osRtxErrorClibSpace             4U  ///
#define osRtxErrorClibMutex             5U  ///
 
/// OS Error Callback function
extern uint32_t osRtxErrorNotify (uint32_t code, void *object_id);
 
/// OS Idle Thread
extern void osRtxIdleThread (void *argument);
 
/// OS Exception handlers
extern void SVC_Handler     (void);
extern void PendSV_Handler  (void);
extern void SysTick_Handler (void);
 
 
//  ==== OS External Configuration ====
 
/// OS Configuration flags
#define osRtxConfigPrivilegedMode   (1UL
#define osRtxConfigStackCheck       (1UL
#define osRtxConfigStackWatermark   (1UL
 
/// OS Configuration structure
typedef struct {
  uint32_t                             flags;   ///
  uint32_t                         tick_freq;   ///
  uint32_t                     robin_timeout;   ///
  struct {                                      ///
    void                              **data;   ///
    uint16_t                             max;   ///
    uint16_t                         padding;
  } isr_queue;
  struct {                                      ///
    void                         *stack_addr;   ///
    uint32_t                      stack_size;   ///
    void                       *mp_data_addr;   ///
    uint32_t                    mp_data_size;   ///
    void                       *mq_data_addr;   ///
    uint32_t                    mq_data_size;   ///
    void                        *common_addr;   ///
    uint32_t                     common_size;   ///
  } mem;
  struct {                                      ///
    osRtxMpInfo_t                     *stack;   ///
    osRtxMpInfo_t                    *thread;   ///
    osRtxMpInfo_t                     *timer;   ///
    osRtxMpInfo_t               *event_flags;   ///
    osRtxMpInfo_t                     *mutex;   ///
    osRtxMpInfo_t                 *semaphore;   ///
    osRtxMpInfo_t               *memory_pool;   ///
    osRtxMpInfo_t             *message_queue;   ///
  } mpi;
  uint32_t                 thread_stack_size;   ///
  const 
  osThreadAttr_t           *idle_thread_attr;   ///
  const
  osThreadAttr_t          *timer_thread_attr;   ///
  const
  osMessageQueueAttr_t        *timer_mq_attr;   ///
  uint32_t                     timer_mq_mcnt;   ///
} osRtxConfig_t;
 
extern const osRtxConfig_t osRtxConfig;         ///
 
 
#ifdef  __cplusplus
}
#endif
 
#endif  // RTX_OS_H_

Timeout Value

Timeout values are an argument to several osXxx functions to allow time for resolving a request. A timeout value of 0 means that the RTOS does not wait and the function returns instantly, even when no resource is available. A timeout value of osWaitForever means that the RTOS waits infinitely until a resource becomes available. Or one forces the thread to resume using osThreadResume which is discouraged.

The timeout value specifies the number of timer ticks until the time delay elapses. The value is an upper bound and depends on the actual time elapsed since the last timer tick.

Examples:

  • timeout value 0 : the system does not wait, even when no resource is available the RTOS function returns instantly.
  • timeout value 1 : the system waits until the next timer tick occurs; depending on the previous timer tick, it may be a very short wait time.
  • timeout value 2 : actual wait time is between 1 and 2 timer ticks.
  • timeout value osWaitForever : system waits infinite until a resource becomes available.

TimerValues.png

Example of timeout using osDelay()

Calls from Interrupt Service Routines

The following CMSIS-RTOS2 functions can be called from threads and Interrupt Service Routines (ISR):

  • osKernelGetInfo, osKernelGetState, osKernelGetTickCount, osKernelGetTickFreq, osKernelGetSysTimerCount, osKernelGetSysTimerFreq
  • osThreadFlagsSet
  • osEventFlagsSet, osEventFlagsClear, osEventFlagsGet, osEventFlagsWait
  • osSemaphoreAcquire, osSemaphoreRelease, osSemaphoreGetCount
  • osMemoryPoolAlloc, osMemoryPoolFree, osMemoryPoolGetCapacity, osMemoryPoolGetBlockSize, osMemoryPoolGetCount, osMemoryPoolGetSpace
  • osMessageQueuePut, osMessageQueueGet, osMessageQueueGetCapacity, osMessageQueueGetMsgSize, osMessageQueueGetCount, osMessageQueueGetSpace

Functions that cannot be called from an ISR are verifying the interrupt status and return the status code osErrorISR, in case they are called from an ISR context. In some implementations, this condition might be caught using the HARD_FAULT vector.

Оставить ответ