lib: cmsis_rtos_v2: Dynamic thread stacks

Implemented dynamic thread stacks for CMSIS threads by declaring an
array of default sized thread stacks. Allocation cannot be done on the
heap as some architectures require strict alignment for stacks so the
macro must be used to define the stack to ensure most compatibility.

Added a Kconfig variable to limit the number of dynamic threads on the
system (they also count towards total CMSIS thread count). This is so a
developer can have fine grained control over how many dynamic threads
can be allocated because all their stacks must be allocated up front so
could use a lot of memory needlessly if oversubscribed. The default
value is 0 which effectively disabled dynamic threads but also reduces
the memory impact to almost none.

Fixed an assert bug where thread_num was being tested against the
maximum allowed CMSIS threads - it previous checked for less than or
equal which actually (due to when the increment happens) allowed there
to be one more thread. The check now correctly uses less than and only
allowed up to the defined maximum.

Signed-off-by: Carlos Stuart <carlosstuart1970@gmail.com>
This commit is contained in:
Carlos Stuart 2019-02-06 08:25:58 +00:00 committed by Anas Nashif
commit d4eb2c9014
2 changed files with 93 additions and 25 deletions

View file

@ -27,12 +27,28 @@ config CMSIS_V2_THREAD_MAX_COUNT
There's a limitation on the number of threads due to memory
related constraints.
config CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT
int "Maximum dynamic thread count in CMSIS RTOS V2 application"
default 0
range 0 255
help
Mention max number of dynamic threads in CMSIS RTOS V2 compliant
application. There's a limitation on the number of threads due to memory
related constraints. Dynamic threads are a subset of all other CMSIS
threads i.e. they also count towards that maximum too.
config CMSIS_V2_THREAD_MAX_STACK_SIZE
int "Max stack size threads can be allocated in CMSIS RTOS V2 application"
default 512
help
Mention max stack size threads can be allocated in CMSIS RTOS V2 application.
config CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE
int "Dynamic stack size threads are allocated in CMSIS RTOS V2 application"
default 0
help
Mention dynamic stack size threads are allocated in CMSIS RTOS V2 application.
config CMSIS_V2_TIMER_MAX_COUNT
int "Maximum timer count in CMSIS RTOS V2 application"
default 5

View file

@ -12,9 +12,26 @@
#include <misc/stack.h>
#include "wrapper.h"
static const osThreadAttr_t init_thread_attrs = {
.name = "ZephyrThread",
.attr_bits = osThreadDetached,
.cb_mem = NULL,
.cb_size = 0,
.stack_mem = NULL,
.stack_size = 0,
.priority = osPriorityNormal,
.tz_module = 0,
.reserved = 0,
};
static sys_dlist_t thread_list;
static struct cv2_thread cv2_thread_pool[CONFIG_CMSIS_V2_THREAD_MAX_COUNT];
static u32_t thread_num;
static u32_t thread_num_dynamic;
static K_THREAD_STACK_ARRAY_DEFINE(cv2_thread_stack_pool, \
CONFIG_CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT, \
CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE);
static inline int _is_thread_cmsis_inactive(struct k_thread *thread)
{
@ -83,36 +100,70 @@ osThreadId_t osThreadNew(osThreadFunc_t threadfunc, void *arg,
s32_t prio;
struct cv2_thread *tid;
static u32_t one_time;
BUILD_ASSERT_MSG(osPriorityISR <= CONFIG_NUM_PREEMPT_PRIORITIES,
"Configure NUM_PREEMPT_PRIORITIES to at least osPriorityISR");
__ASSERT(attr->stack_size <= CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE,
"invalid stack size\n");
__ASSERT(thread_num <= CONFIG_CMSIS_V2_THREAD_MAX_COUNT,
"Exceeded max number of threads\n");
__ASSERT(attr != NULL,
"Zephyr requirement: Pass attributes explicitly for ThreadNew\n");
/* Zephyr needs valid stack to be specified when this API is called */
__ASSERT(attr->stack_mem, "");
__ASSERT(attr->stack_size, "");
__ASSERT((attr->priority >= osPriorityIdle) &&
(attr->priority <= osPriorityISR),
"invalid priority\n");
void *stack;
size_t stack_size;
u32_t this_thread_num;
u32_t this_thread_num_dynamic;
if (k_is_in_isr()) {
return NULL;
}
if (thread_num >= CONFIG_CMSIS_V2_THREAD_MAX_COUNT) {
return NULL;
}
if (attr == NULL) {
attr = &init_thread_attrs;
}
if ((attr->stack_mem == NULL) && (thread_num_dynamic >=
CONFIG_CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT)) {
return NULL;
}
BUILD_ASSERT_MSG(osPriorityISR <= CONFIG_NUM_PREEMPT_PRIORITIES,
"Configure NUM_PREEMPT_PRIORITIES to at least osPriorityISR");
BUILD_ASSERT_MSG(CONFIG_CMSIS_V2_THREAD_DYNAMIC_MAX_COUNT <=
CONFIG_CMSIS_V2_THREAD_MAX_COUNT,
"Number of dynamic threads cannot exceed max number of threads.");
BUILD_ASSERT_MSG(CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE <=
CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE,
"Default dynamic thread stack size cannot exceed max stack size");
__ASSERT(attr->stack_size <= CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE,
"invalid stack size\n");
__ASSERT((attr->priority >= osPriorityIdle) &&
(attr->priority <= osPriorityISR),
"invalid priority\n");
if (attr->stack_mem != NULL) {
if (attr->stack_size == 0) {
return NULL;
}
}
prio = cmsis_to_zephyr_priority(attr->priority);
tid = &cv2_thread_pool[thread_num];
tid->state = attr->attr_bits;/* detached/joinable */
atomic_inc((atomic_t *)&thread_num);
this_thread_num = atomic_inc((atomic_t *)&thread_num);
tid = &cv2_thread_pool[this_thread_num];
tid->state = attr->attr_bits;
if (attr->stack_mem == NULL) {
__ASSERT(CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE > 0,
"dynamic stack size must be configured to be non-zero\n");
this_thread_num_dynamic =
atomic_inc((atomic_t *)&thread_num_dynamic);
stack_size = CONFIG_CMSIS_V2_THREAD_DYNAMIC_STACK_SIZE;
stack = cv2_thread_stack_pool[this_thread_num_dynamic];
} else {
stack_size = attr->stack_size;
stack = attr->stack_mem;
}
k_poll_signal_init(&tid->poll_signal);
k_poll_event_init(&tid->poll_event, K_POLL_TYPE_SIGNAL,
@ -128,12 +179,13 @@ osThreadId_t osThreadNew(osThreadFunc_t threadfunc, void *arg,
sys_dlist_append(&thread_list, &tid->node);
(void)k_thread_create(&tid->z_thread,
attr->stack_mem, attr->stack_size,
stack, stack_size,
(k_thread_entry_t)zephyr_thread_wrapper,
(void *)arg, NULL, threadfunc,
prio, 0, K_NO_WAIT);
k_thread_name_set(&tid->z_thread, attr->name);
memcpy(tid->name, attr->name, 16);
k_thread_name_set(&tid->z_thread, tid->name);
return (osThreadId_t)tid;
}