HiFive1 Rev. B - How to use Wifi? How to get/set CPU clock speed?


#1

Documentation on this board is too sparse. For the average hobbyist it’s too cryptic to figure many things out. It took me several hours to get everything set up and I still don’t understand how to get wifi working. Are there any instructions or examples I can look up? All I found was this: https://github.com/sifive/riscv-zephyr/tree/hifive1-revb/drivers/wifi and this: Hifive1 revb WiFi
But these are ridiculously complex. Shouldn’t there just be a Wifi library?

Another thing I can’t figure out is how to get/set the clock speed. There’s metal_clock, but it’s totally unclear how I’m actually supposed to use it. This is the doc: ht tps://sifive.github.io/freedom-metal-docs/apiref/clock.html#structmetal__clock (This forum has a restriction for new users to only add two links into a post)
All the functions require a metal_clock, but the doc explicitly says:

Note that no mechanism for obtaining a pointer to a struct metal_clock has been defined, making it impossible to call any of these functions without invoking implementation-defined behavior.

What does that mean? I found no usage example of this, even though IMO this is a very basic functionality, considering this board was advertised as having a fast and low power chip, and as having wifi.

One more thing I can’t wrap my head around is why Rev. B has dropped support for the Arduino IDE which would have been much more user friendly than “FreedomStudio”. This wasn’t advertised anywhere except for some small note in chapter 7 or so of the getting started guide. The naming of Rev. B itself is also misleading, causing people to think that it’s going to be at least mostly compatible with the first one, but in fact a lot of the documentation for the first version is now obsolete - sifive should’ve called this hifive2 or something, to avoid confusion and make googling things easier.


SPI Hifive 1 Rev B
#2

Rev B is quite compatible with the first version. In the Rust support library we even use the same SVD file for both FE310-G000 and FE310-G002 chips. If you are familiar with Rust, I’d recommend you to give it a try. There is a quickstart project with some examples.


(Christian Herget) #3

Hello Yugen,

I had the same problem and I’m still not sure how to configure the PLL with the Metal library.
However I RTFM and I came up with the following code which seems to work for me:

#include <stdint.h>
#include <metal/machine.h>

#define FE320_G002_PLLCFG_ADDR          ( METAL_SIFIVE_FE310_G000_PRCI_0_BASE_ADDRESS + METAL_SIFIVE_FE310_G000_PRCI_PLLCFG )
#define FE320_G002_PLLCFG_READ( )       (   *( (uint32_t volatile *) FE320_G002_PLLCFG_ADDR ) )
#define FE320_G002_PLLCFG_WRITE( x_ )   ( ( *( (uint32_t volatile *) FE320_G002_PLLCFG_ADDR ) ) = ( x_ ) )

#define FE320_G002_PLLCFG_PLLR_SHIFT        ( 0 )
#define FE320_G002_PLLCFG_PLLR_MASK         ( 0x07uL << FE320_G002_PLLCFG_PLLR_SHIFT )
#define FE320_G002_PLLCFG_PLLF_SHIFT        ( 4 )
#define FE320_G002_PLLCFG_PLLF_MASK         ( 0x3FuL << FE320_G002_PLLCFG_PLLF_SHIFT )
#define FE320_G002_PLLCFG_PLLQ_SHIFT        ( 10 )
#define FE320_G002_PLLCFG_PLLQ_MASK         ( 0x02uL << FE320_G002_PLLCFG_PLLQ_SHIFT )
#define FE320_G002_PLLCFG_PLLSEL_SHIFT      ( 16 )
#define FE320_G002_PLLCFG_PLLSEL_MASK       ( 0x01uL << FE320_G002_PLLCFG_PLLSEL_SHIFT )
#define FE320_G002_PLLCFG_PLLREFSEL_SHIFT   ( 17 )
#define FE320_G002_PLLCFG_PLLREFSEL_MASK    ( 0x01uL << FE320_G002_PLLCFG_PLLREFSEL_SHIFT )
#define FE320_G002_PLLCFG_PLLBYPASS_SHIFT   ( 18 )
#define FE320_G002_PLLCFG_PLLBYPASS_MASK    ( 0x01uL << FE320_G002_PLLCFG_PLLBYPASS_SHIFT )
#define FE320_G002_PLLCFG_PLLLOCK_SHIFT     ( 31 )
#define FE320_G002_PLLCFG_PLLLOCK_MASK      ( 0x01uL << FE320_G002_PLLCFG_PLLLOCK_SHIFT )

