diff options
| -rw-r--r-- | bsp/drivers/fe300prci/fe300prci_driver.c | 252 | ||||
| -rw-r--r-- | bsp/drivers/fe300prci/fe300prci_driver.h | 79 | ||||
| -rw-r--r-- | bsp/drivers/plic/plic_driver.c (renamed from software/demo_gpio/plic_driver.c) | 2 | ||||
| -rw-r--r-- | bsp/drivers/plic/plic_driver.h (renamed from software/demo_gpio/plic_driver.h) | 7 | ||||
| -rw-r--r-- | bsp/env/common.mk | 3 | ||||
| -rw-r--r-- | bsp/env/hifive1.h | 2 | ||||
| -rw-r--r-- | software/demo_gpio/Makefile | 6 | ||||
| -rw-r--r-- | software/demo_gpio/demo_gpio.c | 2 | 
8 files changed, 347 insertions, 6 deletions
| diff --git a/bsp/drivers/fe300prci/fe300prci_driver.c b/bsp/drivers/fe300prci/fe300prci_driver.c new file mode 100644 index 0000000..2d9c52f --- /dev/null +++ b/bsp/drivers/fe300prci/fe300prci_driver.c @@ -0,0 +1,252 @@ +// 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, and 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 ) +{ + +  uint32_t hfrosctrim = 0; +  uint32_t hfroscdiv = 4; +  uint32_t prev_trim = 0; + +  // In this function we use PLL settings which +  // will give us a 32x multiplier from the output +  // of the HFROSC source to the output of the +  // PLL. We first measure our HFROSC to get the +  // right trim, then finally use it as the PLL source. +  // We should really check here that the f_cpu +  // requested is something in the limit of the PLL. For +  // now that is up to the user. + +  // This will undershoot for frequencies not divisible by 16. +  uint32_t desired_hfrosc_freq = (f_cpu/ 16); + +  PRCI_use_hfrosc(hfroscdiv, hfrosctrim); +   +  // Ignore the first run (for icache reasons) +  uint32_t cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ); + +  cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ); +  uint32_t prev_freq = cpu_freq; +   +  while ((cpu_freq < desired_hfrosc_freq) && (hfrosctrim < 0x1F)){ +    prev_trim = hfrosctrim; +    prev_freq = cpu_freq; +    hfrosctrim ++; +    PRCI_use_hfrosc(hfroscdiv, hfrosctrim); +    cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ); +  }  + +  // We couldn't go low enough +  if (prev_freq > desired_hfrosc_freq){ +    PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); +    cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ); +    return cpu_freq; +  } +   +  // We couldn't go high enough +  if (cpu_freq < desired_hfrosc_freq){ +    PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); +    cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ); +    return cpu_freq; +  } + +  // Check for over/undershoot +  switch(target) { +  case(PRCI_FREQ_CLOSEST): +    if ((desired_hfrosc_freq - prev_freq) < (cpu_freq - desired_hfrosc_freq)) { +      PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); +    } else { +      PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim); +    } +    break; +  case(PRCI_FREQ_UNDERSHOOT): +    PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); +    break; +  default: +    PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim); +  } + +  cpu_freq =  PRCI_measure_mcycle_freq(1000, RTC_FREQ); +  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..7100f46 --- /dev/null +++ b/bsp/drivers/fe300prci/fe300prci_driver.h @@ -0,0 +1,79 @@ +// 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 + * while using HFROSC as the clock source,  + * measure the resulting frequency, then + * use it as the PLL clock source,  + * in an attempt to get over, under, or 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. The PLL + * is rated higher than the hardware. + *  + * There is no check on the desired f_cpu frequency, it + * is up to the user to specify something reasonable. + */ + +uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target); + +__END_DECLS + +#endif +   diff --git a/software/demo_gpio/plic_driver.c b/bsp/drivers/plic/plic_driver.c index 01b7e6e..b27d7a5 100644 --- a/software/demo_gpio/plic_driver.c +++ b/bsp/drivers/plic/plic_driver.c @@ -1,7 +1,7 @@  // See LICENSE for license details.  #include "sifive/devices/plic.h" -#include "plic_driver.h" +#include "plic/plic_driver.h"  #include "platform.h"  #include "encoding.h"  #include <string.h> diff --git a/software/demo_gpio/plic_driver.h b/bsp/drivers/plic/plic_driver.h index 66410be..e7d609b 100644 --- a/software/demo_gpio/plic_driver.h +++ b/bsp/drivers/plic/plic_driver.h @@ -3,6 +3,9 @@  #ifndef PLIC_DRIVER_H  #define PLIC_DRIVER_H + +__BEGIN_DECLS +  #include "platform.h"  typedef struct __plic_instance_t @@ -42,5 +45,7 @@ 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 */ diff --git a/software/demo_gpio/Makefile b/software/demo_gpio/Makefile index 04a11b7..b181c5f 100644 --- a/software/demo_gpio/Makefile +++ b/software/demo_gpio/Makefile @@ -1,7 +1,9 @@  TARGET = demo_gpio -C_SRCS += demo_gpio.c -C_SRCS += plic_driver.c  CFLAGS += -O2 -fno-builtin-printf -DUSE_PLIC -DUSE_M_TIME  BSP_BASE = ../../bsp + +C_SRCS += demo_gpio.c +C_SRCS += $(BSP_BASE)/drivers/plic/plic_driver.c +  include $(BSP_BASE)/env/common.mk diff --git a/software/demo_gpio/demo_gpio.c b/software/demo_gpio/demo_gpio.c index 00bc8cd..bfa388e 100644 --- a/software/demo_gpio/demo_gpio.c +++ b/software/demo_gpio/demo_gpio.c @@ -4,7 +4,7 @@  #include <stdlib.h>  #include "platform.h"  #include <string.h> -#include "plic_driver.h" +#include "plic/plic_driver.h"  #include "encoding.h"  #include <unistd.h>  #include "stdatomic.h" | 
