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..011171861c6a 100644 --- a/core/sched.c +++ b/core/sched.c @@ -76,15 +76,45 @@ static void (*sched_cb) (kernel_pid_t active_thread, kernel_pid_t next_thread) = NULL; #endif +static void _unschedule(thread_t *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 +#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; - /* The bitmask in runqueue_bitcache is never empty, - * since the threading should not be started before at least the idle thread was started. - */ + 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); @@ -102,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 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); +} 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 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 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) 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): 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)