#define FE320_G002_PLL_WRITE_PLLR( x_ )     ( FE320_G002_PLLCFG_WRITE( ( FE320_G002_PLLCFG_READ( ) & ~( FE320_G002_PLLCFG_PLLR_MASK ) ) | ( ( x_ ) << FE320_G002_PLLCFG_PLLR_SHIFT ) ) )
#define FE320_G002_PLL_WRITE_PLLF( x_ )     ( FE320_G002_PLLCFG_WRITE( ( FE320_G002_PLLCFG_READ( ) & ~( FE320_G002_PLLCFG_PLLF_MASK ) ) | ( ( x_ ) << FE320_G002_PLLCFG_PLLF_SHIFT ) ) )
#define FE320_G002_PLL_WRITE_PLLQ( x_ )     ( FE320_G002_PLLCFG_WRITE( ( FE320_G002_PLLCFG_READ( ) & ~( FE320_G002_PLLCFG_PLLQ_MASK ) ) | ( ( x_ ) << FE320_G002_PLLCFG_PLLQ_SHIFT ) ) )

#define FE320_G002_PLL_SET_SEL( )           ( FE320_G002_PLLCFG_WRITE( FE320_G002_PLLCFG_READ( ) |    FE320_G002_PLLCFG_PLLSEL_MASK   ) )
#define FE320_G002_PLL_CLR_SEL( )           ( FE320_G002_PLLCFG_WRITE( FE320_G002_PLLCFG_READ( ) & ~( FE320_G002_PLLCFG_PLLSEL_MASK ) ) )

#define FE320_G002_PLL_SET_REFSEL( )        ( FE320_G002_PLLCFG_WRITE( FE320_G002_PLLCFG_READ( ) |    FE320_G002_PLLCFG_PLLREFSEL_MASK   ) )
#define FE320_G002_PLL_CLR_REFSEL( )        ( FE320_G002_PLLCFG_WRITE( FE320_G002_PLLCFG_READ( ) & ~( FE320_G002_PLLCFG_PLLREFSEL_MASK ) ) )

#define FE320_G002_PLL_SET_BYPASS( )        ( FE320_G002_PLLCFG_WRITE( FE320_G002_PLLCFG_READ( ) |    FE320_G002_PLLCFG_PLLBYPASS_MASK   ) )
#define FE320_G002_PLL_CLR_BYPASS( )        ( FE320_G002_PLLCFG_WRITE( FE320_G002_PLLCFG_READ( ) & ~( FE320_G002_PLLCFG_PLLBYPASS_MASK ) ) )

#define FE320_G002_PLL_IS_LOCKED( )         ( ( FE320_G002_PLLCFG_READ( ) & FE320_G002_PLLCFG_PLLLOCK_MASK ) >> FE320_G002_PLLCFG_PLLLOCK_SHIFT )

#define FE320_G002_PLL_PLLR_SET_DIVIDE_RATIO( x_ )      ( FE320_G002_PLL_WRITE_PLLR( ( x_ ) - 1uL ) )
#define FE320_G002_PLL_PLLF_SET_MULTIPLY_RATIO( x_ )    ( FE320_G002_PLL_WRITE_PLLF( ( ( x_ ) >> 1 ) - 1uL ) )
#define FE320_G002_PLL_PLLQ_SET_DIVIDE_RATIO( x_ )      ( FE320_G002_PLL_WRITE_PLLQ( ( x_ ) == 8uL ? ( 3uL ) : ( ( x_ ) / 2uL ) ) )

