Skip to content

Commit

Permalink
Fix-timer-sdk (#564)
Browse files Browse the repository at this point in the history
* stat fixing timer SDK

* Added functions for counting time, waiting time and enabling timer interupts. Fixed clock cycles counter.

* Revision

Added tests, comments and renamings

* lower tolerances

Lowered tolerances in tests, made time-tolerance frequency-dependent.

* Remove useless define

* added documentation and performed requested fixes on sdk

* update exmaple_timer_sdk app

* renamed function with name conflict

* Fixed simulation errors

* Update main.c (realxed error tolerance)

---------

Co-authored-by: davide schiavone <davide@openhwgroup.org>
  • Loading branch information
FrancescoPoluzzi and davideschiavone authored Sep 5, 2024
1 parent 2733542 commit 98dde53
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 75 deletions.
107 changes: 107 additions & 0 deletions docs/source/Peripherals/Timer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@

# Timer SDK

This SDK provides utilities for execution time measurements using HW timers. It includes functions to start, stop, reset, and configure timers, as well as to enable timer interrupts and measure elapsed time.

## Usage

The SDK provides a set of functions to interact with the HW Timer for various timing operations.

### Initialize Timer for Counting Cycles

This function configures the counter at the running clock frequency to count the number of clock cycles. Call this function before any of the other timer SDK functions.

```c
void timer_cycles_init();
```

### Start Timer

Start the HW timer.

```c
void timer_start();
```

### Get Current Timer Value

Retrieve the current value of the HW timer without stopping it.

```c
uint32_t timer_get_cycles();
```

### Complete timer reset

Completely resets the HW counter, disabling all IRQs, counters, and comparators.
```c
void timer_reset();
```

### Stop and Reset Timer

Retrieve the current value of the HW timer and stop it.

```c
uint32_t timer_stop();
```

### Set Timer Threshold

Set the timer to go off once the counter value reaches the specified threshold. If the timer interrupts and the timer IRQ have been enabled, when the timer reaches that value an interrupt will be called.

```c
void timer_arm_set(uint32_t threshold);
```
### Set Timer Threshold and Start
Set the timer to go off once the counter value reaches the specified threshold, and start the timer. If the timer interrupts and the timer IRQ have been enabled, when the timer reaches that value an interrupt will be called.
```c
void timer_arm_start(uint32_t threshold);
```

### Enable Timer IRQ

Enable the timer interrupt request.

```c
void timer_irq_enable();
```

### Clear Timer IRQ

Clear the timer interrupt request.

```c
void timer_irq_clear();
```

### Enable Timer Machine-level Interrupts

Enable the timer machine-level interrupts for the X-Heep platform.

```c
void enable_timer_interrupt();
```

### Wait for Microseconds

Block execution for a specified number of microseconds. This function is not precise for small numbers of microseconds. Enable timer interrupts with `enable_timer_interrupt()` before using this function.

```c
void timer_wait_us(uint32_t ms);
```
### Get Execution Time in Microseconds
Get the time taken to execute a certain number of cycles, returned as a float representing the time in microseconds.
```c
float get_time_from_cycles(uint32_t cycles);
```

## Example Usage

An example of utilization of the timer SDK can be found in `sw/applications/example_timer_sdk/main.c`.
102 changes: 86 additions & 16 deletions sw/applications/example_timer_sdk/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,27 @@
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
// File: example_timer_sdk.c
// Author: Juan Sapriza
// Date: 15/07/2024
// Author: Juan Sapriza, Francesco Poluzzi
// Date: 23/07/2024
// Description: Example application to test the Timer SDK. Will count the time to execute a few short tasks.

#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "core_v_mini_mcu.h"
#include "timer_sdk.h"
#include "x-heep.h"
#include "soc_ctrl.h"

/* By default, printfs are activated for FPGA and disabled for simulation. */
#define PRINTF_IN_FPGA 1
#define PRINTF_IN_SIM 0

/* Error tolerances for the tests. */
#define CYCLE_TOLERANCE 2 // cycles tolerance for simple timer reads
#define INTERRUPT_TOLERANCE 70 // cycles tolerance for timer interrupt
#define TIMER_WAIT_TOLERANCE 20 // milliseconds tolerance for timer wait

#if TARGET_SIM && PRINTF_IN_SIM
#define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
#elif PRINTF_IN_FPGA && !TARGET_SIM
Expand All @@ -25,32 +32,95 @@
#define PRINTF(...)
#endif

void __attribute__((aligned(4), interrupt)) handler_irq_timer(void) {
timer_arm_stop();
timer_irq_clear();
return;
}

int main(){
uint8_t i = 0;
uint32_t cpu_cycles;
uint32_t i = 0;
uint32_t timer_cycles;
uint32_t nop_cycles[4];

// Get current Frequency
soc_ctrl_t soc_ctrl;
soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS);
uint32_t freq_hz = soc_ctrl_get_frequency(&soc_ctrl);

timer_init(); // Init the timer SDK
timer_cycles_init(); // Init the timer SDK for clock cycles
timer_start(); // Start counting the time
cpu_cycles = timer_stop(); // Stop counting the time
PRINTF("0 NOPs:\t%d cc\n\r", cpu_cycles );

timer_start();
nop_cycles[0] = timer_stop(); // Stop counting the time
PRINTF("0 NOPs:\t%d cc\n\r", nop_cycles[0] );
timer_start();
asm volatile ("nop");
cpu_cycles = timer_stop();
PRINTF("1 NOP:\t%d cc\n\r", cpu_cycles );
nop_cycles[1] = timer_stop();
PRINTF("1 NOP:\t%d cc\n\r", nop_cycles[1] );

timer_start();
asm volatile ("nop");
asm volatile ("nop");
cpu_cycles = timer_stop();
PRINTF("2 NOPs:\t%d cc\n\r", cpu_cycles );
nop_cycles[2] = timer_stop();
PRINTF("2 NOPs:\t%d cc\n\r", nop_cycles[2] );

timer_start();
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
cpu_cycles = timer_stop();
PRINTF("3 NOPs:\t%d cc\n\r", cpu_cycles );
nop_cycles[3] = timer_stop();
PRINTF("3 NOPs:\t%d cc\n\r", nop_cycles[3] );

if( abs(nop_cycles[1] - nop_cycles[0])>CYCLE_TOLERANCE || abs(nop_cycles[2] - nop_cycles[1])>CYCLE_TOLERANCE || abs(nop_cycles[3] - nop_cycles[2])>CYCLE_TOLERANCE){
PRINTF("Clock count failed\n\r");
return EXIT_FAILURE;
}

enable_timer_interrupt(); // Enable the timer machine-level interrupt

timer_cycles_init();
timer_irq_enable();
timer_arm_start(1000);
asm volatile ("wfi"); // Wait for interrupt
timer_cycles = timer_stop();
if(abs(timer_cycles-1000) < INTERRUPT_TOLERANCE){
PRINTF("Timer threshold interrupt working\n" );
} else {
PRINTF("Timer threshold interrupt failed\n\r");
return EXIT_FAILURE;
}

timer_cycles_init(); // Init the timer SDK for microseconds
timer_start();
for(i = 0; i < 1000; i++){
asm volatile ("nop");
}
timer_cycles = timer_stop();
PRINTF("Microseconds for 1000 NOPs:\t%d μs\n\r", (uint32_t)get_time_from_cycles(timer_cycles) );

#ifdef TARGET_IS_FPGA
PRINTF("Wait 5 second\n\r");
timer_wait_us(5000000); // Wait for 5 seconds
timer_cycles = timer_stop();
PRINTF("Done\n\r");

if(abs(timer_cycles-(5*freq_hz)) > TIMER_WAIT_TOLERANCE){
PRINTF("Timer wait failed\n\r");
return EXIT_FAILURE;
}
#endif
#ifdef TARGET_SIM // Reduced time for simulation for faster testing
PRINTF("Wait 0.001 second\n\r");
timer_wait_us(1000); // Wait for 1 millisecond
timer_cycles = timer_stop();
PRINTF("Done\n\r");

if(abs(timer_cycles-(0.001*freq_hz)) > TIMER_WAIT_TOLERANCE){
PRINTF("Timer wait failed\n\r");
return EXIT_FAILURE;
}
#endif

PRINTF("All tests passed\n\r");
return EXIT_SUCCESS;
}
}
6 changes: 6 additions & 0 deletions sw/device/lib/drivers/rv_timer/rv_timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ typedef enum rv_timer_approximate_tick_params_result {
* @param[out] out Tick parameters that will approximately produce the desired
* counter frequency.
* @return The result of the operation.
*
* The minimum value for `counter_freq` is given by:
* counter_freq_min = (255/4096) * clock_freq
* For example, if the clock frequency is 15MHz, the minimum value for `counter_freq` is about
* 1 MHz.
* The maximum value for `counter_freq` is given by the clock frequency.
*/
rv_timer_approximate_tick_params_result_t
rv_timer_approximate_tick_params(uint64_t clock_freq, uint64_t counter_freq,
Expand Down
Loading

0 comments on commit 98dde53

Please sign in to comment.