diff --git a/src/rvprof_timing.c b/src/rvprof_timing.c index 68c4229..8c210ee 100644 --- a/src/rvprof_timing.c +++ b/src/rvprof_timing.c @@ -3,6 +3,11 @@ #include #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; } \ No newline at end of file