# PDI-2025: Practice 3-A: Basic Time Management
The objective of this practice is that the student learns to design, implement and use a set of basic timing services.
### Schedulle
This practice is planned to be carried out in 1 week.
### P3 project creation
In order to create the ```P3-A``` project follow the same steps as for creating ```P2```.
## CLINT Timer Handling
### Installing an CLINC timer handler
See the functions ```install_local_timer_handler``` and ```uninstall_local_timer_handler``` in file ```dispatch.h```. They have the following prototypes:
```c=
void install_local_timer_handler( void (*f)(void) );
void uninstall_local_timer_handler();
```
The first function receives a pointer to a timer handler (ISR). When ```MTIME``` register reaches ```MTIMECMP``` value, ISR is automatically called by the system.
The parameter ```void (*f)(void)``` type means that ISRs are functions that receive NO parameters NOR return values. They use global variables to communicate with ```main```.
### Define CLINT time compare gap
See the function ```local_timer_set_gap``` in file ```dispatch.h```:
```c=
void local_timer_set_gap( uint64_t gap );
```
They are used to define the increment applied to ```MTIMECMP``` register.
### Enable CLINC timer interrupts
See the functions ```enable_timer_clinc_irq``` and ```disable_timer_clinc_irq``` in file ```dispatch.h```:
```c=
// SET MIE REG TIMER BIT (Timer irq enable)
void enable_timer_clinc_irq(void);
// CLEAR MIE REG TIMER BIT (Timer irq disable)
void disable_timer_clinc_irq(void);
```
They are used to enable/disable CLINC timer IRQs.
### Exercise code
In ```main.c``` file and before the ```main``` function add the following ISR handler and variables:
```c=
uint32_t ticks = 0;
void timer_handler(void)
{
printf("%d ", ticks );
ticks++;
}
```
Write to ```main``` function the following code:
```c=
int main()
{
printf("----------TIMER IRQ---------\n");
install_local_timer_handler( timer_handler );
local_timer_set_gap( 10000000 ); // CLINT timer clock is 10Mhz
enable_timer_clinc_irq();
enable_irq(); // Global interrupt enable
while( ticks <= 10 )
; // empty statement
printf("\n----------END---------\n");
}
```
### Timer IRQ test
In order to test the program follow the steps below:
* Build P3-A project and correct any typing errors. Run it typing ```./runP3-A``` in eclipse terminal window.
The program must print the value of ```ticks``` variable each second. After 10 seconds, the output should look like as follows:
```
atcsol@atcsol:~/eclipse-workspace/P3-A$ ./runP3-A
TCP Sockets...
Connected to picsimlab-PDI...
----------TIMER IRQ---------
0 1 2 3 4 5 6 7 8 9 10
----------END---------
atcsol@vmatc:~/eclipse-workspace/P3-A$
```
:::success
:raising_hand: Describe the execution flow of the code.
:raising_hand: Pay attention to all IRQ related configuration issues you must take in account in order to handle the timer IRQ properly.
:::
## Student work
Modify the given example as follows:
1. Select a timer gap in order to run timer ISR handler 10 times per second.
2. The program must print two messages, one every second and the other every 5 seconds.
3. Both messages must be printed in ```main``` function, following a top/bottom architecture. Timer ISR just count ticks and signals timeout by setting a boolean flag variable.
:::info
:information_source: Do not forget to declare all the variables you may need. Also include all the headers files that may be required,
:::
:::success
:raising_hand: Describe the execution flow of the code.
:raising_hand: Do the messages always appear in the same order? For example, what is happening if we get an output like the following? Is it right?
:::
```
Msg 1
Msg 1
Msg 1
Msg 1
Msg 1
Msg 2
Msg 1
Msg 1
Msg 1
Msg 1
Msg 2
Msg 1
```
:::spoiler
```c=
#include "riscv_types.h"
#include "dispatch.h"
#include "clinc.h"
#include "log.h"
uint32_t cnt_1=10, cnt_2=50;
uint32_t flag_1=0, flag_2=0;
void timer_handler(void)
{
cnt_1--;
cnt_2--;
if (cnt_1 == 0)
{
cnt_1 = 10;
flag_1 = 1;
}
if (cnt_2 == 0)
{
cnt_2 = 50;
flag_2 = 1;
}
}
int main()
{
printf("----------TIMER IRQ---------\n");
install_local_timer_handler( timer_handler );
local_timer_set_gap( 1000000 ); // CLINT timer clock is 10Mhz
enable_timer_clinc_irq();
enable_irq(); // Global interrupt enable
while(1)
{
if ( flag_1 )
{
printf("---> Msg 1\n");
flag_1 = 0;
}
if ( flag_2 )
{
printf(" ---> Msg 2\n");
flag_2 = 0;
}
}
}
```
:::
<!--
## Student work 2
Implementation of an GPTIMER basic driver.
-->
---
## Monotonic time service
One of the most basic timing services that can be implemented by the real time counter is a universal time manager via a monotonic clock. A monotonic clock is one that is incremented periodically during the execution of a system. The value taken by the clock can thus be associated to the universal time by means of a reference. For this practice we will use the following routines (***already coded***) that facilitate the implementation of a monotonic clock over a universal time encoding known as Y2K. This encoding uses a 32-bit integer to set the seconds that have passed since 1 January 2000.
:::info
:information_source: A routine that initialises the variables that manage the monotonic clock and sets a reference value in Y2K format as the initial universal time. Its prototype is as follows:
```c=
void InitMonotonicClock(uint32_t Y2KTimeRef);
```
:information_source: Function that returns the current value of the universal time encoded in Y2K. The prototype of the function is as follows:
```c=
void uint32_t GetUniversalTime_Y2K ();
```
:information_source: A function that prints the value of the universal time encoded in Y2K that is passed via the seconds_from_y2K parameter. The prototype of the function is as follows:
```c=
void print_date_time_from_Y2K(uint32_t seconds_from_y2K);
```
:information_source: Function that encodes in Y2K a date and time passed by parameter using the fields day, month, year, hour, minutes and seconds. The prototype of the function is as follows:
```c=
uint32_t date_time_to_Y2K( uint8_t day, uint8_t month, uint8_t year,
uint8_t hour, uint8_t minutes, uint8_t seconds);
```
:information_source: Finally and defined as a macro you can find ```get_ticks_from_reset()```. This macro returns the value of the real time counter, which contains the count of ticks since system boot. Remenber that the clock is 10Mhz :
```c=
#define get_ticks_from_reset() ((volatile uint64_t)(*p_clinc_mtime))
```
:::
Download the files ```riscv_monotonic_clock.c``` and ```riscv_monotonic_clock.h``` from:
* [riscv_monotonic_clock.c](https://drive.google.com/file/d/1dCBnusMVLgFOFeD6059Z9cU7q3ELNbVp/view?usp=sharing)
* [riscv_monotonic_clock.h](https://drive.google.com/file/d/1tmXETLX-fOoO03SuZFrC5v_ZGU3tgPgI/view?usp=sharing)
and add them to the project.
Write to ```main``` function the following code:
```c=
#include "riscv_types.h"
#include "dispatch.h"
#include "log.h"
#include "clinc.h"
#include "riscv_monotonic_clock.h"
int main()
{
printf("----------MONOTONIC TIME---------\n");
// Time set: 12/10/2023 12:40
InitMonotonicClock(date_time_to_Y2K(12, 10, 24, 12, 40, 0 ));
uint32_t previous = 0;
uint32_t current = 0;
do {
current = GetUniversalTime_Y2K();
if ( current != previous )
{
print_date_time_from_Y2K( current );
previous = current;
}
} while ( 1 );
printf("\n----------END---------\n");
}
```
:::info
:information_source: Do not forget to declare all the variables you may need. Also include all the headers files that may be required
:::
:::warning
:warning: In order to build the project it is necessary to modifiy the project settings and include a library as follows.
Open project settings, select ```libraries``` in ```Cross GCC Linker``` section and add ```gcc``` to the libraries window.

:::
Build and run the code. The output will show a sequence of timed messages every second.
```
----------MONOTONIC TIME---------
Fecha: 12 | 10 | 2024 Hora: 12:40:0
Fecha: 12 | 10 | 2024 Hora: 12:40:1
Fecha: 12 | 10 | 2024 Hora: 12:40:2
Fecha: 12 | 10 | 2024 Hora: 12:40:3
Fecha: 12 | 10 | 2024 Hora: 12:40:4
Fecha: 12 | 10 | 2024 Hora: 12:40:5
Fecha: 12 | 10 | 2024 Hora: 12:40:6
Fecha: 12 | 10 | 2024 Hora: 12:40:7
Fecha: 12 | 10 | 2024 Hora: 12:40:8
Fecha: 12 | 10 | 2024 Hora: 12:40:9
```
## Student work
### Coding delay and wait_until services
Given the following function description in ```riscv_monotonic_clock.h```, complete the code of ```delay``` and ```wait_until``` functions in ```riscv_monotonic_clock.c```
```c=
void delay( uint64_t ticks_to_wait )
{
// To complete
// Wait in an active loop for the number of MTIME ticks
// indicated by the ticks parameter
// Use get_ticks_from_reset() macro to get current value of MTIME
}
void wait_until( uint64_t target_ticks )
{
// To complete
// Wait in an active loop until the current MTIME ticks reaches
// the value indicated by the target_ticks parameter.
// Use get_ticks_from_reset() macro to get current value of MTIME
}
```
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)
:::spoiler
```c=
void delay( uint64_t ticks_to_wait )
{
uint64_t target_ticks = get_ticks_from_reset() + ticks_to_wait;
uint64_t current_ticks;
do {
current_ticks = get_ticks_from_reset();
} while ( current_ticks < target_ticks );
}
void wait_until( uint64_t target_ticks )
{
uint64_t current_ticks;
do {
current_ticks = get_ticks_from_reset();
} while ( current_ticks < target_ticks );
}
```
:::
### Modify previous ```main``` function
Modify previous ```main``` function in order to print the universal time each second. Implement two versions: one using ```delay``` and other using ```wait_until```.
```c=
#include "riscv_types.h"
#include "dispatch.h"
#include "log.h"
#include "clinc.h"
#include "riscv_monotonic_clock.h"
int main()
{
printf("----------MONOTONIC TIME---------\n");
// Time set: 12/10/2023 12:40
InitMonotonicClock(date_time_to_Y2K(12, 10, 23, 12, 40, 0 ));
uint32_t current = 0;
// Print universal time time each second
do {
// Modify if necessary
current = GetUniversalTime_Y2K();
print_date_time_from_Y2K( current );
// Modify if necessary
} while ( 1 );
printf("\n----------END---------\n");
}
```
:::info
:information_source: Do not forget to declare all the variables you may need.
:::
## Second Week
The proposed work about Cyclic Executives for the second week can be found [here](https://hackmd.io/@dasilvin/PDI-P3-B).
<!--
### OPTIONAL: Implement an OneShot/Cyclic callback service using CLINT timer interrupt
The objective of this section is to implement a service that allows to schedule the execution of a routine with a precision of milliseconds. The scheduled function must print the elapsed real time ticks in order to verify if it correspond to the time requested.
The service will run as follows:
```c=
#define ONESHOT 1
#define CYCLIC 0
uint64_t prev_rt_cnt = 0; // previous real time counter
uint64_t curr_rt_cnt = 0; // current real time counter
typedef struct {
void (*user_callback_function)();
uint8_t is_oneshot;
uint32_t timer_value;
volatile uint32_t timer_counter;
} timer_t;
timer_t timer1;
void deferred_user_processing(void)
{
// Print elapsed real time counter value
// TO COMPLETE
}
void timer_service(void)
{
// TO COMPLETE
// Decrement timer counter
// When timer counter reaches zero call user callback function
// If timer is cyclic reload timer counter with timer value
}
void program_timed_function(
void (*callback), // User function to callback when time expires
uint32_t mseconds, // Time in milliseconds
uint8_t oneshot_periodic ) // 1: one shot, 0: cyclic
{
// Save values in timer1
timer1.user_callback_function = callback;
timer1.is_oneshot = oneshot_periodic;
timer1.timer_value = mseconds;
timer1.timer_counter = mseconds;
}
int main()
{
printf("----------ONE SHOT/CYCLIC CALLBACK---------\n");
install_local_timer_handler( timer_service );
local_timer_set_gap( 10000 ); // miliseconds timer
enable_timer_clinc_irq();
enable_irq(); // Global interrupt enable
// One shot test
// program_timed_function( deferred_user_processing, 200 /* mseconds*/ , ONESHOT); // oneshot, 200 ms
// Cyclic test
program_timed_function( deferred_user_processing, 500 /* mseconds*/ , CYCLIC); // cylic, 500 ms
prev_rt_cnt = get_ticks_from_reset(); // Get current real time counter value
delay( CLINT_CLOCK * 5 ); // wait 5 seconds and ends
printf("\n----------END---------\n");
}
```
For the ***Cyclic*** test, the output values should approximate those shown below.
For ***Oneshot*** test the elapset ticks will be printed just once.
```
-----------ONE SHOT/CYCLIC CALLBACK---------
TIME ELAPSED: 00000000004C4B18
TIME ELAPSED: 00000000004C4BF4
TIME ELAPSED: 00000000004C4AE6
TIME ELAPSED: 00000000004C4B9A
TIME ELAPSED: 00000000004CA5F4
```
:::info
:information_source: Do not forget to declare all the variables and auxiliary functions you may need. Also include all the headers files that may be required.
:::
:::success
:raising_hand: How long does 0x4C4B18 real time count define?
:raising_hand: What should the real time count be for one tenth of a second?
:::
-->