summaryrefslogtreecommitdiff
path: root/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_ccache0.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_ccache0.c')
-rw-r--r--FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_ccache0.c322
1 files changed, 275 insertions, 47 deletions
diff --git a/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_ccache0.c b/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_ccache0.c
index 6f8723735..76aeb25cc 100644
--- a/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_ccache0.c
+++ b/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_ccache0.c
@@ -1,83 +1,311 @@
-/* Copyright 2019 SiFive, Inc */
+/* Copyright 2020 SiFive, Inc */
/* SPDX-License-Identifier: Apache-2.0 */
#include <metal/machine/platform.h>
#ifdef METAL_SIFIVE_CCACHE0
-#include <stdint.h>
-#include <metal/io.h>
#include <metal/drivers/sifive_ccache0.h>
+#include <metal/init.h>
#include <metal/machine.h>
+#include <stdint.h>
+
+/* Macros to access memory mapped registers */
+#define REGW(x) *(volatile uint32_t *)(METAL_SIFIVE_CCACHE0_0_BASE_ADDRESS + x)
+
+#define REGD(x) *(volatile uint64_t *)(METAL_SIFIVE_CCACHE0_0_BASE_ADDRESS + x)
+
+/* Macros to specify register bit shift */
+#define REG_SHIFT_4 4
+#define REG_SHIFT_8 8
+#define REG_SHIFT_16 16
+#define REG_SHIFT_24 24
+
+#define SIFIVE_CCACHE0_BYTE_MASK 0xFFUL
-#define L2_CONFIG_WAYS_SHIFT 8
-#define L2_CONFIG_WAYS_MASK (0xFF << L2_CONFIG_WAYS_SHIFT)
+static int sifive_ccache0_interrupts[] = METAL_SIFIVE_CCACHE0_INTERRUPTS;
-void __metal_driver_sifive_ccache0_init(struct metal_cache *l2, int ways);
+/* Initialize cache at start-up via metal constructors */
+METAL_CONSTRUCTOR(_sifive_ccache0_init) { sifive_ccache0_init(); }
-static void metal_driver_sifive_ccache0_init(void) __attribute__((constructor));
-static void metal_driver_sifive_ccache0_init(void)
-{
-#ifdef __METAL_DT_SIFIVE_CCACHE0_HANDLE
- /* Get the handle for the L2 cache controller */
- struct metal_cache *l2 = __METAL_DT_SIFIVE_CCACHE0_HANDLE;
- if(!l2) {
- return;
+/* Linker symbols to calculate LIM allocated size */
+extern char metal_segment_lim_target_start, metal_segment_lim_target_end;
+
+int sifive_ccache0_init(void) {
+ int ret;
+
+ sifive_ccache0_config config;
+
+ /* Get cache configuration data */
+ sifive_ccache0_get_config(&config);
+
+ int lim_size =
+ &metal_segment_lim_target_end - &metal_segment_lim_target_start;
+
+ if (lim_size) { /* Do not enable cache ways, corresponding to LIM area in
+ use. */
+ while (lim_size > 0) {
+ lim_size -= (config.block_size * config.num_sets * config.num_bank);
+ config.num_ways--;
+ }
}
- /* Get the number of available ways per bank */
- unsigned long control_base = __metal_driver_sifive_ccache0_control_base(l2);
- uint32_t ways = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CCACHE0_CONFIG));
- ways = ((ways & L2_CONFIG_WAYS_MASK) >> L2_CONFIG_WAYS_SHIFT);
+ /* Enable ways */
+ ret = sifive_ccache0_set_enabled_ways(config.num_ways);
- /* Enable all the ways */
- __metal_driver_sifive_ccache0_init(l2, ways);
-#endif
+ return ret;
}
-void __metal_driver_sifive_ccache0_init(struct metal_cache *l2, int ways)
-{
- metal_cache_set_enabled_ways(l2, ways);
+void sifive_ccache0_get_config(sifive_ccache0_config *config) {
+ uint32_t val;
+
+ if (config) /* Check for NULL */
+ {
+ val = REGW(METAL_SIFIVE_CCACHE0_CONFIG);
+
+ /* Populate cache configuration data */
+ config->num_bank = (val & SIFIVE_CCACHE0_BYTE_MASK);
+ config->num_ways = ((val >> REG_SHIFT_8) & SIFIVE_CCACHE0_BYTE_MASK);
+ config->num_sets =
+ 2 << (((val >> REG_SHIFT_16) & SIFIVE_CCACHE0_BYTE_MASK) - 1);
+ config->block_size =
+ 2 << (((val >> REG_SHIFT_24) & SIFIVE_CCACHE0_BYTE_MASK) - 1);
+ }
}
-int __metal_driver_sifive_ccache0_get_enabled_ways(struct metal_cache *cache)
-{
- unsigned long control_base = __metal_driver_sifive_ccache0_control_base(cache);
+uint32_t sifive_ccache0_get_enabled_ways(void) {
- uint32_t way_enable = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CCACHE0_WAYENABLE));
+ uint32_t val = 0;
- /* The stored number is the index, so add one */
- return (0xFF & way_enable) + 1;
+ val = SIFIVE_CCACHE0_BYTE_MASK & REGW(METAL_SIFIVE_CCACHE0_WAYENABLE);
+
+ /* The stored number is the way index, so increment by 1 */
+ val++;
+
+ return val;
}
-int __metal_driver_sifive_ccache0_set_enabled_ways(struct metal_cache *cache, int ways)
-{
- unsigned long control_base = __metal_driver_sifive_ccache0_control_base(cache);
+int sifive_ccache0_set_enabled_ways(uint32_t ways) {
+
+ int ret = 0;
/* We can't decrease the number of enabled ways */
- if(metal_cache_get_enabled_ways(cache) > ways) {
- return -2;
+ if (sifive_ccache0_get_enabled_ways() > ways) {
+ ret = -1;
+ } else {
+ /* The stored value is the index, so subtract one */
+ uint32_t value = 0xFF & (ways - 1);
+
+ /* Set the number of enabled ways */
+ REGW(METAL_SIFIVE_CCACHE0_WAYENABLE) = value;
+
+ /* Make sure the number of ways was set correctly */
+ if (sifive_ccache0_get_enabled_ways() != ways) {
+ ret = -2;
+ }
}
- /* The stored value is the index, so subtract one */
- uint32_t value = 0xFF & (ways - 1);
+ return ret;
+}
+
+void sifive_ccache0_inject_ecc_error(uint32_t bitindex,
+ sifive_ccache0_ecc_errtype_t type) {
+ /* Induce ECC error at given bit index and location */
+ REGW(METAL_SIFIVE_CCACHE0_ECCINJECTERROR) =
+ (uint32_t)(((type & 0x01) << REG_SHIFT_16) | (bitindex & 0xFF));
+}
+
+void sifive_ccache0_flush(uintptr_t flush_addr) {
+ /* Block memory access until operation completed */
+ __asm volatile("fence rw, io" : : : "memory");
+
+#if __riscv_xlen == 32
+ REGW(METAL_SIFIVE_CCACHE0_FLUSH32) = flush_addr >> REG_SHIFT_4;
+#else
+ REGD(METAL_SIFIVE_CCACHE0_FLUSH64) = flush_addr;
+#endif
+
+ __asm volatile("fence io, rw" : : : "memory");
+}
+
+uintptr_t sifive_ccache0_get_ecc_fix_addr(sifive_ccache0_ecc_errtype_t type) {
+ uintptr_t addr = 0;
+
+ switch (type) {
+ /* Get most recently ECC corrected address */
+ case SIFIVE_CCACHE0_DATA:
+ addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DATECCFIXLOW);
+ break;
+
+ case SIFIVE_CCACHE0_DIR:
+ addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DIRECCFIXLOW);
+ break;
+ }
+
+ return addr;
+}
+
+uint32_t sifive_ccache0_get_ecc_fix_count(sifive_ccache0_ecc_errtype_t type) {
+ uint32_t count = 0;
+
+ switch (type) {
+ /* Get number of times ECC errors were corrected */
+ case SIFIVE_CCACHE0_DATA:
+ count = REGW(METAL_SIFIVE_CCACHE0_DATECCFIXCOUNT);
+ break;
+
+ case SIFIVE_CCACHE0_DIR:
+ count = REGW(METAL_SIFIVE_CCACHE0_DIRECCFIXCOUNT);
+ break;
+ }
+
+ return count;
+}
+
+uintptr_t sifive_ccache0_get_ecc_fail_addr(sifive_ccache0_ecc_errtype_t type) {
+ uintptr_t addr = 0;
+
+ switch (type) {
+ /* Get address location of most recent uncorrected ECC error */
+ case SIFIVE_CCACHE0_DATA:
+ addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DATECCFAILLOW);
+ break;
+
+ case SIFIVE_CCACHE0_DIR:
+ addr = (uintptr_t)REGD(METAL_SIFIVE_CCACHE0_DIRECCFAILLOW);
+ break;
+ }
+
+ return addr;
+}
+
+uint32_t sifive_ccache0_get_ecc_fail_count(sifive_ccache0_ecc_errtype_t type) {
+ uint32_t count = 0;
- /* Set the number of enabled ways */
- __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CCACHE0_WAYENABLE)) = value;
+ switch (type) {
+ /* Get number of times ECC errors were not corrected */
+ case SIFIVE_CCACHE0_DATA:
+ count = REGW(METAL_SIFIVE_CCACHE0_DATECCFAILCOUNT);
+ break;
- /* Make sure the number of ways was set correctly */
- if(metal_cache_get_enabled_ways(cache) != ways) {
- return -3;
+ case SIFIVE_CCACHE0_DIR:
+ count = REGW(METAL_SIFIVE_CCACHE0_DIRECCFAILCOUNT);
+ break;
}
+ return count;
+}
+
+uint64_t sifive_ccache0_get_way_mask(uint32_t master_id) {
+ uint64_t val = 0;
+
+ /* Get way mask for given master ID */
+ val = REGD(METAL_SIFIVE_CCACHE0_WAYMASK0 + master_id * 8);
+
+ return val;
+}
+
+int sifive_ccache0_set_way_mask(uint32_t master_id, uint64_t waymask) {
+
+ /* Set way mask for given master ID */
+ REGD(METAL_SIFIVE_CCACHE0_WAYMASK0 + master_id * 8) = waymask;
+
return 0;
}
-__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_ccache0) = {
- .cache.init = __metal_driver_sifive_ccache0_init,
- .cache.get_enabled_ways = __metal_driver_sifive_ccache0_get_enabled_ways,
- .cache.set_enabled_ways = __metal_driver_sifive_ccache0_set_enabled_ways,
-};
+void sifive_ccache0_set_pmevent_selector(uint32_t counter, uint64_t mask) {
+
+#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0
+ if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) {
+
+ /* Set event selector for specified L2 event counter */
+ REGD(METAL_SIFIVE_CCACHE0_PMEVENTSELECT0 + counter * 8) = mask;
+ }
+#endif
+ return;
+}
+
+uint64_t sifive_ccache0_get_pmevent_selector(uint32_t counter) {
+ uint64_t val = 0;
+
+#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0
+ if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) {
+
+ /* Get event selector for specified L2 event counter */
+ val = REGD(METAL_SIFIVE_CCACHE0_PMEVENTSELECT0 + counter * 8);
+ }
+#endif
+ return val;
+}
+
+void sifive_ccache0_clr_pmevent_counter(uint32_t counter) {
+
+#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0
+ if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) {
+ /* Clear specified L2 event counter */
+ REGD(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter * 8) = 0;
+ }
+#endif
+ return;
+}
+
+uint64_t sifive_ccache0_get_pmevent_counter(uint32_t counter) {
+#if __riscv_xlen == 32
+ uint32_t vh = 0, vh1 = 0, vl = 0;
+#else
+ uint64_t val = 0;
+#endif
+#if METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS > 0
+ if (counter < METAL_SIFIVE_CCACHE0_PERFMON_COUNTERS) {
+ /* Set counter register offset */
+ counter *= 8;
+
+#if __riscv_xlen == 32
+ do {
+ vh = REGW(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter + 4);
+ vl = REGW(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter);
+ vh1 = REGW(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter + 4);
+ } while (vh != vh1);
+#else
+ val = REGD(METAL_SIFIVE_CCACHE0_PMEVENTCOUNTER0 + counter);
+#endif
+ }
+#endif
+#if __riscv_xlen == 32
+ return ((((unsigned long long)vh) << 32) | vl);
+#else
+ return val;
+#endif
+}
+
+void sifive_ccache0_set_client_filter(uint64_t mask) {
+
+ /* Set clients to be excluded from performance monitoring */
+ REGD(METAL_SIFIVE_CCACHE0_PMCLIENTFILTER) = mask;
+}
+
+uint64_t sifive_ccache0_get_client_filter(void) {
+ uint64_t val = 0;
+
+ /* Get currently active client filter mask */
+ val = REGD(METAL_SIFIVE_CCACHE0_PMCLIENTFILTER);
+
+ return val;
+}
+
+int sifive_ccache0_get_interrupt_id(uint32_t src) {
+ int ret = 0;
+
+ if (src < (uint32_t)sizeof(sifive_ccache0_interrupts) / sizeof(int)) {
+ ret = sifive_ccache0_interrupts[src];
+ }
+
+ return ret;
+}
+
+struct metal_interrupt *sifive_ccache0_interrupt_controller(void) {
+ return METAL_SIFIVE_CCACHE0_INTERRUPT_PARENT;
+}
#endif