Skip to content

Commit

Permalink
kernel: introduce single-threaded kernel
Browse files Browse the repository at this point in the history
For very constrained systems, like bootloaders.

Only the main thread is available, so a main() function must be
provided. Kernel objects where pending is in play will not behave as
expected, since the main thread cannot pend, it being the only thread in
the system. Usage of objects should be limited to using K_NO_WAIT as the
timeout parameter, effectively polling on the object.

Change-Id: Iae0261daa98bff388dc482797cde69f94e2e95cc
Signed-off-by: Benjamin Walsh <[email protected]>
  • Loading branch information
benwrs authored and bboccoqq committed Dec 15, 2016
1 parent 48db0b3 commit b12a8e0
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 3 deletions.
1 change: 1 addition & 0 deletions arch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ config SIMPLE_FATAL_ERROR_HANDLER
prompt "Simple system fatal error handler"
bool
default n
default y if !MULTITHREADING
help
Provides an implementation of _SysFatalErrorHandler() that hard hangs
instead of aborting the faulting thread, and does not print anything,
Expand Down
7 changes: 6 additions & 1 deletion include/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -2963,8 +2963,13 @@ extern void k_cpu_atomic_idle(unsigned int key);
* private APIs that are utilized by one or more public APIs
*/

extern int _is_thread_essential(void);
#ifdef CONFIG_MULTITHREADING
extern void _init_static_threads(void);
#else
#define _init_static_threads() do { } while ((0))
#endif

extern int _is_thread_essential(void);
extern void _timer_expiration_handler(struct _timeout *t);

#ifdef __cplusplus
Expand Down
20 changes: 18 additions & 2 deletions kernel/unified/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,25 @@ config KERNEL_DEBUG

Note that debugging the kernel internals can be very verbose.

config MULTITHREADING
bool
prompt "Multi-threading"
default y
help
If disabled, only the main thread is available, so a main() function
must be provided. Interrupts are available. Kernel objects will most
probably not behave as expected, especially with regards to pending,
since the main thread cannot pend, it being the only thread in the
system.

Many drivers and subsystems will not work with this option; use only
when you REALLY know what you are doing.

config NUM_COOP_PRIORITIES
int
prompt "Number of coop priorities"
prompt "Number of coop priorities" if MULTITHREADING
default 16
default 1 if !MULTITHREADING
help
Number of cooperative priorities configured in the system. Gives access
to priorities:
Expand All @@ -58,8 +73,9 @@ config NUM_COOP_PRIORITIES

config NUM_PREEMPT_PRIORITIES
int
prompt "Number of preemptible priorities"
prompt "Number of preemptible priorities" if MULTITHREADING
default 15
default 0 if !MULTITHREADING
help
Number of preemptible priorities available in the system. Gives access
to priorities 0 to CONFIG_NUM_PREEMPT_PRIORITIES - 1.
Expand Down
4 changes: 4 additions & 0 deletions kernel/unified/include/ksched.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ static inline int _is_idle_thread(void *entry_point)
return entry_point == idle;
}

