# PDI-2025: Practice 3-B: Cyclic executives
In this part of the practice 3 we will see how the system real time counter will allow us to provide services destined to the construction of a cyclic executive, that is, a programme that periodically repeats a sequence of task execution. Cyclic executives have a basic period, which controls the start of the execution of a specific sequence of tasks, and a hyper-period, which is the number of basic periods from which the complete sequence of execution of the tasks of the cyclic executive is repeated.
The following figure shows an example of a cyclic executive, in which the periodic execution of 3 tasks is controlled, whose periods and execution times are determined in the attached table.

The cyclic executive has a basic period of 2 seconds, while the hyperperiod is 6 basic periods.
```c=
#include "riscv_types.h"
#include "riscv_uart.h"
#include "clinc.h"
#include "dispatch.h"
#include "log.h"
#include "riscv_monotonic_clock.h"
#define NULL (0)
void emu_execution_time( uint32_t mseconds )
{
// TO COMPLETE: Call delay with the number of ticks to sleep
}
// Task code T1
void Task1(void)
{
printf(" Start T1\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
emu_execution_time(1000); //execution time 1000 ms
printf(" End T1\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
}
// Task code T2
void Task2(void)
{
printf(" Start T2\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
emu_execution_time(300); //execution time 300 ms
printf(" End T2\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
}
// Task code T3
void Task3(void)
{
printf(" Start T3\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
emu_execution_time(500); //execution time 500 ms
printf(" End T3\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
}
// Definition of the configuration macros
// CLINT_CLOCK is 10000000
#define CYClIC_EXECUTIVE_PERIOD_IN_SECONDS 2
#define CYClIC_EXECUTIVE_PERIOD_IN_TICKS (CYClIC_EXECUTIVE_PERIOD_IN_SECONDS\
* CLINT_CLOCK)
#define CYClIC_EXECUTIVE_HYPER_PERIOD 6
#define CYClIC_EXECUTIVE_TASKS_NUMBER 3
// Two-dimensional function pointer array to define
// the sequence of tasks of the cyclic executive
void (*cyclic_executive [CYClIC_EXECUTIVE_HYPER_PERIOD]
[CYClIC_EXECUTIVE_TASKS_NUMBER+1])(void) = {
{Task1,Task2,Task3,NULL},
{Task1,NULL,NULL,NULL},
{Task2,Task1,Task3,NULL},
{Task1,Task2,NULL,NULL},
{Task1,Task3,NULL,NULL},
{Task2,Task1,NULL,NULL}
};
// Main function
int main()
{
// Declaration of control variables of the cyclic executive
uint8_t current_period=0;
int task_index=0;
uint64_t next_period_in_ticks_from_reset;
// Initialisation of the timing service and taking of absolute reference
// of the number of ticks since system reset
// Init Timing Service
InitMonotonicClock(date_time_to_Y2K(12, 10, 23, 12, 40, 0 )); // 12/10/2023 12:40
//Get Absolute reference
next_period_in_ticks_from_reset = get_ticks_from_reset();
while(1) {
task_index=0; //set to 0 at the start of each basic period
printf("\nStart period: %d\n", current_period );
print_date_time_from_Y2K(GetUniversalTime_Y2K());
// Control the execution of the tasks of each basic period
while( cyclic_executive[current_period][task_index] != NULL ) {
cyclic_executive[current_period][task_index]();
task_index++;
}
// Synchronisation with the start of the next basic period
// Update Absolute reference with next period
next_period_in_ticks_from_reset += CYClIC_EXECUTIVE_PERIOD_IN_TICKS;
wait_until(next_period_in_ticks_from_reset);
//Next basic period
current_period++;
if(current_period==CYClIC_EXECUTIVE_HYPER_PERIOD) {
current_period=0;
printf("\n****************\n");
printf("Next hyperperiod");
printf("\n****************\n");
}
}
printf("\n----------END---------\n");
return 0;
}
```
The elements that have been used in the programme for the implementation of the cyclic executive, and which need to be analysed in detail, are the following:
## Definition of configuration macros
To implement the cyclic executive, first of all, a set of macros have been defined with the following meaning:
* ```CYCLIC_EXECUTIVE_PERIOD_IN_SECONDS```: duration of the basic period in seconds
* ```CYCLIC_EXECUTIVE_PERIOD_IN_TICKS```: duration of the basic period in ticks
* ```CYCLIC_EXECUTIVE_HYPER_PERIOD```: number of basic hyperperiod periods
* ```CYCLIC_EXECUTIVE_TASKS_NUMBER```: total number of tasks of the cyclic executive
To implement the executive in figure 4 these macros have taken the following values:
```c=
//MACROS DEFINING THE CYCLIC EXECUTIVE
#define CYClIC_EXECUTIVE_PERIOD_IN_SECONDS 2
#define CYClIC_EXECUTIVE_PERIOD_IN_TICKS (CYClIC_EXECUTIVE_PERIOD_IN_SECONDS\
* CLINT_CLOCK)
#define CYClIC_EXECUTIVE_HYPER_PERIOD 6
#define CYClIC_EXECUTIVE_TASKS_NUMBER 3
```
## Two-dimensional array for defining the task sequence of the cyclic executive
To support the execution of tasks, a two-dimensional array of function pointers is defined, the two dimensions being: the number of basic periods of which the hyperperiod consists ```(CYCl_EXEC_HYPER_PERIOD)``` and the number of different tasks to be executed during the cyclic executive increased by one unit ```(CYCl_EXEC_TASKS_NUMBER + 1)```. Each row of this array is initialised with the sequence of tasks to be executed in the corresponding basic period, taking NULL value the final elements of each row that are not used in that period.
Defining the second dimension of the array as ```CYCl_EXEC_TASKS_NUMBER + 1``` ensures that the execution sequence defined in each row always ends with at least one NULL element, which is a simple way of indicating that there are no more tasks to be executed in that basic period, thus facilitating execution control.
```c=
// Two-dimensional function pointer array to define
// the sequence of tasks of the cyclic executive
void (*cyclic_executive [CYClIC_EXECUTIVE_HYPER_PERIOD]
[CYClIC_EXECUTIVE_TASKS_NUMBER+1])(void) = {
{Task1,Task2,Task3,NULL},
{Task1,NULL,NULL,NULL},
{Task2,Task1,Task3,NULL},
{Task1,Task2,NULL,NULL},
{Task1,Task3,NULL,NULL},
{Task2,Task1,NULL,NULL}
};
```
## Declaration of cyclic executive control variables
To control the cyclic executive, the following three variables are declared in the main
function:
```c=
//Declaration of control variables of the cyclic executive
uint8_t current_period=0;
uint8_t task_index=0;
uint64_t next_period_in_ticks_from_reset;
```
Each of these variables has the following meaning:
* ```current_period```: Variable that determines the current basic period being executed.
* ```task_index```: Variable that determines the position of the current task to be executed within the sequence of tasks of each basic period.
* ```next_period_in_ticks_from_reset```: Variable containing the number of ticks from the system reset at which the next basic period is to start.
## Initialisation of the timing service and taking absolute reference of the number of ticks since system reset.
Before starting the cyclic executive control loop in main, the timing service is initialised, and the number of ticks since reset is taken as the absolute reference:
```c=
//Init Timing Service
InitMonotonicClock( date_time_to_Y2K(18, 3, 22, 0, 0, 0, 0 ));
//Get Absolute reference
next_period_in_ticks_from_reset=get_ticks_from_reset();
```
## Monitoring of the execution of the tasks of each basic period
Once in the global control loop of the cyclic executive, the following code is used to control the execution of the sequence of tasks for each basic period.
```c=
// Monitoring of the execution of tasks for each basic period
while(cyclic_executive[current_period][task_index]){
cyclic_executive[current_period][task_index]();
task_index++;
}
```
## Synchronisation with the start of the next basic period
With the following code an active wait is performed which controls the synchronisation with the start of the next basic period.
```c=
//Synchronisation with the start of the next basic period
//Update Absolute reference with next period
next_period_in_ticks_from_reset+=CYClIC_EXECUTIVE_PERIOD_IN_TICKS;
//Wait until next period starts
wait_until(next_period_in_ticks_from_reset);
```
In order to better undestand and clarify the use of Delay and WaitUntil primitives, please take a look to: [Delay and WaitUntil](https://hackmd.io/@SistemasEmpotrados/SE-DELAY-WAITUNITL)
### Example of an hyperperiod execution
:::spoiler
Start period: 0
Fecha: 12 | 10 | 2023 Hora: 12:40:0
Start T1
Fecha: 12 | 10 | 2023 Hora: 12:40:0
End T1
Fecha: 12 | 10 | 2023 Hora: 12:40:1
Start T2
Fecha: 12 | 10 | 2023 Hora: 12:40:1
End T2
Fecha: 12 | 10 | 2023 Hora: 12:40:1
Start T3
Fecha: 12 | 10 | 2023 Hora: 12:40:1
End T3
Fecha: 12 | 10 | 2023 Hora: 12:40:1
Start period: 1
Fecha: 12 | 10 | 2023 Hora: 12:40:2
Start T1
Fecha: 12 | 10 | 2023 Hora: 12:40:2
End T1
Fecha: 12 | 10 | 2023 Hora: 12:40:3
Start period: 2
Fecha: 12 | 10 | 2023 Hora: 12:40:4
Start T2
Fecha: 12 | 10 | 2023 Hora: 12:40:4
End T2
Fecha: 12 | 10 | 2023 Hora: 12:40:4
Start T1
Fecha: 12 | 10 | 2023 Hora: 12:40:4
End T1
Fecha: 12 | 10 | 2023 Hora: 12:40:5
Start T3
Fecha: 12 | 10 | 2023 Hora: 12:40:5
End T3
Fecha: 12 | 10 | 2023 Hora: 12:40:5
Start period: 3
Fecha: 12 | 10 | 2023 Hora: 12:40:6
Start T1
Fecha: 12 | 10 | 2023 Hora: 12:40:6
End T1
Fecha: 12 | 10 | 2023 Hora: 12:40:7
Start T2
Fecha: 12 | 10 | 2023 Hora: 12:40:7
End T2
Fecha: 12 | 10 | 2023 Hora: 12:40:7
Start period: 4
Fecha: 12 | 10 | 2023 Hora: 12:40:8
Start T1
Fecha: 12 | 10 | 2023 Hora: 12:40:8
End T1
Fecha: 12 | 10 | 2023 Hora: 12:40:9
Start T3
Fecha: 12 | 10 | 2023 Hora: 12:40:9
End T3
Fecha: 12 | 10 | 2023 Hora: 12:40:9
Start period: 5
Fecha: 12 | 10 | 2023 Hora: 12:40:10
Start T2
Fecha: 12 | 10 | 2023 Hora: 12:40:10
End T2
Fecha: 12 | 10 | 2023 Hora: 12:40:10
Start T1
Fecha: 12 | 10 | 2023 Hora: 12:40:10
End T1
Fecha: 12 | 10 | 2023 Hora: 12:40:11
****************
Next hyperperiod
****************
Start period: 0
Fecha: 12 | 10 | 2023 Hora: 12:40:12
Start T1
Fecha: 12 | 10 | 2023 Hora: 12:40:12
End T1
Fecha: 12 | 10 | 2023 Hora: 12:40:13
Start T2
Fecha: 12 | 10 | 2023 Hora: 12:40:13
End T2
Fecha: 12 | 10 | 2023 Hora: 12:40:13
Start T3
Fecha: 12 | 10 | 2023 Hora: 12:40:13
End T3
Fecha: 12 | 10 | 2023 Hora: 12:40:13
```...repeat the previuos sequence every 12 seconds```
:::
## Student work

Modify the program so that, using the following implementation code for each of the four tasks, the cyclic executive described in figure above is implemented.
```c=
void TAvoidObstacle(void) {
printf(" Start Avoid Obstacles\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
emu_execution_time(2000);
printf(" End Avoid Obstacles\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
}
void TPathTracking(void) {
printf(" Start Path Tracking\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
emu_execution_time(3000);
printf(" End Path Tracking\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
}
void TSensorFusion(void) {
printf(" Start Sensor Fusion\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
emu_execution_time(5000);
printf(" End Sensor Fusion\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
}
void TCalculatePath(void) {
printf(" Start Calculate Path\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
emu_execution_time(6000);
printf(" End Calculate Path\n");
print_date_time_from_Y2K(GetUniversalTime_Y2K());
}
```
### Example of an hyperperiod execution
:::spoiler
Start period: 0
Fecha: 12 | 10 | 2023 Hora: 12:40:0
Start Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:0
End Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:2
Start Path Tracking
Fecha: 12 | 10 | 2023 Hora: 12:40:2
End Path Tracking
Fecha: 12 | 10 | 2023 Hora: 12:40:5
Start Sensor Fusion
Fecha: 12 | 10 | 2023 Hora: 12:40:5
End Sensor Fusion
Fecha: 12 | 10 | 2023 Hora: 12:40:10
Start period: 1
Fecha: 12 | 10 | 2023 Hora: 12:40:10
Start Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:10
End Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:12
Start Calculate Path
Fecha: 12 | 10 | 2023 Hora: 12:40:12
End Calculate Path
Fecha: 12 | 10 | 2023 Hora: 12:40:18
Start period: 2
Fecha: 12 | 10 | 2023 Hora: 12:40:20
Start Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:20
End Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:22
Start Path Tracking
Fecha: 12 | 10 | 2023 Hora: 12:40:22
End Path Tracking
Fecha: 12 | 10 | 2023 Hora: 12:40:25
****************
Next hyperperiod
****************
Start period: 0
Fecha: 12 | 10 | 2023 Hora: 12:40:30
Start Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:30
End Avoid Obstacles
Fecha: 12 | 10 | 2023 Hora: 12:40:32
Start Path Tracking
Fecha: 12 | 10 | 2023 Hora: 12:40:32
```...repeat the previuos sequence every 30 seconds```
:::
<!--
#include "riscv_types.h"
#include "riscv_uart.h"
#include "clinc.h"
#include "dispatch.h"
#include "log.h"
#include "riscv_monotonic_clock.h"
#define NULL (0)
void emu_execution_time( uint32_t mseconds )
{
// TO COMPLETE: Call delay with the number of ticks to sleep
delay( (CLINT_CLOCK/1000)*mseconds); // delay solution
}
void TAvoidObstacle(void) {
printf(" Start Avoid Obstacles\n");
emu_execution_time(2000);
printf(" End Avoid Obstacles\n");
}
void TPathTracking(void) {
printf(" Start Path Tracking\n");
emu_execution_time(3000);
printf(" End Path Tracking\n");
}
void TSensorFusion(void) {
printf(" Start Sensor Fusion\n");
emu_execution_time(5000);
printf(" End Sensor Fusion\n");
}
void TCalculatePath(void) {
printf(" Start Calculate Path\n");
emu_execution_time(6000);
printf(" End Calculate Path\n");
}
// Definition of the configuration macros
#define CYClIC_EXECUTIVE_PERIOD_IN_SECONDS 10
#define CYClIC_EXECUTIVE_PERIOD_IN_TICKS (CYClIC_EXECUTIVE_PERIOD_IN_SECONDS\
* CLINT_CLOCK)
#define CYClIC_EXECUTIVE_HYPER_PERIOD 3
#define CYClIC_EXECUTIVE_TASKS_NUMBER 4
// Two-dimensional function pointer array to define
// the sequence of tasks of the cyclic executive
void (*cyclic_executive [CYClIC_EXECUTIVE_HYPER_PERIOD]
[CYClIC_EXECUTIVE_TASKS_NUMBER+1])(void) = {
{TAvoidObstacle,TPathTracking,TSensorFusion,NULL,NULL},
{TAvoidObstacle,TCalculatePath,NULL,NULL,NULL},
{TAvoidObstacle,TPathTracking,NULL,NULL,NULL}
};
// Main function
int main()
{
//Declaration of control variables of the cyclic executive
uint8_t current_period=0;
int task_index=0;
uint64_t next_period_in_ticks_from_reset;
//Initialisation of the timing service and taking of absolute reference
// of the number of ticks since system reset
//Init Timing Service
InitMonotonicClock(date_time_to_Y2K(15, 11, 23, 17, 20, 0 )); // 15/11/2023 17:20
//Get Absolute reference
next_period_in_ticks_from_reset = get_ticks_from_reset();
while(1) {
task_index=0; //set to 0 at the start of each basic period
printf("\nStart period: %d\n", current_period );
print_date_time_from_Y2K(GetUniversalTime_Y2K());
// Control the execution of the tasks of each basic period
while( cyclic_executive[current_period][task_index] != NULL ) {
cyclic_executive[current_period][task_index]();
task_index++;
}
// Synchronisation with the start of the next basic period
// Update Absolute reference with next period
next_period_in_ticks_from_reset += CYClIC_EXECUTIVE_PERIOD_IN_TICKS;
wait_until(next_period_in_ticks_from_reset);
//Next basic period
current_period++;
if(current_period==CYClIC_EXECUTIVE_HYPER_PERIOD) {
current_period=0;
printf("\n****************\n");
printf("Next hyperperiod");
printf("\n****************\n");
}
}
printf("\n----------END---------\n");
return 0;
}
--