Browse Source
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>pull/11892/head
8 changed files with 405 additions and 1 deletions
@ -0,0 +1,6 @@
@@ -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) |
@ -0,0 +1,56 @@
@@ -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. |
@ -0,0 +1,15 @@
@@ -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 |
||||
|
@ -0,0 +1,27 @@
@@ -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" |
@ -0,0 +1,226 @@
@@ -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(); |
||||
} |
@ -0,0 +1,63 @@
@@ -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 */ |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
.. _portability-samples: |
||||
|
||||
Portability Samples |
||||
################### |
||||
|
||||
.. toctree:: |
||||
:maxdepth: 2 |
||||
:glob: |
||||
|
||||
**/* |
Loading…
Reference in new issue