#ifdef CONFIG_MULTITHREADING
#define _ASSERT_VALID_PRIO(prio, entry_point) do { \
__ASSERT(((prio) == K_IDLE_PRIO && _is_idle_thread(entry_point)) || \
(_is_prio_higher_or_equal((prio), \
Expand All @@ -57,6 +58,9 @@ static inline int _is_idle_thread(void *entry_point)
K_LOWEST_APPLICATION_THREAD_PRIO, \
K_HIGHEST_APPLICATION_THREAD_PRIO); \
} while ((0))
#else
#define _ASSERT_VALID_PRIO(prio, entry_point) __ASSERT((prio) == -1, "")
#endif

/*
* The _is_prio_higher family: I created this because higher priorities are
Expand Down
2 changes: 2 additions & 0 deletions kernel/unified/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,13 @@ static void prepare_multithreading(struct k_thread *dummy_thread)
_mark_thread_as_started(_main_thread);
_add_thread_to_ready_q(_main_thread);

#ifdef CONFIG_MULTITHREADING
_new_thread(_idle_stack, IDLE_STACK_SIZE,
idle, NULL, NULL, NULL,
K_LOWEST_THREAD_PRIO, K_ESSENTIAL);
_mark_thread_as_started(_idle_thread);
_add_thread_to_ready_q(_idle_thread);
#endif

initialize_timeouts();

Expand Down
24 changes: 24 additions & 0 deletions kernel/unified/sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,26 @@
struct _kernel _kernel = {0};

/* set the bit corresponding to prio in ready q bitmap */
#ifdef CONFIG_MULTITHREADING
static void _set_ready_q_prio_bit(int prio)
{
int bmap_index = _get_ready_q_prio_bmap_index(prio);
uint32_t *bmap = &_ready_q.prio_bmap[bmap_index];

*bmap |= _get_ready_q_prio_bit(prio);
}
#endif

/* clear the bit corresponding to prio in ready q bitmap */
#ifdef CONFIG_MULTITHREADING
static void _clear_ready_q_prio_bit(int prio)
{
int bmap_index = _get_ready_q_prio_bmap_index(prio);
uint32_t *bmap = &_ready_q.prio_bmap[bmap_index];

*bmap &= ~_get_ready_q_prio_bit(prio);
}
#endif

/*
* Find the next thread to run when there is no thread in the cache and update
Expand Down Expand Up @@ -73,6 +77,7 @@ static struct k_thread *_get_ready_q_head(void)

void _add_thread_to_ready_q(struct k_thread *thread)
{
#ifdef CONFIG_MULTITHREADING
int q_index = _get_ready_q_q_index(thread->base.prio);
sys_dlist_t *q = &_ready_q.q[q_index];

Expand All @@ -82,6 +87,11 @@ void _add_thread_to_ready_q(struct k_thread *thread)
struct k_thread **cache = &_ready_q.cache;

*cache = _is_t1_higher_prio_than_t2(thread, *cache) ? thread : *cache;
#else
sys_dlist_append(&_ready_q.q[0], &thread->base.k_q_node);
_ready_q.prio_bmap[0] = 1;
_ready_q.cache = thread;
#endif
}

/*
Expand All @@ -93,6 +103,7 @@ void _add_thread_to_ready_q(struct k_thread *thread)

void _remove_thread_from_ready_q(struct k_thread *thread)
{
#ifdef CONFIG_MULTITHREADING
int q_index = _get_ready_q_q_index(thread->base.prio);
sys_dlist_t *q = &_ready_q.q[q_index];

Expand All @@ -104,6 +115,11 @@ void _remove_thread_from_ready_q(struct k_thread *thread)
struct k_thread **cache = &_ready_q.cache;

*cache = *cache == thread ? _get_ready_q_head() : *cache;
#else
_ready_q.prio_bmap[0] = 0;
_ready_q.cache = NULL;
sys_dlist_remove(&thread->base.k_q_node);
#endif
}

/* reschedule threads if the scheduler is not locked */
Expand Down Expand Up @@ -158,6 +174,7 @@ void k_sched_unlock(void)
* Callback for sys_dlist_insert_at() to find the correct insert point in a
* wait queue (priority-based).
*/
#ifdef CONFIG_MULTITHREADING
static int _is_wait_q_insert_point(sys_dnode_t *node, void *insert_prio)
{
struct k_thread *waitq_node =
Expand All @@ -168,6 +185,7 @@ static int _is_wait_q_insert_point(sys_dnode_t *node, void *insert_prio)

return _is_prio_higher((int)insert_prio, waitq_node->base.prio);
}
#endif

/* convert milliseconds to ticks */

Expand All @@ -185,6 +203,7 @@ int32_t _ms_to_ticks(int32_t ms)
/* must be called with interrupts locked */
void _pend_thread(struct k_thread *thread, _wait_q_t *wait_q, int32_t timeout)
{
#ifdef CONFIG_MULTITHREADING
sys_dlist_t *dlist = (sys_dlist_t *)wait_q;

sys_dlist_insert_at(dlist, &thread->base.k_q_node,
Expand All @@ -198,6 +217,7 @@ void _pend_thread(struct k_thread *thread, _wait_q_t *wait_q, int32_t timeout)

_add_thread_timeout(thread, wait_q, ticks);
}
#endif
}

/* pend the current thread */
Expand Down Expand Up @@ -257,6 +277,7 @@ void k_thread_priority_set(k_tid_t tid, int prio)
*/
void _move_thread_to_end_of_prio_q(struct k_thread *thread)
{
#ifdef CONFIG_MULTITHREADING
int q_index = _get_ready_q_q_index(thread->base.prio);
sys_dlist_t *q = &_ready_q.q[q_index];

Expand All @@ -270,6 +291,7 @@ void _move_thread_to_end_of_prio_q(struct k_thread *thread)
struct k_thread **cache = &_ready_q.cache;

*cache = *cache == thread ? _get_ready_q_head() : *cache;
#endif
}

void k_yield(void)
Expand All @@ -289,6 +311,7 @@ void k_yield(void)

void k_sleep(int32_t duration)
{
#ifdef CONFIG_MULTITHREADING
/* volatile to guarantee that irq_lock() is executed after ticks is
* populated
*/
Expand All @@ -313,6 +336,7 @@ void k_sleep(int32_t duration)
_add_thread_timeout(_current, NULL, ticks);

_Swap(key);
#endif
}

void k_wakeup(k_tid_t thread)
Expand Down
10 changes: 10 additions & 0 deletions kernel/unified/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,18 @@ FUNC_NORETURN void _thread_entry(void (*entry)(void *, void *, void *),
{
entry(p1, p2, p3);

#ifdef CONFIG_MULTITHREADING
if (_is_thread_essential()) {
_NanoFatalErrorHandler(_NANO_ERR_INVALID_TASK_EXIT,
&_default_esf);
}

k_thread_abort(_current);
#else
for (;;) {
k_cpu_idle();
}
#endif

/*
* Compiler can't tell that k_thread_abort() won't return and issues a
Expand Down Expand Up @@ -232,6 +238,7 @@ static void schedule_new_thread(struct k_thread *thread, int32_t delay)
#endif
}

#ifdef CONFIG_MULTITHREADING
k_tid_t k_thread_spawn(char *stack, size_t stack_size,
void (*entry)(void *, void *, void*),
void *p1, void *p2, void *p3,
Expand All @@ -247,6 +254,7 @@ k_tid_t k_thread_spawn(char *stack, size_t stack_size,

return new_thread;
}
#endif

int k_thread_cancel(k_tid_t tid)
{
Expand Down Expand Up @@ -375,6 +383,7 @@ void _k_thread_single_abort(struct k_thread *thread)
_mark_thread_as_dead(thread);
}

#ifdef CONFIG_MULTITHREADING
void _init_static_threads(void)
{
unsigned int key;
Expand Down Expand Up @@ -417,6 +426,7 @@ void _init_static_threads(void)
irq_unlock(key);
k_sched_unlock();
}
#endif

void _init_thread_base(struct _thread_base *thread_base, int priority,
uint32_t initial_state, unsigned int options)
Expand Down

0 comments on commit b12a8e0

Please sign in to comment.