100 lines
2.7 KiB
C
100 lines
2.7 KiB
C
#include <time.h>
|
|
#include <signal.h>
|
|
#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;
|
|
|
|
// 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;
|
|
} |