#include #include #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; // signal handler for illegal instructions 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 = sigkill_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_avaiable = 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; }