Skip to content

Commit

Permalink
Merge branch 'DMA_smart' of https://github.com/TommiTerza/x-heep into…
Browse files Browse the repository at this point in the history
… DMA_smart
  • Loading branch information
TommiTerza committed Sep 6, 2024
2 parents 84b01b6 + bfa8137 commit a3b660d
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 75 deletions.
7 changes: 7 additions & 0 deletions docs/source/How_to/eXtendingHEEP.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,10 @@ If you plan to store source files in a different location that the one proposed,
make app PROJECT=your_app SOURCE=<path_to_your_sw_relative_to_x_heep_sw>
```
Consider that inside this `sw` folder the same structure than the one proposed is required.


## Inter-process communication using Verilator's DPI

The following [repository](https://github.com/specs-feup/x-heep) uses X-HEEP and the Verilator simulator to model a CPU-CGRA hybrid system. This architecture simulates the CPU integrated into the X-HEEP system, and an external Java process simulates the accelerator. Both components require a communication channel to exchange instructions and data. Using the existing infrastructure to to interact with an external OS process is not feasible at first sight, given that the X-HEEP ecosystem's pipeline encapsulates most of the simulation build and execution, with all modules supplied directly to Verilator.

To circumvent this issue, this project uses [Direct Programming Interface (DPI)](https://verilator.org/guide/latest/connecting.html) calls (defined in `hw/ip_examples/cgraitf/cgraitfdpi.c`) to establish a connection and communicate with an external process through a Unix Domain Socket. This behavior mirrors the UART module (used as the skeleton code) that connects and outputs _printf_ information to the pseudo-terminal. These calls are embedded in a mock CGRA peripheral/interface, located in `hw/ip_examples/cgraitf/cgraitf.sv`. The module overrides reads and writes to the specified peripheral address, with the proper socket-based mechanism (_send_ or _recv_). The _simple_accelerator_ module could also be similarly customized to perform the same operations, using X-HEEP's interfaces and memory access protocols. A given user program executed in the CPU (such as `sw/applications/cgra_itf/main.c`) must then select assignments to or from the address to trigger the appropriate action.
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 a3b660d

Please sign in to comment.