int main( void )
{   
    FE320_G002_PLL_SET_BYPASS( );                   /* PLL registers can be configured when pllbypass is set */
    FE320_G002_PLL_SET_REFSEL( );                   /* PLLREF = 16MHz (OSC) */
    FE320_G002_PLL_CLR_SEL( );                      /* Do not drive PLL output */
    FE320_G002_PLL_PLLR_SET_DIVIDE_RATIO( 2uL );    /* REFR   = 16MHz / 2  =   8MHz [6..12MHz] */
    FE320_G002_PLL_PLLF_SET_MULTIPLY_RATIO( 64uL ); /* VCO    =  8MHz * 64 = 512MHz [384..768MHz] */
    FE320_G002_PLL_PLLQ_SET_DIVIDE_RATIO( 8uL );    /* PLLOUT = 16MHz / 8  =  64MHz [48..384MHz] */
    FE320_G002_PLL_CLR_BYPASS( );                   /* Enable PLL */
    while( ! FE320_G002_PLL_IS_LOCKED( ) )
    {
        __asm( " NOP" );    /* Wait */
    }
    FE320_G002_PLL_SET_SEL( );                      /* Drive PLL output */
    /* CPU seems to run at 64MHz, I have checked this with the RTC */
}

This code has to be treated with caution as I’m not sure about the correct sequence (the manual is a bit vague).

Best regards,
Christian


#4

My experience was that it is a lot easier to just read the FE350-G002 specs and write the clock code in assembly, rather than try to figure out what the ‘metal’ library was trying to be clever about.

Here is the entireity of my clock speed setting code.

 # PRCI holds the CPU timing controls
.equ P_BASE, 0x10008000
.equ P_HFROSCCFG  , 0x00	# Ring osc config
.equ P_HFXOSCCFG  , 0x04	# XTAL osc config
.equ P_PLLCFG     , 0x08	# PLL config
.equ P_PLLDIV     , 0x0C
.equ P_PROCMONCFG , 0xF0

.equ PLLSEL, 1<<16	# PLL selected as clock source
.equ PLLREFSEL, 1<<17	# PLL to take XTAL as reference
.equ PLLBYPASS, 1<<18	# PLL bypassed and shut off

.text
# Initialize all timing functions.  We start out on the Ring oscillator
# but need to use the XTAL instead, through the Phase-locked Loop.	
.global	cinit
cinit:
# Make sure XTAL osc is stable so we can depend on it.
    li	t0, P_BASE
1:	lw	t1, P_HFXOSCCFG(t0)
    	bgtz	t1, 1b

# Set PLL configuration for 64 MHz but do not select it yet.
    li	t1, 0x20DF1
    sw	t1, P_PLLCFG(t0)
    # Wait for PLL to lock, indicated in sign bit
2:    lw	t1, P_PLLCFG(t0)
    bgtz	t1, 2b
    # Ok now to select the PLL as the clock source.
    li	t1, 0x30DF1
    sw	t1, P_PLLCFG(t0)
   ret

#5

Thanks, looks disgusting but I’ll give it a try. I’ll also have a look at the Rust lib, but I still think there should be a simple way to do this in C, since it’s the official language.


#6

Yeah I find C is harder to work with when it comes to this board than Rust. The rust guys did a great job writing nice and functional abstractions. I find metal completely useless and extremely hard to follow (seen the vtable mess?)


(Michael Scott) #7

Actually its quite simple (but just not properly documented). Ramping up from 32 to 320 MHz certainly makes a difference! See (line 143)

https://fossies.org/linux/wolfssl/IDE/ECLIPSE/SIFIVE/main.c

Mike


(Michael Scott) #8

Actually its quite simple (but just not properly documented). Ramping up from 32 to 320 MHz certainly makes a difference! See (line 143)

https://fossies.org/linux/wolfssl/IDE/ECLIPSE/SIFIVE/main.c

Mike


#9

To get 320 MHz working in C language, see my example here (Thanks to @LightBulb): https://github.com/kjarvel/hifive1revb_wifi/blob/master/cpu.c