diff --git a/samples/portability/cmsis_rtos_v1/philosophers/CMakeLists.txt b/samples/portability/cmsis_rtos_v1/philosophers/CMakeLists.txt new file mode 100644 index 00000000000..0a6c43f046f --- /dev/null +++ b/samples/portability/cmsis_rtos_v1/philosophers/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.8.2) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(philosophers_cmsis_rtos_v1) + +target_include_directories(app PRIVATE $ENV{ZEPHYR_BASE}/include/cmsis_rtos_v1) +target_sources(app PRIVATE src/main.c) diff --git a/samples/portability/cmsis_rtos_v1/philosophers/README.rst b/samples/portability/cmsis_rtos_v1/philosophers/README.rst new file mode 100644 index 00000000000..896e35e3522 --- /dev/null +++ b/samples/portability/cmsis_rtos_v1/philosophers/README.rst @@ -0,0 +1,56 @@ +.. _cmsis_rtos_v1-sample: + +Dining Philosophers (CMSI RTOS V1 APIs) +####################################### + +Overview +******** +This sample implements a solution to the `Dining Philosophers problem +`_ (a classic +multi-thread synchronization problem) using CMSIS RTOS V1 APIs. This particular +implementation demonstrates the usage of multiple preemptible and cooperative +threads of differing priorities, as well as mutex/semaphore and thread sleeping +which uses CMSIS RTOS V1 API implementation in Zephyr. + +The philosopher always tries to get the lowest fork first (f1 then f2). When +done, he will give back the forks in the reverse order (f2 then f1). If he +gets two forks, he is EATING. Otherwise, he is THINKING. Transitional states +are shown as well, such as STARVING when the philosopher is hungry but the +forks are not available, and HOLDING ONE FORK when a philosopher is waiting +for the second fork to be available. + +Each Philosopher will randomly alternate between the EATING and THINKING state. + + +Building and Running +******************** + +This project outputs to the console. It can be built and executed +on QEMU as follows: + +.. zephyr-app-commands:: + :zephyr-app: samples/philosophers + :host-os: unix + :board: qemu_x86 + :goals: run + :compact: + +Sample Output +============= + +.. code-block:: console + + Philosopher 0 [C:-2] THINKING [ 500 ms ] + Philosopher 1 [C:-1] THINKING [ 375 ms ] + Philosopher 2 [P: 0] THINKING [ 575 ms ] + Philosopher 3 [P: 1] EATING [ 525 ms ] + Philosopher 4 [P: 2] THINKING [ 800 ms ] + Philosopher 5 [P: 3] EATING [ 625 ms ] + + Demo Description + ---------------- + An implementation of a solution to the Dining Philosophers + problem (a classic multi-thread synchronization problem) using + CMSIS RTOS V1 APIs. This particular implementation demonstrates the + usage of multiple preemptible threads of differing + priorities, as well as semaphores and thread sleeping. diff --git a/samples/portability/cmsis_rtos_v1/philosophers/prj.conf b/samples/portability/cmsis_rtos_v1/philosophers/prj.conf new file mode 100644 index 00000000000..4a07a5eca37 --- /dev/null +++ b/samples/portability/cmsis_rtos_v1/philosophers/prj.conf @@ -0,0 +1,15 @@ +CONFIG_STDOUT_CONSOLE=n +CONFIG_SYS_CLOCK_TICKS_PER_SEC=100 +CONFIG_ASSERT=y +CONFIG_ASSERT_LEVEL=2 +CONFIG_CMSIS_RTOS_V1=y +CONFIG_NUM_PREEMPT_PRIORITIES=56 +CONFIG_HEAP_MEM_POOL_SIZE=256 +CONFIG_THREAD_NAME=y +CONFIG_THREAD_STACK_INFO=y +CONFIG_THREAD_MONITOR=y +CONFIG_INIT_STACKS=y +CONFIG_POLL=y +CONFIG_SCHED_SCALABLE=y +CONFIG_THREAD_CUSTOM_DATA=y + diff --git a/samples/portability/cmsis_rtos_v1/philosophers/sample.yaml b/samples/portability/cmsis_rtos_v1/philosophers/sample.yaml new file mode 100644 index 00000000000..71c7bf12fa3 --- /dev/null +++ b/samples/portability/cmsis_rtos_v1/philosophers/sample.yaml @@ -0,0 +1,27 @@ +sample: + name: CMSIS_RTOS_V1 Dining Philosophers +common: + extra_args: "-DDEBUG_PRINTF=1" + tags: cmsis_rtos_v1_philosopher + min_ram: 32 + min_flash: 34 + harness: console + harness_config: + type: multi_line + ordered: false + regex: + - ".*STARVING.*" + - ".*DROPPED ONE FORK.*" + - ".*THINKING.*" + - ".*EATING.*" +tests: + sample.philosopher: + tags: cmsis_rtos_v1_philosopher + sample.philosopher.tracing: + min_ram: 32 + extra_configs: + - CONFIG_SEGGER_SYSTEMVIEW=y + sample.philosopher.same_prio: + extra_args: "-DSAME_PRIO=1" + sample.philosopher.semaphores: + extra_args: "-DFORKS=SEMAPHORES" diff --git a/samples/portability/cmsis_rtos_v1/philosophers/src/main.c b/samples/portability/cmsis_rtos_v1/philosophers/src/main.c new file mode 100644 index 00000000000..0c9bdd9d226 --- /dev/null +++ b/samples/portability/cmsis_rtos_v1/philosophers/src/main.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * Dining philosophers demo + * + * The demo can be configured to use SEMAPHORES or MUTEXES as object types for its + * synchronization. To configure a specific object, set the value of FORKS to one + * of these. + * + * By default, the demo uses MUTEXES. + * + * The demo can be configured to work with threads of the same priority or + * not. If using different priorities of preemptible threads; if using one + * priority, there will be six preemptible threads of priority normal. + * + * By default, the demo uses different priorities. + * + * The number of threads is set via NUM_PHIL. The demo has only been tested + * with six threads. In theory it should work with any number of threads, but + * not without making changes to the thread attributes and corresponding kernel + * object attributes array in the phil_obj_abstract.h + * header file. + */ +#include +#include +#include + +#if defined(CONFIG_STDOUT_CONSOLE) +#include +#else +#include +#endif + +#include + +#include "phil_obj_abstract.h" + +#define SEMAPHORES 1 +#define MUTEXES 2 + +/**************************************/ +/* control the behaviour of the demo **/ + +#ifndef DEBUG_PRINTF +#define DEBUG_PRINTF 0 +#endif + +#ifndef FORKS +#define FORKS MUTEXES +#if 0 +#define FORKS SEMAPHORES +#endif +#endif + +#ifndef SAME_PRIO +#define SAME_PRIO 0 +#endif + +#ifndef NUM_PHIL +#define NUM_PHIL 6 +#endif + +/* end - control behaviour of the demo */ +/***************************************/ +osSemaphoreId forks[NUM_PHIL]; + +#define fork(x) (forks[x]) + +#define STACK_SIZE 512 + +/* + * There are multiple threads doing printfs and they may conflict. + * Therefore use puts() instead of printf(). + */ +#if defined(CONFIG_STDOUT_CONSOLE) +#define PRINTF(...) { char output[256]; \ + sprintf(output, __VA_ARGS__); puts(output); } +#else +#define PRINTF(...) printk(__VA_ARGS__) +#endif + +#if DEBUG_PRINTF +#define PR_DEBUG PRINTF +#else +#define PR_DEBUG(...) +#endif + +#include "phil_obj_abstract.h" + +static void set_phil_state_pos(int id) +{ +#if !DEBUG_PRINTF + PRINTF("\x1b[%d;%dH", id + 1, 1); +#endif +} + +#include +static void print_phil_state(int id, const char *fmt, s32_t delay) +{ + int prio = osThreadGetPriority(osThreadGetId()); + + set_phil_state_pos(id); + + PRINTF("Philosopher %d [%s:%s%d] ", + id, prio < 0 ? "C" : "P", + prio < 0 ? "" : " ", + prio); + + if (delay) { + PRINTF(fmt, delay < 1000 ? " " : "", delay); + } else { + PRINTF(fmt, ""); + } + + PRINTF("\n"); +} + +static s32_t get_random_delay(int id, int period_in_ms) +{ + /* + * The random delay is unit-less, and is based on the philosopher's ID + * and the current uptime to create some pseudo-randomness. It produces + * a value between 0 and 31. + */ + s32_t delay = (k_uptime_get_32() / 100 * (id + 1)) & 0x1f; + + /* add 1 to not generate a delay of 0 */ + s32_t ms = (delay + 1) * period_in_ms; + + return ms; +} + +static inline int is_last_philosopher(int id) +{ + return id == (NUM_PHIL - 1); +} + +void philosopher(void const *id) +{ + fork_t fork1; + fork_t fork2; + + int my_id = (int)id; + + /* Djkstra's solution: always pick up the lowest numbered fork first */ + if (is_last_philosopher(my_id)) { + fork1 = fork(0); + fork2 = fork(my_id); + } else { + fork1 = fork(my_id); + fork2 = fork(my_id + 1); + } + + while (1) { + s32_t delay; + + print_phil_state(my_id, " STARVING ", 0); + take(fork1); + print_phil_state(my_id, " HOLDING ONE FORK ", 0); + take(fork2); + + delay = get_random_delay(my_id, 25); + print_phil_state(my_id, " EATING [ %s%d ms ] ", delay); + osDelay(delay); + + drop(fork2); + print_phil_state(my_id, " DROPPED ONE FORK ", 0); + drop(fork1); + + delay = get_random_delay(my_id, 25); + print_phil_state(my_id, " THINKING [ %s%d ms ] ", delay); + osDelay(delay); + } + +} + +osThreadDef(philosopher, osPriorityLow, 6, STACK_SIZE); + +static int new_prio(int phil) +{ +#if SAME_PRIO + return osPriorityNormal; +#else + return osPriorityLow + phil; +#endif +} + +static void start_threads(void) +{ + osThreadId id; + + for (int i = 0; i < NUM_PHIL; i++) { + int prio = new_prio(i); + id = osThreadCreate(osThread(philosopher), (void *)i); + osThreadSetPriority(id, prio); + } +} + +#define DEMO_DESCRIPTION \ + "\x1b[2J\x1b[15;1H" \ + "Demo Description\n" \ + "----------------\n" \ + "An implementation of a solution to the Dining Philosophers\n" \ + "problem (a classic multi-thread synchronization problem) using\n" \ + "CMSIS RTOS V1 APIs. This particular implementation demonstrates the\n" \ + "usage of multiple preemptible threads of differing\n" \ + "priorities, as well as %s and thread sleeping.\n", fork_type_str + +static void display_demo_description(void) +{ +#if !DEBUG_PRINTF + PRINTF(DEMO_DESCRIPTION); +#endif +} + +void main(void) +{ + display_demo_description(); + start_threads(); +} diff --git a/samples/portability/cmsis_rtos_v1/philosophers/src/phil_obj_abstract.h b/samples/portability/cmsis_rtos_v1/philosophers/src/phil_obj_abstract.h new file mode 100644 index 00000000000..43159db3b9f --- /dev/null +++ b/samples/portability/cmsis_rtos_v1/philosophers/src/phil_obj_abstract.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * Object type abstraction. + * + * Each object type that can be used as a "fork" provides: + * + * - a definition for fork_t (a reference to a fork) and fork_obj_t (an + * instance of the fork object) + * - a 'fork_init' function that initializes the object + * - a 'take' function that simulates taking the fork (eg. k_sem_take) + * - a 'drop' function that simulates dropping the fork (eg. k_mutex_unlock) + * - a 'fork_type_str' string defining the object type + * + * When using dynamic objects, the instances of the fork objects are placed + * automatically in the fork_objs[] array . References to these are in turn + * placed automatically in the forks[] array, the array of references to the + * forks. + * + * When using static objects, references to each object must be put by hand in + * the forks[] array. + */ + +#ifndef phil_obj_abstract__h +#define phil_obj_abstract__h + +#if FORKS == SEMAPHORES + osSemaphoreDef(0); + osSemaphoreDef(1); + osSemaphoreDef(2); + osSemaphoreDef(3); + osSemaphoreDef(4); + osSemaphoreDef(5); + + #define fork_init(x) osSemaphoreCreate(osSemaphore(##x), 1) + #define take(x) osSemaphoreWait(x, osWaitForever) + #define drop(x) osSemaphoreRelease(x) + #define fork_type_str "semaphores" + #define fork_t osSemaphoreId + +#elif FORKS == MUTEXES + osMutexDef(0); + osMutexDef(1); + osMutexDef(2); + osMutexDef(3); + osMutexDef(4); + osMutexDef(5); + + #define fork_init(x) osMutexCreate(osMutex(##x)); + #define take(x) osMutexWait(x, 0) + #define drop(x) osMutexRelease(x) + #define fork_type_str "mutexes" + #define fork_t osMutexId +#else + #error unknown fork type +#endif +#endif /* phil_obj_abstract__h */ diff --git a/samples/portability/portability.rst b/samples/portability/portability.rst new file mode 100644 index 00000000000..1be238d794c --- /dev/null +++ b/samples/portability/portability.rst @@ -0,0 +1,10 @@ +.. _portability-samples: + +Portability Samples +################### + +.. toctree:: + :maxdepth: 2 + :glob: + + **/* diff --git a/samples/samples.rst b/samples/samples.rst index dacc0bb1929..464fa0dfeb8 100644 --- a/samples/samples.rst +++ b/samples/samples.rst @@ -5,7 +5,7 @@ Samples and Demos .. toctree:: - :maxdepth: 2 + :maxdepth: 3 :glob: kernel @@ -20,6 +20,7 @@ Samples and Demos application_development/* display/* shields/* + portability/* To add a new sample document, please use the template available under :file:`doc/templates/sample.tmpl`