samples: cmsis_rtos_v1: Demo with Dining philosopher's problem
Sample application to demonstrate usage of cmsis_rtos_v1 APIs
with dining philosopher's problem implementation.
This covers semaphores, mutex and thread APIs of CMSIS RTOS V1.
Signed-off-by: Spoorthi K <spoorthi.k@intel.com>
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 0000000..0a6c43f
--- /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 0000000..896e35e
--- /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
+<https://en.wikipedia.org/wiki/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 0000000..4a07a5e
--- /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 0000000..71c7bf1
--- /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 0000000..0c9bdd9
--- /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 <kernel.h>
+#include <cmsis_os.h>
+#include <zephyr.h>
+
+#if defined(CONFIG_STDOUT_CONSOLE)
+#include <stdio.h>
+#else
+#include <misc/printk.h>
+#endif
+
+#include <misc/__assert.h>
+
+#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 <stdarg.h>
+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 0000000..43159db
--- /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 0000000..1be238d
--- /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 dacc0bb..464fa0d 100644
--- a/samples/samples.rst
+++ b/samples/samples.rst
@@ -5,7 +5,7 @@
.. toctree::
- :maxdepth: 2
+ :maxdepth: 3
:glob:
kernel
@@ -20,6 +20,7 @@
application_development/*
display/*
shields/*
+ portability/*
To add a new sample document, please use the template available under
:file:`doc/templates/sample.tmpl`