add timing logic

This commit is contained in:
Patrick Lipka 2025-08-18 17:25:33 +02:00
parent a66ea40850
commit e9f3e9a236
1 changed files with 86 additions and 0 deletions

View File

@ -3,6 +3,11 @@
#include <setjmp.h>
#include "rvprof_internal.h"
/*
The inline assembly calls in this file are the only places where the code is really RISCV-specific.
Maybe replace with a library call in the future
*/
// global flag for cycle counter availability
static volatile int g_cycle_counter_available = 0;
static volatile jmp_buf g_cycle_test_jmpbuf;
@ -11,4 +16,85 @@ static volatile jmp_buf g_cycle_test_jmpbuf;
static void sigkill_handler(int sig){
g_cycle_counter_available = 0;
longjmp(g_cycle_test_jmpbuf, 1);
}
static inline uint64_t read_cycles(void){
if (!g_cycle_counter_available) {
return 0;
}
uint64_t cycles;
__asm__ volatile("csrr %0, cycle" : "=r"(cycles));
return cycles;
}
// nanosecond timing for all actual measurements
static inline uint64_t read_time_ns(void){
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
}
int rvprof_timing_test_cycle_counter(void){
// set up signal handler for illegal instruction
struct sigaction old_action, new_action;
new_action.sa_handler = sigill_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
if (sigaction(SIGILL, &new_action, &old_action) != 0){
// can't set up signal handler, assume cycle counter not available
g_cycle_counter_available = 0;
return 0;
}
// test cycle counter access
if (setjmp(g_cycle_test_jmpbuf) == 0) {
uint64_t cycles1, cycles2;
__asm__ volatile("csrr %0, cycle" : "=r"(cycles1));
// small delay
for (volatile int i = 0; i < 1000; i++);
__asm__ volatile("csrr %0, cycle" : "=r"(cycles2));
if (cycles2 > cycles1){
g_cycle_counter_available = 1;
} else {
g_cycle_counter_available = 0;
}
}else{
// illegal instruction occurred
g_cycle_counter_available = 0;
}
// restore original signal handler
sigaction(SIGILL, &old_action, NULL);
return g_cycle_counter_available;
}
rvprof_error_t rvprof_timing_init(void){
// always use nanosecond timer for actual timing
g_rvprof.use_cycles = 0;
// test if cycle counter is available for informational display
g_rvprof.cycles_available = rvprof_timing_test_cycle_counter();
return RVPROF_SUCCESS;
}
uint64_t rvprof_timing_get_current(void) {
return read_time_ns();
}
uint64_t rvprof_timing_get_cycles(void) {
return read_cycles(); // Returns 0 if not available
}
double rvprof_timing_to_seconds(uint64_t nanoseconds) {
return (double)nanoseconds / 1000000000.0;
}