summaryrefslogtreecommitdiff
path: root/bsp
diff options
context:
space:
mode:
Diffstat (limited to 'bsp')
-rw-r--r--bsp/drivers/fe300prci/fe300prci_driver.c251
-rw-r--r--bsp/drivers/fe300prci/fe300prci_driver.h71
-rw-r--r--bsp/drivers/plic/plic_driver.c127
-rw-r--r--bsp/drivers/plic/plic_driver.h51
-rw-r--r--bsp/env/common.mk3
-rw-r--r--bsp/env/hifive1.h2
6 files changed, 504 insertions, 1 deletions
diff --git a/bsp/drivers/fe300prci/fe300prci_driver.c b/bsp/drivers/fe300prci/fe300prci_driver.c
new file mode 100644
index 0000000..8491554
--- /dev/null
+++ b/bsp/drivers/fe300prci/fe300prci_driver.c
@@ -0,0 +1,251 @@
+// See LICENSE file for license details
+
+#include "platform.h"
+
+#ifdef PRCI_BASE_ADDR
+#include "fe300prci/fe300prci_driver.h"
+#include <unistd.h>
+
+#define rdmcycle(x) { \
+ uint32_t lo, hi, hi2; \
+ __asm__ __volatile__ ("1:\n\t" \
+ "csrr %0, mcycleh\n\t" \
+ "csrr %1, mcycle\n\t" \
+ "csrr %2, mcycleh\n\t" \
+ "bne %0, %2, 1b\n\t" \
+ : "=r" (hi), "=r" (lo), "=r" (hi2)) ; \
+ *(x) = lo | ((uint64_t) hi << 32); \
+ }
+
+uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq)
+{
+
+ uint32_t start_mtime = CLINT_REG(CLINT_MTIME);
+ uint32_t end_mtime = start_mtime + mtime_ticks + 1;
+
+ // Make sure we won't get rollover.
+ while (end_mtime < start_mtime){
+ start_mtime = CLINT_REG(CLINT_MTIME);
+ end_mtime = start_mtime + mtime_ticks + 1;
+ }
+
+ // Don't start measuring until mtime edge.
+ uint32_t tmp = start_mtime;
+ do {
+ start_mtime = CLINT_REG(CLINT_MTIME);
+ } while (start_mtime == tmp);
+
+ uint64_t start_mcycle;
+ rdmcycle(&start_mcycle);
+
+ while (CLINT_REG(CLINT_MTIME) < end_mtime) ;
+
+ uint64_t end_mcycle;
+ rdmcycle(&end_mcycle);
+ uint32_t difference = (uint32_t) (end_mcycle - start_mcycle);
+
+ uint64_t freq = ((uint64_t) difference * mtime_freq) / mtime_ticks;
+ return (uint32_t) freq & 0xFFFFFFFF;
+
+}
+
+
+void PRCI_use_hfrosc(int div, int trim)
+{
+ // Make sure the HFROSC is running at its default setting
+ // It is OK to change this even if we are running off of it.
+
+ PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(div) | ROSC_TRIM(trim) | ROSC_EN(1));
+
+ while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0);
+
+ PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(1);
+}
+
+void PRCI_use_pll(int refsel, int bypass,
+ int r, int f, int q, int finaldiv,
+ int hfroscdiv, int hfrosctrim)
+{
+ // Ensure that we aren't running off the PLL before we mess with it.
+ if (PRCI_REG(PRCI_PLLCFG) & PLL_SEL(1)) {
+ // Make sure the HFROSC is running at its default setting
+ PRCI_use_hfrosc(4, 16);
+ }
+
+ // Set PLL Source to be HFXOSC if desired.
+ uint32_t config_value = 0;
+
+ config_value |= PLL_REFSEL(refsel);
+
+ if (bypass) {
+ // Bypass
+ config_value |= PLL_BYPASS(1);
+
+ PRCI_REG(PRCI_PLLCFG) = config_value;
+
+ // If we don't have an HFXTAL, this doesn't really matter.
+ // Set our Final output divide to divide-by-1:
+ PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0));
+ } else {
+
+ // To overclock, use the hfrosc
+ if (hfrosctrim >= 0 && hfroscdiv >= 0) {
+ PRCI_use_hfrosc(hfroscdiv, hfrosctrim);
+ }
+
+ // Set DIV Settings for PLL
+
+ // (Legal values of f_REF are 6-48MHz)
+
+ // Set DIVR to divide-by-2 to get 8MHz frequency
+ // (legal values of f_R are 6-12 MHz)
+
+ config_value |= PLL_BYPASS(1);
+ config_value |= PLL_R(r);
+
+ // Set DIVF to get 512Mhz frequncy
+ // There is an implied multiply-by-2, 16Mhz.
+ // So need to write 32-1
+ // (legal values of f_F are 384-768 MHz)
+ config_value |= PLL_F(f);
+
+ // Set DIVQ to divide-by-2 to get 256 MHz frequency
+ // (legal values of f_Q are 50-400Mhz)
+ config_value |= PLL_Q(q);
+
+ // Set our Final output divide to divide-by-1:
+ if (finaldiv == 1){
+ PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0));
+ } else {
+ PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV(finaldiv-1));
+ }
+
+ PRCI_REG(PRCI_PLLCFG) = config_value;
+
+ // Un-Bypass the PLL.
+ PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1);
+
+ // Wait for PLL Lock
+ // Note that the Lock signal can be glitchy.
+ // Need to wait 100 us
+ // RTC is running at 32kHz.
+ // So wait 4 ticks of RTC.
+ uint32_t now = CLINT_REG(CLINT_MTIME);
+ while (CLINT_REG(CLINT_MTIME) - now < 4) ;
+
+ // Now it is safe to check for PLL Lock
+ while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0);
+
+ }
+
+ // Switch over to PLL Clock source
+ PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1);
+
+ // If we're running off HFXOSC, turn off the HFROSC to
+ // save power.
+ if (refsel) {
+ PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1);
+ }
+
+}
+
+void PRCI_use_default_clocks()
+{
+ // Turn off the LFROSC
+ AON_REG(AON_LFROSC) &= ~ROSC_EN(1);
+
+ // Use HFROSC
+ PRCI_use_hfrosc(4, 16);
+}
+
+void PRCI_use_hfxosc(uint32_t finaldiv)
+{
+
+ PRCI_use_pll(1, // Use HFXTAL
+ 1, // Bypass = 1
+ 0, // PLL settings don't matter
+ 0, // PLL settings don't matter
+ 0, // PLL settings don't matter
+ finaldiv,
+ -1,
+ -1);
+}
+
+// This is a generic function, which
+// doesn't span the entire range of HFROSC settings.
+// It only adjusts the trim, which can span a hundred MHz or so.
+// This function does not check the legality of the PLL settings
+// at all, ant it is quite possible to configure invalid PLL settings
+// this way.
+// It returns the actual measured CPU frequency.
+
+uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target )
+{
+
+ int hfrosctrim = 16;
+ PRCI_use_default_clocks();
+ PRCI_use_pll(0, 0, 1, 31, 1, 1, 4, 16);
+ // Ignore the first run (for icache reasons)
+ uint32_t cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ int prev_trim = hfrosctrim;
+ int prev_freq = cpu_freq;
+
+ if (cpu_freq > F_CPU) {
+ while((cpu_freq > F_CPU) && (hfrosctrim > 0) ){
+ prev_freq = cpu_freq;
+ prev_trim = hfrosctrim;
+ hfrosctrim--;
+ PRCI_use_pll(0, 0, 1, 31, 1, 1, 4, hfrosctrim );
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ }
+
+ // Check for over/undershoot
+ switch(target) {
+ case(PRCI_FREQ_CLOSEST):
+ if ((prev_freq - F_CPU) < (F_CPU - cpu_freq) && target == PRCI_FREQ_CLOSEST) {
+ PRCI_use_pll(0, 0, 1, 31, 1, 1, 4, prev_trim);
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ }
+ break;
+ case(PRCI_FREQ_OVERSHOOT):
+ PRCI_use_pll(0, 0, 1, 31, 1, 1, 4, prev_trim);
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ break;
+ //default:
+ // Do Nothing
+ }
+
+ return cpu_freq;
+ }
+
+ while(cpu_freq < F_CPU && hfrosctrim < 0x20) {
+ prev_freq = cpu_freq;
+ prev_trim = hfrosctrim;
+ hfrosctrim ++;
+ PRCI_use_pll(0, 0, 1, 31, 1, 1, 4, hfrosctrim );
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ }
+
+
+ // Check for over/undershoot
+ switch(target) {
+ case(PRCI_FREQ_CLOSEST):
+ if ((F_CPU - prev_freq) < (cpu_freq - F_CPU)) {
+ PRCI_use_pll(0, 0, 1, 31, 1, 1, 4, prev_trim);
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ }
+ break;
+ case(PRCI_FREQ_UNDERSHOOT):
+ PRCI_use_pll(0, 0, 1, 31, 1, 1, 4, prev_trim);
+ cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
+ break;
+ //default:
+ // Do Nothing
+ }
+
+ return cpu_freq;
+
+}
+
+#endif
diff --git a/bsp/drivers/fe300prci/fe300prci_driver.h b/bsp/drivers/fe300prci/fe300prci_driver.h
new file mode 100644
index 0000000..243e6de
--- /dev/null
+++ b/bsp/drivers/fe300prci/fe300prci_driver.h
@@ -0,0 +1,71 @@
+// See LICENSE file for license details
+
+#ifndef _FE300PRCI_DRIVER_H_
+#define _FE300PRCI_DRIVER_H_
+
+__BEGIN_DECLS
+
+#include <unistd.h>
+
+typedef enum prci_freq_target {
+
+ PRCI_FREQ_OVERSHOOT,
+ PRCI_FREQ_CLOSEST,
+ PRCI_FREQ_UNDERSHOOT
+
+} PRCI_freq_target;
+
+/* Measure and return the approximate frequency of the
+ * CPU, as given by measuring the mcycle counter against
+ * the mtime ticks.
+ */
+uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq);
+
+/* Safely switch over to the HFROSC using the given div
+ * and trim settings.
+ */
+void PRCI_use_hfrosc(int div, int trim);
+
+/* Safely switch over to the 16MHz HFXOSC,
+ * applying the finaldiv clock divider (1 is the lowest
+ * legal value).
+ */
+void PRCI_use_hfxosc(uint32_t finaldiv);
+
+/* Safely switch over to the PLL using the given
+ * settings.
+ *
+ * Note that not all combinations of the inputs are actually
+ * legal, and this function does not check for their
+ * legality ("safely" means that this function won't turn off
+ * or glitch the clock the CPU is actually running off, but
+ * doesn't protect against you making it too fast or slow.)
+ */
+
+void PRCI_use_pll(int refsel, int bypass,
+ int r, int f, int q, int finaldiv,
+ int hfroscdiv, int hfrosctrim);
+
+/* Use the default clocks configured at reset.
+ * This is ~16Mhz HFROSC and turns off the LFROSC
+ * (on the current FE310 Dev Platforms, an external LFROSC is
+ * used as it is more power efficient).
+ */
+void PRCI_use_default_clocks();
+
+/* This routine will adjust the HFROSC trim
+ * as the PLL clock source, measure the resulting
+ * frequency, and attempt to get close to the requested
+ * frequency. It returns the actual measured frequency.
+ *
+ * Note that the requested frequency must be within the
+ * range supported by the PLL so not all values are
+ * achievable with this function, and not all
+ * are guaranteed to actually work.
+ */
+uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target);
+
+__END_DECLS
+
+#endif
+
diff --git a/bsp/drivers/plic/plic_driver.c b/bsp/drivers/plic/plic_driver.c
new file mode 100644
index 0000000..b27d7a5
--- /dev/null
+++ b/bsp/drivers/plic/plic_driver.c
@@ -0,0 +1,127 @@
+// See LICENSE for license details.
+
+#include "sifive/devices/plic.h"
+#include "plic/plic_driver.h"
+#include "platform.h"
+#include "encoding.h"
+#include <string.h>
+
+
+// Note that there are no assertions or bounds checking on these
+// parameter values.
+
+void volatile_memzero(uint8_t * base, unsigned int size)
+{
+ volatile uint8_t * ptr;
+ for (ptr = base; ptr < (base + size); ptr++){
+ *ptr = 0;
+ }
+}
+
+void PLIC_init (
+ plic_instance_t * this_plic,
+ uintptr_t base_addr,
+ uint32_t num_sources,
+ uint32_t num_priorities
+ )
+{
+
+ this_plic->base_addr = base_addr;
+ this_plic->num_sources = num_sources;
+ this_plic->num_priorities = num_priorities;
+
+ // Disable all interrupts (don't assume that these registers are reset).
+ unsigned long hart_id = read_csr(mhartid);
+ volatile_memzero((uint8_t*) (this_plic->base_addr +
+ PLIC_ENABLE_OFFSET +
+ (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET)),
+ (num_sources + 8) / 8);
+
+ // Set all priorities to 0 (equal priority -- don't assume that these are reset).
+ volatile_memzero ((uint8_t *)(this_plic->base_addr +
+ PLIC_PRIORITY_OFFSET),
+ (num_sources + 1) << PLIC_PRIORITY_SHIFT_PER_SOURCE);
+
+ // Set the threshold to 0.
+ volatile plic_threshold* threshold = (plic_threshold*)
+ (this_plic->base_addr +
+ PLIC_THRESHOLD_OFFSET +
+ (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET));
+
+ *threshold = 0;
+
+}
+
+void PLIC_set_threshold (plic_instance_t * this_plic,
+ plic_threshold threshold){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile plic_threshold* threshold_ptr = (plic_threshold*) (this_plic->base_addr +
+ PLIC_THRESHOLD_OFFSET +
+ (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET));
+
+ *threshold_ptr = threshold;
+
+}
+
+
+void PLIC_enable_interrupt (plic_instance_t * this_plic, plic_source source){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile uint8_t * current_ptr = (volatile uint8_t *)(this_plic->base_addr +
+ PLIC_ENABLE_OFFSET +
+ (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) +
+ (source >> 3));
+ uint8_t current = *current_ptr;
+ current = current | ( 1 << (source & 0x7));
+ *current_ptr = current;
+
+}
+
+void PLIC_disable_interrupt (plic_instance_t * this_plic, plic_source source){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile uint8_t * current_ptr = (volatile uint8_t *) (this_plic->base_addr +
+ PLIC_ENABLE_OFFSET +
+ (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) +
+ (source >> 3));
+ uint8_t current = *current_ptr;
+ current = current & ~(( 1 << (source & 0x7)));
+ *current_ptr = current;
+
+}
+
+void PLIC_set_priority (plic_instance_t * this_plic, plic_source source, plic_priority priority){
+
+ if (this_plic->num_priorities > 0) {
+ volatile plic_priority * priority_ptr = (volatile plic_priority *)
+ (this_plic->base_addr +
+ PLIC_PRIORITY_OFFSET +
+ (source << PLIC_PRIORITY_SHIFT_PER_SOURCE));
+ *priority_ptr = priority;
+ }
+}
+
+plic_source PLIC_claim_interrupt(plic_instance_t * this_plic){
+
+ unsigned long hart_id = read_csr(mhartid);
+
+ volatile plic_source * claim_addr = (volatile plic_source * )
+ (this_plic->base_addr +
+ PLIC_CLAIM_OFFSET +
+ (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET));
+
+ return *claim_addr;
+
+}
+
+void PLIC_complete_interrupt(plic_instance_t * this_plic, plic_source source){
+
+ unsigned long hart_id = read_csr(mhartid);
+ volatile plic_source * claim_addr = (volatile plic_source *) (this_plic->base_addr +
+ PLIC_CLAIM_OFFSET +
+ (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET));
+ *claim_addr = source;
+
+}
+
diff --git a/bsp/drivers/plic/plic_driver.h b/bsp/drivers/plic/plic_driver.h
new file mode 100644
index 0000000..e7d609b
--- /dev/null
+++ b/bsp/drivers/plic/plic_driver.h
@@ -0,0 +1,51 @@
+// See LICENSE file for licence details
+
+#ifndef PLIC_DRIVER_H
+#define PLIC_DRIVER_H
+
+
+__BEGIN_DECLS
+
+#include "platform.h"
+
+typedef struct __plic_instance_t
+{
+ uintptr_t base_addr;
+
+ uint32_t num_sources;
+ uint32_t num_priorities;
+
+} plic_instance_t;
+
+typedef uint32_t plic_source;
+typedef uint32_t plic_priority;
+typedef uint32_t plic_threshold;
+
+void PLIC_init (
+ plic_instance_t * this_plic,
+ uintptr_t base_addr,
+ uint32_t num_sources,
+ uint32_t num_priorities
+ );
+
+void PLIC_set_threshold (plic_instance_t * this_plic,
+ plic_threshold threshold);
+
+void PLIC_enable_interrupt (plic_instance_t * this_plic,
+ plic_source source);
+
+void PLIC_disable_interrupt (plic_instance_t * this_plic,
+ plic_source source);
+
+void PLIC_set_priority (plic_instance_t * this_plic,
+ plic_source source,
+ plic_priority priority);
+
+plic_source PLIC_claim_interrupt(plic_instance_t * this_plic);
+
+void PLIC_complete_interrupt(plic_instance_t * this_plic,
+ plic_source source);
+
+__END_DECLS
+
+#endif
diff --git a/bsp/env/common.mk b/bsp/env/common.mk
index 420c196..74f5582 100644
--- a/bsp/env/common.mk
+++ b/bsp/env/common.mk
@@ -19,6 +19,7 @@ C_SRCS += $(PLATFORM_DIR)/init.c
LINKER_SCRIPT := $(PLATFORM_DIR)/link.lds
INCLUDES += -I$(BSP_BASE)/include
+INCLUDES += -I$(BSP_BASE)/drivers/
INCLUDES += -I$(ENV_DIR)
INCLUDES += -I$(PLATFORM_DIR)
@@ -47,7 +48,7 @@ $(ASM_OBJS): %.o: %.S $(HEADERS)
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
$(C_OBJS): %.o: %.c $(HEADERS)
- $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+ $(CC) $(CFLAGS) $(INCLUDES) -include sys/cdefs.h -c -o $@ $<
.PHONY: clean
clean:
diff --git a/bsp/env/hifive1.h b/bsp/env/hifive1.h
index 11b98f8..4c65f18 100644
--- a/bsp/env/hifive1.h
+++ b/bsp/env/hifive1.h
@@ -74,6 +74,8 @@
#define HAS_HFXOSC 1
#define HAS_LFROSC_BYPASS 1
+#define RTC_FREQ 32768
+
void write_hex(int fd, uint32_t hex);
#endif /* _SIFIVE_HIFIVE1_H */