Something I learned about the CPU clock


#1

The FE310-G002 manual is not real clear about this and I discovered it by experiment. On page 30 it talks about the PLLBYPASS flag bit, which not only bypasses the PLL, it powers it down. The text states that the PLL can be reconfigured in this state in the PLLCFG register, which is true. It also says that the PLL should not be used until it has acheived lock.

What it does not come right out and say is that, until you clear the PLLBYPASS bit, the PLL will never acheive lock because it is powered down! So the reliable sequence is,

  1. Clear the PLLSEL bit, which makes the CPU use the Ring oscillator as the clock. For me this turns out to be 20.2 MHz with the default settings but your mileage may vary. The Ring oscillator is not very precise so don’t run this way for long.
  2. Now you can set PLLBYPASS which powers down the PLL.
  3. Change the PLL R, F, and Q factors as desired, see pages 29 and 30.
  4. Clear PLLBYPASS. This starts up the PLL again and it will try to acheive lock.
  5. Wait at least 100 microseconds for the lock indicator to become stable. I use 4 ticks of the 32 KHz low-frequency timer for this.
  6. Wait for the PLLLOCKED bit to turn on. This is the high-order bit in the PLLCFG register.
  7. Finally set the PLLSEL bit to switch the CPU from using the Ring oscillator to the PLL output.

At this point you will probably need to change the clock dividers for UARTs and SPI devices to account for the new central clock frequency.


#2

Hello LightBulb,

Thank you for your post, it is very helpful.

However I am new in this field and I would like to make you a question. I have posted a question here about clock generation on HiFive1. I have read the documentation but it is still not clear to me how to change the values of these registers that you are mentioning. I cannot find an interface for that in the sdk, thus I assume that I should use assembly for that (or maybe I am wrong). The only relevant variables that I find in the repo are the PLL_{R, F, Q, SEL, REFSEL, BYPASS, LOCK} constants in sifive_fe310-g000_pll.c by I am not sure if I can use these constants somehow in my code.

For example, I know that the PLLSEL is the 16th (numbering starts from 0) bit of the pllcfg register. However, I am not sure how to modify this register. Any help here is appreciated.

Thanks in advance and I am sorry if my question is silly,
Stef


#3

I’m sorry, I have no experience using the C libraries for the HiFive1. But looking at that code, it seems they supply high level routines to do the work for you… I would start with __metal_driver_sifive_fe310_g000_pll_set_rate_hz


#4

Hello LightBulb,

Thanks for the info. I have seen the functions that exist in this file and they seem to are for this exact purpose. However, it is not clear to me yet how I will define a valid struct metal_clock that I use as an argument for these functions, but I think I can figure out. Also in the meanwhile, I have examined the 16 (4*4) bytes in the address 0x1000_8000 that contain the registers hfrosccfg, hfxosccfg, pllcfg, and plloutdiv so now I know exactly how my device is setup before I try to change anything.

I can also try modifying these registers through my code with some pointers in those addresses.

Best,
Stef


#5

Keep in mind that if you change the CPU clock speed, and you are using UART0 for console input or output, you must also change the UART0 clock divider to compensate, in order to maintain the same console baud rate. The J-Link interface is separate even though both come through the same USB cable.

It looks like if you call that routine with the desired clock rate, it will set a speed as close as possible to the one you request. You can’t set just any arbitrary speed, as is outlined in Chapter 6.


#6

You mean that I can use the formula
uart_div

that is mentioned in the fe310 manual and set div = cpu_frequency/fbaud - 1 right?


#7

yes