Skip to content

Commit 533d43a

Browse files
Alexander pinheiro paschoalettoalexpaschoaletto
authored andcommitted
samples: CBS: adding code samples for the Constant Bandwidth Server (CBS)
This commit adds two sample codes for the the CBS, one for testing a very simple logging application and other for effectively implementing it alongside other hard real-time threads. Signed-off-by: Alexander pinheiro paschoaletto <1222703@isep.ipp.pt>
1 parent 97650f1 commit 533d43a

File tree

16 files changed

+957
-0
lines changed

16 files changed

+957
-0
lines changed

samples/cbs/cbs.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.. zephyr:code-sample-category:: cbs_samples
2+
:name: Constant Bandwidth Server (CBS)
3+
:show-listing:
4+
5+
Samples that demonstrate the usage of the Constant Bandwidth Server (CBS).
6+
The CBS is an extension of the Earliest Deadline First (EDF) scheduler
7+
that tasks that automatically recalculates their deadlines when they
8+
exceed their configured timing constraints. If a task takes longer than
9+
expected, it can be preempted by awaiting tasks instead of jeopardizing
10+
their deadlines.

samples/cbs/edf/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(cbs_sample)
7+
8+
target_sources(app PRIVATE src/main.c)

samples/cbs/edf/README.rst

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
.. zephyr:code-sample:: cbs_edf
2+
:name: CBS + EDF
3+
4+
A sample to understand how the CBS works alongside regular EDF threads.
5+
6+
7+
Overview
8+
********
9+
10+
A sample that creates a Constant Bandwidth Server (CBS) to run
11+
aperiodic jobs within a periodic EDF taskset. The user can select
12+
between two examples:
13+
14+
* 1: one CBS with (budget, period) = (3, 8) time units and one
15+
periodic EDF task with (execution_time, period) = (4, 7)
16+
time units. The EDF task period is equal to the deadline.
17+
18+
* 2: one CBS with (budget, period) = (2, 6) time units and two
19+
periodic EDF tasks with (execution_time, relative_deadline) =
20+
(2, 6) and (3, 9) time units, respectively. The EDF tasks have
21+
periods equal to deadlines.
22+
23+
24+
the time units have millisecond resolution, and are defined by U:
25+
26+
.. code-block:: c
27+
28+
/* all time values are multiplied by U (ms) */
29+
#define U 1000
30+
31+
This is the recommended setup when the application has both tasks
32+
that have hard deadlines (directly scheduled by EDF) and soft
33+
deadlines (wrapped by a CBS). The CBS in this case guarantees that
34+
any overruns caused by the soft tasks will not jeopardize the
35+
execution environment of the hard tasks. It can be seen as the
36+
EDF equivalent of a :ref:`workqueue thread<workqueues_v2>`, and will
37+
deliver better overall performance than a workqueue.
38+
39+
.. note::
40+
If the system prioritizes schedulability and no task has hard
41+
deadlines, then all tasks can be wrapped by their own personal
42+
CBS. This is specially useful if the execution times of the tasks
43+
are unknown, as the CBS budget can be used instead. If the budget
44+
matches the task worst-case execution time (WCET), it is guaranteed
45+
that the system will behave just like a regular EDF.
46+
47+
If this is the case, all the application needs to do is guarantee
48+
that the sum of all CBS's (budget / period) ratio is lower than 1.
49+
50+
51+
Building and Running
52+
********************
53+
54+
This application can be built and executed on QEMU as follows:
55+
56+
.. zephyr-app-commands::
57+
:zephyr-app: samples/cbs/edf
58+
:host-os: unix
59+
:board: qemu_riscv32
60+
:goals: run
61+
:compact:
62+
63+
To build and run on a physical target (i.e. XIAO ESP32-C3) instead,
64+
run the following:
65+
66+
.. zephyr-app-commands::
67+
:zephyr-app: samples/cbs/edf
68+
:board: xiao_esp32c3
69+
:goals: build flash
70+
:compact:
71+
72+
Sample Output
73+
=============
74+
75+
Each task, in its cycle, will print its own ID at every tenth of its
76+
execution time, with square brackets at the edges to signal the start
77+
and end of the cycle. For a task of ID = '1', This is the output:
78+
79+
.. code-block:: console
80+
81+
[1-1-1-1-1-1-1-1-1-1]
82+
83+
EDF tasks have a numeric ID (1, 2, ...), while the CBS jobs have an
84+
alphabetic ID (A, B, ...). After a few seconds, the system will dump
85+
a tracing of the events in the following format:
86+
87+
.. code-block:: console
88+
89+
========================
90+
EDF events:
91+
1034 [ 1 ] TRIG 0
92+
1036 [ 1 ] START 0
93+
5093 [ 1 ] END 0
94+
========================
95+
96+
What it means is that EDF thread 1 was triggered at system tick 1034,
97+
started executing in tick 1036 and finished running in tick 5093. Thus,
98+
for the example above, thread 1 takes roughly 4057 ticks to execute.
99+
In a diagram, that would mean:
100+
101+
.. image:: doc/example-1.svg
102+
:align: center
103+
:width: 80%
104+
105+
In the given example, thread 1 is periodic and has period = deadline = 7000.
106+
So if activating at instant 1034, thread 1 will be set an absolute deadline
107+
of 8034, which is also its next triggering event. In other words, the next
108+
sequence of events would be likely the following:
109+
110+
.. code-block:: console
111+
112+
[1-1-1-1-1-1-1-1-1-1]-[1-1-1-1-1-1-1-1-1-1]
113+
114+
========================
115+
EDF events:
116+
1034 [ 1 ] TRIG 0
117+
1036 [ 1 ] START 0
118+
5093 [ 1 ] END 0
119+
...
120+
8034 [ 1 ] TRIG 1
121+
8035 [ 1 ] START 1
122+
12032 [ 1 ] END 1
123+
...
124+
========================
125+
126+
Now, if we add up a CBS with (budget, period) of (3000, 8000), we might see a
127+
somewhat different execution outcome. If the CBS receives a job A at instant
128+
4035 with an expected execution time of 4000 ticks, this is what happens:
129+
130+
.. code-block:: console
131+
132+
[1-1-1-1-1-1-1-1-1-1-1]-[A-A-A-A-A-A-A-A-[1-1-1-1-1-1-1-1-1-1-1]-A-A-A]
133+
134+
========================
135+
EDF events:
136+
1034 [ 1 ] TRIG 0
137+
1036 [ 1 ] START 0
138+
4035 [ A ] TRIG 0
139+
5093 [ 1 ] END 0
140+
5093 [ A ] START 0
141+
8034 [ 1 ] TRIG 1
142+
8095 [ 1 ] START 1
143+
12182 [ 1 ] END 1
144+
13002 [ A ] END 0
145+
========================
146+
147+
* As job A is triggered at instant 4035 and the CBS was idle, its absolute
148+
deadline is calculated as (4035 + CBS period) = 12035. However, at this very
149+
event thread 1 was mid-execution and its deadline, as said before, was
150+
set at 8034. Therefore, job A is triggered but not yet executed. It only
151+
starts to run at instant 5093, right after thread 1 finished its cycle.
152+
153+
* At instant 8034, thread A is triggered for its next cycle. Its deadline
154+
is set at (8034 + 7000) = 15034. However, the CBS is still running job A
155+
and its deadline is 12035, so thread 1 cannot start just yet.
156+
157+
* At instant 8093, 3000 ticks after job A started, the CBS budget runs out.
158+
In other words, it executed for its allowed time slice. The kernel then
159+
intervenes, replenishing the CBS budget at the cost of postponing its
160+
deadline. The new CBS absolute deadline is then 8093 + 8000 = 16093.
161+
162+
* Thread 1 now has the earliest absolute deadline (15034), so it *preempts*
163+
the CBS (and by extension, job A) and starts at tick 8095. That's why we
164+
see a [1-1-1] nested within the [A-A-A] logs above.
165+
166+
* Thread 1 finishes at tick 12182. The CBS is now allowed to resume. It
167+
finishes job A soon after, at tick 13002.
168+
169+
In a diagram, the events listed above translate to the following:
170+
171+
.. image:: doc/example-1-cbs.svg
172+
:align: center
173+
:width: 80%
174+
175+
Note that the CBS schedulability and deadlines only depend of the configured
176+
values of budget and period. It doesn't depend on how many jobs are received,
177+
nor it takes into account the expected duration of these jobs. The CBS thus
178+
wraps the jobs within a know scheduling context which ensures that the remaining
179+
application threads will never miss a deadline.

samples/cbs/edf/doc/example-1-cbs.svg

Lines changed: 3 additions & 0 deletions
Loading

samples/cbs/edf/doc/example-1.svg

Lines changed: 3 additions & 0 deletions
Loading

samples/cbs/edf/prj.conf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CONFIG_SYS_CLOCK_TICKS_PER_SEC=10000
2+
CONFIG_HEAP_MEM_POOL_SIZE=256
3+
CONFIG_CBS=y
4+
5+
###############################################################################
6+
### Uncomment the following if using QEMU targets. ############################
7+
###############################################################################
8+
9+
CONFIG_QEMU_ICOUNT=n
10+
CONFIG_STDOUT_CONSOLE=y

samples/cbs/edf/sample.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
sample:
2+
description: An Earliest Deadline First with Constant Bandwidth Server CBS sample
3+
name: EDF with CBS sample
4+
common:
5+
tags:
6+
- EDF
7+
- CBS
8+
tests:
9+
sample.cbs.edf:
10+
build_only: true
11+
platform_allow:
12+
- qemu_riscv32
13+
- qemu_cortex_a53
14+
integration_platforms:
15+
- qemu_riscv32
16+
tags:
17+
- cbs
18+
- edf

samples/cbs/edf/src/lib/helper.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2024 Instituto Superior de Engenharia do Porto (ISEP).
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef _APP_HELPER_H_
7+
#define _APP_HELPER_H_
8+
9+
/*
10+
* Remember that for "pure" EDF results, all
11+
* user threads must have the same static priority.
12+
* That happens because EDF will be a tie-breaker
13+
* among two or more ready tasks of the same static
14+
* priority. An arbitrary positive number is chosen here.
15+
*/
16+
#define EDF_PRIORITY 5
17+
#define INACTIVE -1
18+
#define MSEC_TO_CYC(msec) k_ms_to_cyc_near32(msec)
19+
#define MSEC_TO_USEC(msec) (msec * USEC_PER_MSEC)
20+
21+
typedef struct {
22+
char id;
23+
int counter;
24+
int32_t initial_delay_msec;
25+
int32_t rel_deadline_msec;
26+
int32_t period_msec;
27+
uint32_t wcet_msec;
28+
k_tid_t thread;
29+
struct k_msgq queue;
30+
struct k_timer timer;
31+
} edf_t;
32+
33+
typedef struct {
34+
char id;
35+
int counter;
36+
uint32_t wcet_msec;
37+
} job_t;
38+
39+
job_t *create_job(char id, int counter, uint32_t wcet_msec)
40+
{
41+
job_t *job = (job_t *)k_malloc(sizeof(job_t));
42+
43+
job->id = id;
44+
job->counter = counter;
45+
job->wcet_msec = wcet_msec;
46+
return job;
47+
}
48+
49+
void destroy_job(job_t *job)
50+
{
51+
k_free(job);
52+
}
53+
54+
void report_cbs_settings(void)
55+
{
56+
printf("\n/////////////////////////////////////////////////////////////////////////////////"
57+
"/////\n");
58+
printf("\nBoard:\t\t%s\n", CONFIG_BOARD_TARGET);
59+
#ifdef CONFIG_CBS
60+
printf("[CBS]\t\tCBS enabled.\n");
61+
#ifdef CONFIG_CBS_LOG
62+
printf("[CBS]\t\tCBS events logging: enabled.\n");
63+
#else
64+
printf("[CBS]\t\tCBS events logging: disabled.\n");
65+
#endif
66+
#ifdef CONFIG_TIMEOUT_64BIT
67+
printf("[CBS]\t\tSYS 64-bit timeouts: supported.\n");
68+
#else
69+
printf("[CBS]\t\tSYS 64-bit timeouts: not supported. using 32-bit API instead.\n");
70+
#endif
71+
#else
72+
printf("[CBS]\t\tCBS disabled.\n");
73+
#endif
74+
printf("\n/////////////////////////////////////////////////////////////////////////////////"
75+
"/////\n\n");
76+
}
77+
78+
#endif

samples/cbs/edf/src/lib/tracer.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2024 Instituto Superior de Engenharia do Porto (ISEP).
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef _APP_TRACER_H_
7+
#define _APP_TRACER_H_
8+
9+
#include <string.h>
10+
11+
#define TRIGGER 0
12+
#define START 1
13+
#define END 2
14+
#define BUF_SIZE 100
15+
16+
/*
17+
* This struct will hold execution
18+
* metadata for the threads
19+
*/
20+
typedef struct {
21+
uint32_t timestamp;
22+
char thread_id;
23+
int counter;
24+
int event;
25+
} trace_t;
26+
27+
trace_t events[BUF_SIZE];
28+
int event_count;
29+
uint32_t offset;
30+
31+
void toString(int evt, char *target)
32+
{
33+
switch (evt) {
34+
case TRIGGER:
35+
strcpy(target, "TRIG ");
36+
break;
37+
case START:
38+
strcpy(target, "START");
39+
break;
40+
case END:
41+
strcpy(target, "END ");
42+
break;
43+
default:
44+
strcpy(target, "-----");
45+
}
46+
}
47+
48+
void begin_trace(void)
49+
{
50+
offset = k_uptime_get_32();
51+
event_count = 0;
52+
}
53+
54+
void reset_trace(void)
55+
{
56+
event_count = 0;
57+
}
58+
59+
void print_trace(struct k_timer *timer)
60+
{
61+
(void) timer;
62+
char event[10];
63+
64+
printf("\n========================\nEDF events:\n");
65+
66+
for (int i = 0; i < event_count; i++) {
67+
uint32_t timestamp = events[i].timestamp - offset;
68+
69+
toString(events[i].event, event);
70+
printf("%u \t[ %c ] %s %d\n", timestamp, events[i].thread_id, event,
71+
events[i].counter);
72+
}
73+
printf("========================\n");
74+
reset_trace();
75+
}
76+
77+
void trace(char thread_id, int thread_counter, int event)
78+
{
79+
if (event_count >= BUF_SIZE) {
80+
return;
81+
}
82+
events[event_count].timestamp = k_uptime_get_32();
83+
events[event_count].thread_id = thread_id;
84+
events[event_count].counter = thread_counter;
85+
events[event_count].event = event;
86+
event_count++;
87+
}
88+
89+
#endif

0 commit comments

Comments
 (0)