Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: make idle thread optional #14224

Merged
merged 7 commits into from
Jun 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion core/include/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
15 changes: 9 additions & 6 deletions core/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down
56 changes: 36 additions & 20 deletions core/sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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

Expand Down
2 changes: 2 additions & 0 deletions cpu/cortexm_common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions cpu/cortexm_common/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -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
21 changes: 21 additions & 0 deletions cpu/cortexm_common/thread_arch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
5 changes: 5 additions & 0 deletions kconfigs/Kconfig.features
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions makefiles/features_modules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
17 changes: 10 additions & 7 deletions sys/schedstatistics/schedstatistics.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
bergzand marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand Down
11 changes: 8 additions & 3 deletions tests/shell/tests/01-run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -102,6 +101,8 @@
('end_test', '[TEST_END]'),
)

CMDS_REGEX = {'ps'}

BOARD = os.environ['BOARD']


Expand All @@ -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):
Expand Down
3 changes: 3 additions & 0 deletions tests/unittests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down