Is it possible to brick the HiFive board?

After messing around with hfxosccfg and hfrosccfg I can’t connect via openocd anymore or flash a new program.

The code that supposedly bricked the board would be:

impl<'a> AonClock<'a> {
    /// Use external real time oscillator (default)
    pub unsafe fn use_external(&self) {
        // FIXME: lfxosccfg and lfrosccfg are undocumented. Does lfxosccfg
        // have a ready field?

        // Enable LFXOSC
        self.0.lfxosccfg.write(|w| w.enable().bit(true));
        // Disable LFROSC
        self.0.lfrosccfg.write(|w| w.enable().bit(false));

impl<'a> CoreClock<'a> {
    pub unsafe fn use_external(&self) {
        // Enable HFXOSC
        self.0.hfxosccfg.write(|w| w.enable().bit(true));
        // Wait for HFXOSC to stabilize
        while ! {}
        // Disable bypass to select HFXOSC
        self.0.pllcfg.modify(|_, w| w.bypass().bit(false));
        // Disable HFROSC to save power
        self.0.hfrosccfg.write(|w| w.enable().bit(false));

Would desoldering R13 halting via openocd, resolder R13 while power on and then reflashing work?

What if you use the “safe” boot loader mode? You should be able to reprogram it then. You do it by double tapping the reset button (when it flashes green, hit the reset button again, it should blink red indicating it’s in safe bootloader mode).

1 Like

Awesome! That worked :smile:

1 Like

You definitely can brick it, if you modify the One-Time-Programmable ROM. But that’s not easy to do by accident.

By the way @dvc, I believe the problem with your code snippet above is that in your CoreClock block you aren’t ensuring that you are running off HFROSC before changing the PLL settings. I don’t know what you did before this, but make sure that you are running off the HFROSC before changing anything about the PLL (including switching it to use the HFXOSC bypass). The HFROSC is designed to allow you to change its settings while running off of it, the PLL is not.

Note that on the HiFive1, there is no LFXOSC. The LFROSC is also not used, because there is a pin which selects the LFCLK source to be the external 32kHz MEMS Oscillator on the board. This is not controllable by software. It’s still worth turning off the LFROSC since the board isn’t using it.

mmh, doesn’t the bypass field control the mux in FIgure 4.1: E300 clock generation scheme (psdclkbypass_n)? In the diagram that looks like it’s outside the PLL block.

HFXOSC: bypass 0
HFROSC: pllsel 0, bypass 1

Ok I misunderstood the diagram, now it makes sense…

I find all this very confusing and it doesn’t seem to be easy to find a clear definition of the various oscillators and clocks.

Could you please correct anything erroneous in the following?

  1. there is a Low Frequency Oscillator LFROSC on the FE310, which can be used as the source of a LFCLK that the CPU can run from. I don’t know what frequency the on-chip LFOSC can run at.

  2. however on the HiFive1 the LFCLK is selected to come from an external clock, which is 32 kHz.

  3. there is a High Frequency Oscillator, which uses an external 16 MHz crystal.

  4. there is a PLL which runs at some integer multiple of the HFOSC, with 16 and 20 being the most common (256 and 320 MHz)

@brucehoult Maybe my clock driver helps clarify things. It’s intended to be bullet proof not efficient (but most of the functions are inlined so it’s pretty good actually).

impl<'a> CoreClock<'a> {
    pub unsafe fn use_external(&self, clint: &Clint) {
        self.use_pll(clint, |_, w| {
            // bypass PLL
                // select HFXOSC
        }, |w| w.divby1().bit(true));

    unsafe fn wait_for_lock(&self, clint: &Clint) {
        // Won't lock when bypassed and will loop forever
        if ! {
            // 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.
            let time = clint.get_mtime() + ::aonclk::Ticks(4);
            while clint.get_mtime() < time {}
            // Now it is safe to check for PLL Lock
            while ! {}

    pub unsafe fn use_pll<F, G>(&self, clint: &Clint, pllcfg: F, plloutdiv: G)
        for<'w> F: FnOnce(&prci::pllcfg::R,
                          &'w mut prci::pllcfg::W) -> &'w mut prci::pllcfg::W,
        for<'w> G: FnOnce(&'w mut prci::plloutdiv::W) -> &'w mut prci::plloutdiv::W,
        // Make sure we are running of internal clock
        // before configuring the PLL.
        // Enable HFXOSC
        self.0.hfxosccfg.write(|w| w.enable().bit(true));
        // Wait for HFXOSC to stabilize
        while ! {}
        // Configure PLL
        // Wait for PLL lock
        // Switch to PLL
        self.0.pllcfg.modify(|_, w| {
        // Disable HFROSC to save power
        self.0.hfrosccfg.write(|w| w.enable().bit(false));

    pub unsafe fn use_internal(&self) {
        // Enable HFROSC
        self.0.hfrosccfg.write(|w| {
            // It is OK to change this even if we are running off of it.
            // Reset them to default values.
        // Wait for HFROSC to stabilize
        while ! {}
        // Switch to HFROSC
        self.0.pllcfg.modify(|_, w| {
        // Bypass PLL to save power
        self.0.pllcfg.modify(|_, w| {
            // Select HFROSC as PLL ref to disable HFXOSC later
        // Disable HFXOSC to save power.
        self.0.hfxosccfg.write(|w| w.enable().bit(false));

No, not even slightly, I’m afraid.

I’ve read the C code sources for the system. And just like there, this code is simply using names such as “hfosc” (and all the others) without defining or describing them in any adequate way.

These are the clock’s in an E310 SoC:
Core 16MHz
HFXOSC (external) (available G000)
HFROSC (internal) (available G000)

Real time 32768Hz
LFXOSC (external)
LFRCOSC (internal)
LFROSC (internal) (available on G000 but unusable)
psdlfaltclk (external) (required on G000)

Manufacturing time options:
HF crystal oscillator (available G000)
PLL (available G000)
LF crystal oscillator
LF RC oscillator

At manufacturing time configuration options:
psdclkbypass_n (pulled up on G000) so HFXOSC is always used through the PLL regardless of if it’s bypassed or not.
psdlfclksel (pulled down on G000) so LFROSC can’t be used and psdlfaltclk is always used.

Reference Figure 4.1: E300 clock generation scheme.

What is missing is a matrix of configuration options for the G000 chip in the G000 manual.

@brucehoult from your list of 4, all is correct, except that for point (4), the reason we are able to achieve the higher PLL frequency is by using the HFROSC (as apposed to the HFXOSC) as the source to the PLL, then increasing its frequency (by tweaking its trim settings) until we reach the 320 MHz.

@dvc there is no LFXOSC in the FE310-G000, only LFROSC and external clock pin lfaltclk (in other words, it does not have low-frequency crystal driver pads).

The point is well taken from this conversation that the exact clock configuration in the FE310-G000 Silicon + 48-pin package + HiFive1 board all need to be more clear, vs the generic FE300 clocking scheme.

Some other things that are unclear to me:
Where are the sources for the OTP code?
Is there any documentation on the SBI other than the code in the linux port? Are any sbi calls implemented in the OTP? Obviously 6 and 7 aren’t, but RTOS’s might benefit from the other ones…

#define SBI_SET_TIMER 0
#define SBI_CLEAR_IPI 3
#define SBI_SEND_IPI 4
#define SBI_SHUTDOWN 8

You can find the sources of the OTP code in the HiFive1 Getting Started Guide, Page 21:

There is not much in the OTP, and no SBI calls are implemented there.

1 Like

Ah, I didn’t read the getting started guide. The additional notes section is the missing piece I was looking for. Maybe you can put that with the schematic which is something I’m more likely to read :slight_smile: