|
| 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. |
0 commit comments