From e3f6c0f340104c60d98026b9188228d4cc40858d Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 4 Jun 2020 21:52:57 +0200 Subject: [PATCH 1/7] core: make idle thread optional --- Makefile.dep | 3 +++ core/include/sched.h | 16 +++++++++++++++- core/init.c | 15 +++++++++------ core/sched.c | 9 ++++++--- makefiles/features_modules.inc.mk | 5 +++++ 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Makefile.dep b/Makefile.dep index 77c6528a67e2..65abf25c1145 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -1074,6 +1074,9 @@ FEATURES_REQUIRED += $(filter arch_%,$(FEATURES_PROVIDED)) # always select CPU core features FEATURES_REQUIRED += $(filter cpu_core_%,$(FEATURES_PROVIDED)) +# don't use idle thread if architecture has needed support +FEATURES_OPTIONAL += no_idle_thread + ifneq (,$(filter ecc_%,$(USEMODULE))) USEMODULE += ecc endif diff --git a/core/include/sched.h b/core/include/sched.h index 5eb533443fb0..f37092cc4665 100644 --- a/core/include/sched.h +++ b/core/include/sched.h @@ -203,8 +203,22 @@ extern clist_node_t sched_runqueues[SCHED_PRIO_LEVELS]; */ NORETURN void sched_task_exit(void); -#if IS_USED(MODULE_SCHED_CB) || defined(DOXYGEN) +/** + * @brief Set CPU to idle mode (CPU dependent) + * + * Only used when there's no idle thread. + * + * This function will be called by the scheduler when there's no runnable thread. + * It will be called from ISR context, and *must* allow other ISR handlers to be run. + * E.g., on Cortex-M, the PendSV priority is temporarily lowered (set to higher + * value) in order to enable other exceptions to be run. + * + * This function should also invoke setting a low power mode, e.g., by calling + * 'pm_set_lowest()'. + */ +void sched_arch_idle(void); +#if IS_USED(MODULE_SCHED_CB) || defined(DOXYGEN) /** * @brief Scheduler run callback * diff --git a/core/init.c b/core/init.c index 5b8e905fdfc9..a679867a370a 100644 --- a/core/init.c +++ b/core/init.c @@ -53,6 +53,9 @@ static void *main_trampoline(void *arg) return NULL; } +static char main_stack[THREAD_STACKSIZE_MAIN]; +static char idle_stack[THREAD_STACKSIZE_IDLE]; + static void *idle_thread(void *arg) { (void)arg; @@ -64,17 +67,17 @@ static void *idle_thread(void *arg) return NULL; } -static char main_stack[THREAD_STACKSIZE_MAIN]; -static char idle_stack[THREAD_STACKSIZE_IDLE]; void kernel_init(void) { irq_disable(); - thread_create(idle_stack, sizeof(idle_stack), - THREAD_PRIORITY_IDLE, - THREAD_CREATE_WOUT_YIELD | THREAD_CREATE_STACKTEST, - idle_thread, NULL, "idle"); + if (IS_USED(MODULE_CORE_IDLE_THREAD)) { + thread_create(idle_stack, sizeof(idle_stack), + THREAD_PRIORITY_IDLE, + THREAD_CREATE_WOUT_YIELD | THREAD_CREATE_STACKTEST, + idle_thread, NULL, "idle"); + } thread_create(main_stack, sizeof(main_stack), THREAD_PRIORITY_MAIN, diff --git a/core/sched.c b/core/sched.c index b76c67de65cf..981c28b70b9f 100644 --- a/core/sched.c +++ b/core/sched.c @@ -80,11 +80,14 @@ int __attribute__((used)) sched_run(void) { sched_context_switch_request = 0; +#ifndef MODULE_CORE_IDLE_THREAD + while (!runqueue_bitcache) { + sched_arch_idle(); + } +#endif + thread_t *active_thread = (thread_t *)sched_active_thread; - /* The bitmask in runqueue_bitcache is never empty, - * since the threading should not be started before at least the idle thread was started. - */ int nextrq = bitarithm_lsb(runqueue_bitcache); thread_t *next_thread = container_of(sched_runqueues[nextrq].next->next, thread_t, rq_entry); diff --git a/makefiles/features_modules.inc.mk b/makefiles/features_modules.inc.mk index 3d69156e3547..8a8bcbff7653 100644 --- a/makefiles/features_modules.inc.mk +++ b/makefiles/features_modules.inc.mk @@ -28,3 +28,8 @@ endif # select cortexm_svc pseudomodule if the corresponding feature is used USEMODULE += $(filter cortexm_svc, $(FEATURES_USED)) + +# select core_idle_thread if the feature no_idle_thread is *not* used +ifeq (, $(filter no_idle_thread, $(FEATURES_USED))) + USEMODULE += core_idle_thread +endif From 0ff9e554eb712fe4f37ea46e232eaed9c20b9071 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Mon, 8 Jun 2020 12:36:01 +0200 Subject: [PATCH 2/7] cpu/cortexm: implement sched_arch_idle() and disable idle thread --- cpu/cortexm_common/Kconfig | 2 ++ cpu/cortexm_common/Makefile.features | 5 +++++ cpu/cortexm_common/thread_arch.c | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/cpu/cortexm_common/Kconfig b/cpu/cortexm_common/Kconfig index 245a97942997..271d23c0ed1a 100644 --- a/cpu/cortexm_common/Kconfig +++ b/cpu/cortexm_common/Kconfig @@ -13,11 +13,13 @@ config CPU_ARCH_ARMV7M bool select HAS_ARCH_ARM select HAS_ARCH_32BIT + select HAS_NO_IDLE_THREAD config CPU_ARCH_ARMV8M bool select HAS_ARCH_ARM select HAS_ARCH_32BIT + select HAS_NO_IDLE_THREAD config CPU_ARCH default "armv6m" if CPU_ARCH_ARMV6M diff --git a/cpu/cortexm_common/Makefile.features b/cpu/cortexm_common/Makefile.features index 974d78d60698..eef3d0ea8edf 100644 --- a/cpu/cortexm_common/Makefile.features +++ b/cpu/cortexm_common/Makefile.features @@ -30,3 +30,8 @@ else ifeq ($(CPU_CORE),cortex-m23) else $(error Unkwnown cortexm core: $(CPU_CORE)) endif + +# cortex-m3 and higher don't need the idle thread +ifneq (,$(filter armv7m armv8m,$(CPU_ARCH))) + FEATURES_PROVIDED += no_idle_thread +endif diff --git a/cpu/cortexm_common/thread_arch.c b/cpu/cortexm_common/thread_arch.c index 914fd152806d..16c5efcef6f9 100644 --- a/cpu/cortexm_common/thread_arch.c +++ b/cpu/cortexm_common/thread_arch.c @@ -446,3 +446,24 @@ void __attribute__((used)) isr_svc(void) SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } #endif /* MODULE_CORTEXM_SVC */ + +void sched_arch_idle(void) +{ + /* by default, PendSV has the same priority as other ISRs. + * In this function, we temporarily lower the priority (set higher value), + * allowing other ISRs to interrupt. + * + * According to [this](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHJICIE.html), + * dynamically changing the priority is not supported on CortexM0(+). + */ + NVIC_SetPriority(PendSV_IRQn, CPU_CORTEXM_PENDSV_IRQ_PRIO + 1); + __DSB(); + __ISB(); +#ifdef MODULE_PM_LAYERED + void pm_set_lowest(void); + pm_set_lowest(); +#else + __WFI(); +#endif + NVIC_SetPriority(PendSV_IRQn, CPU_CORTEXM_PENDSV_IRQ_PRIO); +} From baca419934d568ae58a343e78f484d565a93e6cf Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 16 Jun 2020 13:18:53 +0200 Subject: [PATCH 3/7] sys/schedstatistics: handle if PIDs are KERNEL_PID_UNDEF Previously, sched_statistics_cb() was always called with two valid PIDs. Now it is possible (when the idle thread is not used) that one of the two might be KERNEL_PID_UNDEF, as the callback might be called when descheduling the last thread, or scheduling the first. --- sys/schedstatistics/schedstatistics.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sys/schedstatistics/schedstatistics.c b/sys/schedstatistics/schedstatistics.c index 0f5c78d0dd17..92743ca6ba30 100644 --- a/sys/schedstatistics/schedstatistics.c +++ b/sys/schedstatistics/schedstatistics.c @@ -31,15 +31,18 @@ void sched_statistics_cb(kernel_pid_t active_thread, kernel_pid_t next_thread) { uint32_t now = xtimer_now().ticks32; - /* Update active thread runtime, there is always an active thread since - first sched_run happens when main_trampoline gets scheduled */ - schedstat_t *active_stat = &sched_pidlist[active_thread]; - active_stat->runtime_ticks += now - active_stat->laststart; + /* Update active thread stats */ + if (active_thread != KERNEL_PID_UNDEF) { + schedstat_t *active_stat = &sched_pidlist[active_thread]; + active_stat->runtime_ticks += now - active_stat->laststart; + } /* Update next_thread stats */ - schedstat_t *next_stat = &sched_pidlist[next_thread]; - next_stat->laststart = now; - next_stat->schedules++; + if (next_thread != KERNEL_PID_UNDEF) { + schedstat_t *next_stat = &sched_pidlist[next_thread]; + next_stat->laststart = now; + next_stat->schedules++; + } } void init_schedstatistics(void) From d199865a128b8c4e72a277406dfd3b3fd286f6ee Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 17 Jun 2020 10:57:49 +0200 Subject: [PATCH 4/7] core: sched: correctly unschedule when there's no idle thread --- core/sched.c | 55 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/core/sched.c b/core/sched.c index 981c28b70b9f..011171861c6a 100644 --- a/core/sched.c +++ b/core/sched.c @@ -76,18 +76,45 @@ static void (*sched_cb) (kernel_pid_t active_thread, kernel_pid_t next_thread) = NULL; #endif -int __attribute__((used)) sched_run(void) +static void _unschedule(thread_t *active_thread) { - sched_context_switch_request = 0; + if (active_thread->status == STATUS_RUNNING) { + active_thread->status = STATUS_PENDING; + } -#ifndef MODULE_CORE_IDLE_THREAD - while (!runqueue_bitcache) { - sched_arch_idle(); +#ifdef SCHED_TEST_STACK + if (*((uintptr_t *)active_thread->stack_start) != + (uintptr_t)active_thread->stack_start) { + LOG_WARNING( + "scheduler(): stack overflow detected, pid=%" PRIkernel_pid "\n", + active_thread->pid); + } +#endif +#ifdef MODULE_SCHED_CB + if (sched_cb) { + sched_cb(active_thread->pid, KERNEL_PID_UNDEF); } #endif +} +int __attribute__((used)) sched_run(void) +{ + sched_context_switch_request = 0; thread_t *active_thread = (thread_t *)sched_active_thread; + if (!IS_USED(MODULE_CORE_IDLE_THREAD)) { + if (!runqueue_bitcache) { + if (active_thread) { + _unschedule(active_thread); + active_thread = NULL; + } + + while (!runqueue_bitcache) { + sched_arch_idle(); + } + } + } + int nextrq = bitarithm_lsb(runqueue_bitcache); thread_t *next_thread = container_of(sched_runqueues[nextrq].next->next, thread_t, rq_entry); @@ -105,26 +132,12 @@ int __attribute__((used)) sched_run(void) } if (active_thread) { - if (active_thread->status == STATUS_RUNNING) { - active_thread->status = STATUS_PENDING; - } - -#ifdef SCHED_TEST_STACK - if (*((uintptr_t *)active_thread->stack_start) != - (uintptr_t)active_thread->stack_start) { - LOG_WARNING( - "scheduler(): stack overflow detected, pid=%" PRIkernel_pid "\n", - active_thread->pid); - } -#endif + _unschedule(active_thread); } #ifdef MODULE_SCHED_CB if (sched_cb) { - /* Use `sched_active_pid` instead of `active_thread` since after `sched_task_exit()` is - called `active_thread` is set to NULL while `sched_active_thread` isn't updated until - `next_thread` is scheduled*/ - sched_cb(sched_active_pid, next_thread->pid); + sched_cb(KERNEL_PID_UNDEF, next_thread->pid); } #endif From 3af04d0da8af72fed9c72eb9b5ccef6e25820bfc Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 17 Jun 2020 13:23:17 +0200 Subject: [PATCH 5/7] kconfigs/features: add "no_idle_thread" --- kconfigs/Kconfig.features | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index 1753b900da2f..465f8d342a3c 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -67,6 +67,11 @@ config HAS_ETHERNET help Indicates that Ethernet connectivity is present. +config HAS_NO_IDLE_THREAD + bool + help + Indicates that this MCU doesn't need the idle thread + config HAS_MOTOR_DRIVER bool help From 0ccf94fa2389e4e0083cbbc4e07b77a942dff474 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 23 Jun 2020 13:37:47 +0200 Subject: [PATCH 6/7] tests/shell: don't expect idle thread in ps output --- tests/shell/tests/01-run.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/shell/tests/01-run.py b/tests/shell/tests/01-run.py index ecd4f43b9268..d64e3c7958c1 100755 --- a/tests/shell/tests/01-run.py +++ b/tests/shell/tests/01-run.py @@ -24,8 +24,7 @@ EXPECTED_PS = ( '\tpid | state Q | pri', - '\t 1 | pending Q | 15', - '\t 2 | running Q | 7' + '\t \d | running Q | 7' ) # In native we are directly executing the binary (no terminal program). We must @@ -102,6 +101,8 @@ ('end_test', '[TEST_END]'), ) +CMDS_REGEX = {'ps'} + BOARD = os.environ['BOARD'] @@ -112,10 +113,14 @@ def print_error(message): def check_cmd(child, cmd, expected): + regex = cmd in CMDS_REGEX child.expect(PROMPT) child.sendline(cmd) for line in expected: - child.expect_exact(line) + if regex: + child.expect(line) + else: + child.expect_exact(line) def check_startup(child): From f85f78211f51aac2d31b891df3146cd09fa52173 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 25 Jun 2020 16:12:23 +0200 Subject: [PATCH 7/7] tests/unittests: increase main stacksize to THREAD_STACKSIZE_LARGE --- tests/unittests/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unittests/Makefile b/tests/unittests/Makefile index f2f70dcad361..3a36d50523fe 100644 --- a/tests/unittests/Makefile +++ b/tests/unittests/Makefile @@ -28,6 +28,9 @@ BASELIBS += $(UNIT_TESTS:%=$(BINDIR)/%.a) INCLUDES += -I$(RIOTBASE)/tests/unittests/common +# some tests need more stack +CFLAGS += -DTHREAD_STACKSIZE_MAIN=THREAD_STACKSIZE_LARGE + include $(RIOTBASE)/Makefile.include .PHONY: $(UNIT_TESTS)