libsel4bench: Add event counter for riscv Implement event counter, currently only supports sifive U540.
diff --git a/libsel4bench/arch_include/riscv/sel4bench/arch/sel4bench.h b/libsel4bench/arch_include/riscv/sel4bench/arch/sel4bench.h index 51cb7da..61fe70b 100644 --- a/libsel4bench/arch_include/riscv/sel4bench/arch/sel4bench.h +++ b/libsel4bench/arch_include/riscv/sel4bench/arch/sel4bench.h
@@ -12,45 +12,91 @@ #pragma once #include <autoconf.h> +#include <stdlib.h> #include <sel4bench/types.h> #include <sel4/sel4.h> #include <utils/util.h> #define FASTFN inline __attribute__((always_inline)) -#define SEL4BENCH_READ_CCNT(var) asm volatile("rdcycle %0" :"=r"(var)); +#if __riscv_xlen == 32 +#define SEL4BENCH_READ_CCNT(var) \ + uint32_t nH1, nL, nH2; \ + asm volatile("rdcycleh %0\n" \ + "rdcycle %1\n" \ + "rdcycleh %2\n" \ + : "=r"(nH1), "=r"(nL), "=r"(nH2)); \ + if (nH1 < nH2) { \ + asm volatile("rdcycle %0" : "=r"(nL); \ + nH1 = nH2; \ + } \ + var = ((uint64_t)((uint64_t) nH1 << 32 | (nL); +#else +#define SEL4BENCH_READ_CCNT(var) \ + asm volatile("rdcycle %0" :"=r"(var)); +#endif #define SEL4BENCH_RESET_CCNT do {\ ; \ } while(0) -#define SEL4BENCH_EVENT_EXECUTE_INSTRUCTION 0x00 -#define SEL4BENCH_EVENT_CACHE_L1I_MISS 0xf0 -#define SEL4BENCH_EVENT_CACHE_L1D_MISS 0xf1 -#define SEL4BENCH_EVENT_TLB_L1I_MISS 0xf2 -#define SEL4BENCH_EVENT_TLB_L1D_MISS 0xf3 -#define SEL4BENCH_EVENT_BRANCH_MISPREDICT 0xf4 -#define SEL4BENCH_EVENT_MEMORY_ACCESS 0xf5 +#if __riscv_xlen == 32 +#define SEL4BENCH_READ_PCNT(idx, var) \ + uint32_t nH1, nL, nH2; \ + asm volatile("csrr %0, hpmcounterh" #idx \ + "csrr %1, hpmcounter" #idx \ + "csrr %2, hpmcounterh" #idx \ + : "=r"(nH1), "=r"(nL), "=r"(nH2)); \ + if (nH1 < nH2) { \ + asm volatile("csrr %0, hpmcounter" #idx : "=r"(nL); \ + nH1 = nH2; \ + } \ + var = ((uint64_t)((uint64_t) nH1 << 32 | (nL); +#else +#define SEL4BENCH_READ_PCNT(idx, var) \ + asm volatile("csrr %0, hpmcounter" #idx : "=r"(var)); +#endif + +/* Check out SiFive FU540 Manual Chapter 4.10 for details + * These settings are platform specific, however, they + * might become part of the RISCV spec in the future. + */ +#define SEL4BENCH_EVENT_EXECUTE_INSTRUCTION 0x3FFFF00 +#define SEL4BENCH_EVENT_CACHE_L1I_MISS 0x102 +#define SEL4BENCH_EVENT_CACHE_L1D_MISS 0x202 +#define SEL4BENCH_EVENT_TLB_L1I_MISS 0x802 +#define SEL4BENCH_EVENT_TLB_L1D_MISS 0x1002 +#define SEL4BENCH_EVENT_BRANCH_MISPREDICT 0x6001 +#define SEL4BENCH_EVENT_MEMORY_ACCESS 0x202 #define CCNT_FORMAT "%"PRIu64 typedef uint64_t ccnt_t; static FASTFN void sel4bench_init() { + /* Nothing to do */ } static FASTFN void sel4bench_destroy() { + /* Nothing to do */ } static FASTFN seL4_Word sel4bench_get_num_counters() { +#ifdef CONFIG_PLAT_HIFIVE + return 2; +#else return 0; +#endif } static FASTFN ccnt_t sel4bench_get_cycle_count() { ccnt_t val; + + SEL4BENCH_READ_CCNT(val); + return val; } @@ -60,37 +106,95 @@ */ static FASTFN ccnt_t sel4bench_get_counter(counter_t counter) { - uint32_t val = 0; + ccnt_t val; + + /* Sifive U540 only supports two event counters */ + switch (counter) { + case 0: + SEL4BENCH_READ_PCNT(3, val); + break; + case 1: + SEL4BENCH_READ_PCNT(4, val); + break; + default: + val = 0; + break; + } + return val; } -/* This reader function is too complex to be inlined, so we force it to be - * cacheline-aligned in order to avoid icache misses with the counters off. - * (relevant note: GCC compiles this function to be exactly one ARMV7 cache - * line in size) however, the pointer dereference is overwhelmingly likely to - * produce a dcache miss, which will occur with the counters off - */ static inline ccnt_t sel4bench_get_counters(counter_bitfield_t mask, ccnt_t *values) { - return 0; + ccnt_t ccnt; + unsigned int counter = 0; + + for (; mask != 0 ; mask >>= 1, counter++) { + if (mask & 1) { + values[counter] = sel4bench_get_counter(counter); + } + } + + SEL4BENCH_READ_CCNT(ccnt); + + return ccnt; } static FASTFN void sel4bench_set_count_event(counter_t counter, event_id_t event) { + /* Sifive U540 only supports two event counters */ + switch (counter) { + case 0: + /* Stop the counter */ + asm volatile("csrw mhpmevent3, 0"); + + /* Reset and start the counter*/ +#if __riscv_xlen == 32 + asm volatile("csrw hpmcounterh3, 0"); +#endif + asm volatile("csrw hpmcounter3, 0\n" + "csrw mhpmevent3, %0\n" + :: "r"(event)); + break; + case 1: + asm volatile("csrw mhpmevent4, 0"); +#if __riscv_xlen == 32 + asm volatile("csrw hpmcounterh4, 0"); +#endif + asm volatile("csrw hpmcounter4, 0\n" + "csrw mhpmevent4, %0\n" + :: "r"(event)); + break; + default: + break; + } + return; } +/* Writing the to event CSR would automatically start the counter */ static FASTFN void sel4bench_start_counters(counter_bitfield_t mask) { - return; + /* Nothing to do */ } +/* Note that the counter is stopped by clearing the event CSR. + * Set event CSR before starting the counter again + */ static FASTFN void sel4bench_stop_counters(counter_bitfield_t mask) { + /* Sifive U540 only supports two event counters */ + if (mask & (1 << 3)) { + asm volatile("csrw mhpmevent3, 0"); + } + + if (mask & (1 << 4)) { + asm volatile("csrw mhpmevent4, 0"); + } return; } static FASTFN void sel4bench_reset_counters(void) { - return; + /* Nothing to do */ }