# 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. ![](https://i.imgur.com/vvHWmCG.png) 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 ![](https://i.imgur.com/SVwX0DI.png) 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; } --