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 */
}