RISC-V Project: Driving Outputs

Two RISC-V Development Boards

The RISC-V instruction set architecture (ISA) is rapidly gaining traction with electronics and robotics enthusiasts.  Thanks to new development kits like the SiFive Revision B board, future developers can get started with minimal resources and training.  Now it’s time to revisit the RISC-V development kits and try our hand at driving external components.

Figure 1: Two Development Kits for RISC-V Microcontrollers

The RISC-V name is short for “Reduced Instruction Set Computer Five.”  Without going into great historical detail, the project began as an open alternative to traditional ISAs.  As a result, there are numerous RISC-V devices in circulation created by universities, startup companies, and national laboratories.  This guide will focus on the FE310-G002 microcontroller created by SiFive, Inc.

Notice of Non-Affiliation and Disclaimer: As of the publication date, we are not affiliated with, associated with, authorized by, endorsed by, compensated by, or in any way officially connected with the RISC-V Foundation, SiFive, Sparkfun Electronics, ARM Holdings, Microchip Technology Inc., NXP, TSMC, or Microsoft, or their owners, subsidiaries or affiliates.  The names RISC-V, SiFive, PIC, AVR, Arm, Kinetis, Freedom Studio, Freedom E310 (FE310), Sparkfun Electronics, RedBoard, RED-V, ARM, and Microsoft, as well as related names, marks, emblems, and images are trademarks of their respective owners.

External Links: Links to external web pages have been provided as a convenience and for informational purposes only. Unboxing Tomorrow and Voxidyne Media bear no responsibility for the accuracy, legality or content of the external site or for that of subsequent links. Contact the external site for answers to questions regarding its content.

Objectives

Recently, I covered how to get started programming physical RISC-V devices using the Freedom Studio integrated development environment (IDE) and two development kits:

  • The Sparkfun DEV-15799 (RISC-V Thing Plus)
  • The Sparkfun DEV-15594 (RISC-V RedBoard)

Our electronics and robotics would not be especially useful without some way to toggle the individual pins on a controlled schedule.  So today, I will cover how to control the general-purpose input/output (GPIO) pins on these development kits.  We’ll also try out a simple way to generate time delays in the code.

  • Controlling the GPIO using the metal-gpio library
  • Generating Delays using the clock() function
  • Add code to show a red, green, and blue LED sequence
  • Upload the code via USB and SEGGER’s JLINK debugger

Material Requirements

The exact RISC-V chip I will use today is the FE310-G002 by SiFive, Inc.  This 32-bit chip is a feature of Sparkfun’s RED-V Thing Plus development board.  However, if you are okay with using a different form factor, you can also use the Sparkfun RED-V RedBoard or possibly the SiFive HiFive1 Rev. B.  All three of these options use the FE310-G002 chip.

  • HiFive1 Rev. B compatible board (I am using the DEV-15799)
  • USB-C cable
  • Red LED
  • Green LED
  • 1 kΩ resistors (2 total)
  • (Recommended) Anti-Static Workstation

Any LEDs you use for this should be the smaller, indication style of LED meant to draw less than 20 mA.  You should avoid very bright LEDs like the kind used in flashlights or vehicles unless you are using an external LED driver.

Software Requirements

Today, I will program the board using SiFive’s Freedom Studio IDE.  Before you install it, SiFive recommends reading the Freedom Studio User Manual.  My earlier installation guides for Windows and Ubuntu should help you get it running.

Important: Use 3.3-volt Logic

The GPIO pins on these development kits require 3.3-volt logic.  If you need to interface with 5-volt logic devices or higher voltages, you will need to use a level shifter.

Step 1: Create a New Project

Open the Freedom Studio IDE.

After you select your Workspace directory (I chose the default), create a blank Freedom E SDK project by navigating to:

File –> New –> Create a Freedom E SDK Project

Then create a blank project by selecting:

  • Target = sifive-hifive1-revb
  • Example Program = empty

This will give your project the default name of “sifive-hifive1-revb-empty.”  But at this point, you can optionally change the Project Name if you want to.

Figure 2: Creating an Empty Freedom E SDK Project

After you click “finish,” you will get the “Edit Configuration” window.  There is nothing to configure for now, so just click “close.”

Figure 3: Close the Edit Configuration Window

After a short delay, you will see the near-empty main.c file.  By default, it will only contain comments and the main() function…

/* Copyright 2019 SiFive, Inc */
/* SPDX-License-Identifier: Apache-2.0 */

int main() {
	return 0;
}

Step 2: Include Files

First, include the following 3 lines near the very top of the main.c file.  In this demo, metal/gpio.h is how we will control the pins.  And time.h was added for a clock function.

#include <stdio.h>      // Include Serial Library
#include <time.h>       // Time-related features
#include <metal/gpio.h> // SiFive’s GPIO library

Later, we will add a 1-second startup delay, and a dwell time between LED changes, so let’s define both of those delays as 1 million microseconds (µs or us), by typing the following just below the #include lines…

#define STARTUP_DELAY_US (1000000)
#define DWELL_TIME_US (1000000)

Step 3: Create a Delay Function

Sparkfun has demonstrated a way to generate approximate time delays using the time.h resource and the clock() function.

The clock() method function is vaguely like the millis() function in Arduino in that it is an uptime counter that increments without our intervention.  The main difference is clock() apparently counts at a higher rate of speed.

In my case, I’ve named the function delay_us() as a reminder that it accepts the microseconds unit.  Place this code just above the main() function.

void delay_us(int delayTime_us) {
    // Covert the requested time to multiples of 100 nanoseconds
    int requestedDelay_us_mul_10 = 10 * delayTime_us;

    // Store the start time
    clock_t startTime_us = clock();

    // Loop until the required time delay is achieved
    while( clock() < startTime_us + requestedDelay_us_mul_10);
}

Step 4: Choose Your GPIO

Now we need to define how we will use the pins of the development board.  In my case, I want:

  • One pin to blink the on-board blue LED
  • One pin to blink the red LED at pin 0
  • One pin to blink the green LED at pin 1

This isn’t quite as simple as it may sound because the silkscreen labels of the DEV-15799 and DEV-15594 boards do not necessarily indicate the GPIO numbering used by the Freedom E SDK system or the FE310-G002 chip itself.

For example, on the smaller DEV 15799, pin 0 on the silkscreen corresponds to IO 0.  However, on the larger DEV 15594, it will be IO 8.

Figure 4: The FE310-G002 Numbering System for IO is Different from the Pin Markings for the Connector

To get around this, I wrote my own pin definitions based on the schematics for the DEV-15799 and DEV-15594.  Just note that as of November 2020, these definitions are not yet peer-tested and may contain inaccuracies…

//---Pin Definitions for DEV-15799 (Experimental)
#define OUTPUT_DEV_15799_IO_0 (0)    // Label: "0"                 // (PWM 0-0)
#define OUTPUT_DEV_15799_IO_1 (1)    // Label: "1"                 // (PWM 0-1)
#define OUTPUT_DEV_15799_IO_2 (2)    // Label: "2"   (SPI 1 SS0)   // (PWM 0-2)
#define OUTPUT_DEV_15799_IO_SI (3)   // Label: "SI"  (SPI 1 MOSI)  // (PWM 0-3)
#define OUTPUT_DEV_15799_IO_SO (4)   // Label: "SO"  (SPI 1 MISO)  //
#define OUTPUT_DEV_15799_IO_SCK (5)  // Label: "SCK" (SPI 1 SCK)   // (Blue LED)
#define OUTPUT_DEV_15799_IO_9 (9)    // Label: "9"   (SPI 1 SS2)   //
#define OUTPUT_DEV_15799_IO_10 (10)  // Label: "10"  (SPI 1 SS3)   // (PWM 2-0)
#define OUTPUT_DEV_15799_IO_11 (11)  // Label: "11"  (SPI 1 SS*)   // (PWM 2-1)
#define OUTPUT_DEV_15799_IO_SDA (12) // Label: "SDA" (I2C 0 Data)  // (PWM 2-2)
#define OUTPUT_DEV_15799_IO_SCL (13) // Label: "SCL" (I2C 0 Clock) // (PWM 2-3)
#define OUTPUT_DEV_15799_IO_RXI (16) // Label: "RXI" (UART 0 In)   //
#define OUTPUT_DEV_15799_IO_TXO (17) // Label: "TXO" (UART 0 Out)  //
#define OUTPUT_DEV_15799_IO_18 (18)  // Label: "18"  (UART TXO)    //
#define OUTPUT_DEV_15799_IO_19 (19)  // Label: "19"                // (PWM 1-1)
#define OUTPUT_DEV_15799_IO_20 (20)  // Label: "20"                // (PWM 1-0)
#define OUTPUT_DEV_15799_IO_21 (21)  // Label: "21"                // (PWM 1-2)
#define OUTPUT_DEV_15799_IO_22 (22)  // Label: "22"                // (PWM 1-3)
#define OUTPUT_DEV_15799_IO_23 (23)  // Label: "23"  (UART RXI)    //
//----Pin Definitions for DEV-15594 (Experimental)
#define OUTPUT_DEV_15594_IO_RXI (17) // Label: "RXI" (UART 0 RXI)    //
#define OUTPUT_DEV_15594_IO_TXO (16) // Label: "TXO" (UART 0 TXO)    //
#define OUTPUT_DEV_15594_IO_2 (18)   // Label: "2"   (UART TXO)      //
#define OUTPUT_DEV_15594_IO_3 (19)   // Label: "~3"                  // (PWM 1-1)
#define OUTPUT_DEV_15594_IO_4 (20)   // Label: "4"                   // (PWM 1-0)
#define OUTPUT_DEV_15594_IO_5 (21)   // Label: "~5"                  // (PWM 1-2)
#define OUTPUT_DEV_15594_IO_6 (22)   // Label: "~6"                  // (PWM 1-3)
#define OUTPUT_DEV_15594_IO_7 (23)   // Label: "7"   (UART RXI)      //
#define OUTPUT_DEV_15594_IO_8 (0)    // Label: "8"                   // (PWM 0-0)
#define OUTPUT_DEV_15594_IO_9 (1)    // Label: "~9"                  // (PWM 0-1)
#define OUTPUT_DEV_15594_IO_10 (2)   // Label: "~10" (SPI 1-SS0)     // (PWM 0-2)
#define OUTPUT_DEV_15594_IO_11 (3)   // Label: "~11" (SPI 1 MOSI)    // (PWM 0-3)
#define OUTPUT_DEV_15594_IO_12 (4)   // Label: "12" (SPI 1 MISO)     //
#define OUTPUT_DEV_15594_IO_13 (5)   // Label: "13" (SPI 1 SCK)      // ( Blue LED)
#define OUTPUT_DEV_15594_IO_SDA (12) // Label: "~SDA/18" (I2C 0 SDA) // (PWM 2-2)
#define OUTPUT_DEV_15594_IO_SCL (13) // Label: "~SCL/19" (I2C 0 SCL) // (PWM 2-3)
#define OUTPUT_DEV_15594_IO_15 (9)   // Label: "15" (SPI 1 SS2)      // (PWM 2-0)
#define OUTPUT_DEV_15594_IO_16 (10)  // Label: "16" (SPI 1 SS3)      // (PWM 2-1)
#define OUTPUT_DEV_15594_IO_17 (11)  // Label: "~17" (SPI 1 SS3*)    // (PWM 2-2)
#define OUTPUT_DEV_15594_IO_18 (12)  // Is connected to: OUTPUT_DEV_15594_IO_SDA
#define OUTPUT_DEV_15594_IO_19 (13)  // Is connected to: OUTPUT_DEV_15594_IO_SCL

The anode of the on-board blue LED is physically wired to GPIO 5, which is also the “SCK” (serial clock) pin.  To use the blue LED, we will need to disable the serial clock function later.

In summary, my DEV-15799 board will have the following assignments…

  • The on-board blue LED will use OUTPUT_DEV_15799_IO_SCK
  • My external red LED will use OUTPUT_DEV_15799_IO_0
  • My external green LED will use OUTPUT_DEV_15799_IO_1

Had I used the larger DEV-15594 board, the assignments would have instead been…

  • On-board blue LED at OUTPUT_DEV_15594_IO_13
  • External red LED at OUTPUT_DEV_15594_IO_8
  • External green LED at OUTPUT_DEV_15594_IO_9

Step 5: Create a Metal-GPIO Interface

Back inside our own main() function, first I will give the power supply 1 second startup delay to stabilize before running any code.  Doing this is totally optional.

Second, use the printf() function to push a text message to the USB debugging interface.  This may help us debug later.

Third, we need to set up a GPIO device interface.  Do this using SiFive’s metal_gpio_get_device() function…

int main() {
    delay_us(STARTUP_DELAY_US);
    printf("[ OK ] Power-on reset\n");

    //---Bare Metal Library: GPIO Setup
    struct metal_gpio* gpio_device = metal_gpio_get_device(0);
    return 0;
} 

Note that even though we will use many GPIO pins later, we only need to create one metal_gpio interface.

Step 6: Set the Pin Directions

Next, we need to enable digital output mode for the three LED pins.

The blue LED will not illuminate outside of live debugging mode unless you disable its serial clock function within the pin multiplexer (pin mux).  So let’s disable it now using the metal_gpio_disable_pinmux() function!  For info on what the pin mux contains, see section “GPIO Multiplexed Outputs” of SiFive’s official FE310-G002 Preliminary Datasheet.

int main() {
    delay_us(STARTUP_DELAY_US);
    printf("[ OK ] Power-on reset\n");

    //---Bare Metal Library: GPIO Setup
    struct metal_gpio* gpio_device = metal_gpio_get_device(0);

    //---Initialize GPIO directions
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_0);    // Red LED 
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_1);    // Green LED
metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_SCK);  // Blue LED

metal_gpio_disable_pinmux(gpio_device, OUTPUT_DEV_15799_IO_SCK); // Disable SPI
return 0;
}

Step 7: Set the Pin States

Next, its time to blink the LEDs.  Here I will blink them in a Red –> Green –> Blue sequence that will have a 1-second dwell time between state changes.  For good measure, it will also print a message at the end of every loop so we can verify the program works as expected.

So my completed main() function will now be…

int main() {
    delay_us(STARTUP_DELAY_US);
    printf("[ OK ] Power-on reset\n");

    //---Bare Metal Library: GPIO Setup
    struct metal_gpio* gpio_device = metal_gpio_get_device(0);

    //---Initialize GPIO directions
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_0); // Red LED
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_1); // Green LED
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_SCK); // Blue LED
    metal_gpio_disable_pinmux(gpio_device, OUTPUT_DEV_15799_IO_SCK); // Disable SPI

    while(1)
    {
        metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_0, 1); // Red On
        delay_us(DWELL_TIME_US);
        metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_0, 0); // Red off
        metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_1, 1); // Green On
        delay_us(DWELL_TIME_US);
        metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_1, 0); // Green off
        metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_SCK, 1); // Blue On
        delay_us(DWELL_TIME_US);
        metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_SCK, 0); // Blue Off
        printf("[INFO] Cycle complete.\n");
    }

    return 0;
}

Step 8: Save and Build

This completes the programming portion.  By now, the entire main.c program looks like this…

#include <stdio.h>           // Include pin definitions
#include <time.h>            // Include time resources
#include <metal/gpio.h>      // Resource for General Purpose Input/Output (GPIO)

#define STARTUP_DELAY_US (1000000)
#define DWELL_TIME_US (1000000)

//---Pin Definitions for DEV-15799 (Experimental)
#define OUTPUT_DEV_15799_IO_0 (0)    // Label: "0"                 // (PWM 0-0)
#define OUTPUT_DEV_15799_IO_1 (1)    // Label: "1"                 // (PWM 0-1)
#define OUTPUT_DEV_15799_IO_2 (2)    // Label: "2"   (SPI 1 SS0)   // (PWM 0-2)
#define OUTPUT_DEV_15799_IO_SI (3)   // Label: "SI"  (SPI 1 MOSI)  // (PWM 0-3)
#define OUTPUT_DEV_15799_IO_SO (4)   // Label: "SO"  (SPI 1 MISO)  //
#define OUTPUT_DEV_15799_IO_SCK (5)  // Label: "SCK" (SPI 1 SCK)   // (Blue LED anode)
#define OUTPUT_DEV_15799_IO_9 (9)    // Label: "9"   (SPI 1 SS2)   //
#define OUTPUT_DEV_15799_IO_10 (10)  // Label: "10"  (SPI 1 SS3)   // (PWM 2-0)
#define OUTPUT_DEV_15799_IO_11 (11)  // Label: "11"  (SPI 1 SS*)   // (PWM 2-1)
#define OUTPUT_DEV_15799_IO_SDA (12) // Label: "SDA" (I2C 0 Data)  // (PWM 2-2)
#define OUTPUT_DEV_15799_IO_SCL (13) // Label: "SCL" (I2C 0 Clock) // (PWM 2-3)
#define OUTPUT_DEV_15799_IO_RXI (16) // Label: "RXI" (UART 0 In)   //
#define OUTPUT_DEV_15799_IO_TXO (17) // Label: "TXO" (UART 0 Out)  //
#define OUTPUT_DEV_15799_IO_18 (18)  // Label: "18"  (UART TXO)    //
#define OUTPUT_DEV_15799_IO_19 (19)  // Label: "19"                // (PWM 1-1)
#define OUTPUT_DEV_15799_IO_20 (20)  // Label: "20"                // (PWM 1-0)
#define OUTPUT_DEV_15799_IO_21 (21)  // Label: "21"                // (PWM 1-2)
#define OUTPUT_DEV_15799_IO_22 (22)  // Label: "22"                // (PWM 1-3)
#define OUTPUT_DEV_15799_IO_23 (23)  // Label: "23"  (UART RXI)    //
//----Pin Definitions for DEV-15594 (Experimental)
#define OUTPUT_DEV_15594_IO_RXI (17) // Label: "RXI" (UART 0 RXI)    //
#define OUTPUT_DEV_15594_IO_TXO (16) // Label: "TXO" (UART 0 TXO)    //
#define OUTPUT_DEV_15594_IO_2 (18)   // Label: "2"   (UART TXO)      //
#define OUTPUT_DEV_15594_IO_3 (19)   // Label: "~3"                  // (PWM 1-1)
#define OUTPUT_DEV_15594_IO_4 (20)   // Label: "4"                   // (PWM 1-0)
#define OUTPUT_DEV_15594_IO_5 (21)   // Label: "~5"                  // (PWM 1-2)
#define OUTPUT_DEV_15594_IO_6 (22)   // Label: "~6"                  // (PWM 1-3)
#define OUTPUT_DEV_15594_IO_7 (23)   // Label: "7"   (UART RXI)      //
#define OUTPUT_DEV_15594_IO_8 (0)    // Label: "8"                   // (PWM 0-0)
#define OUTPUT_DEV_15594_IO_9 (1)    // Label: "~9"                  // (PWM 0-1)
#define OUTPUT_DEV_15594_IO_10 (2)   // Label: "~10" (SPI 1-SS0)     // (PWM 0-2)
#define OUTPUT_DEV_15594_IO_11 (3)   // Label: "~11" (SPI 1 MOSI)    // (PWM 0-3)
#define OUTPUT_DEV_15594_IO_12 (4)   // Label: "12" (SPI 1 MISO)     //
#define OUTPUT_DEV_15594_IO_13 (5)   // Label: "13" (SPI 1 SCK)      // ( Blue LED anode)
#define OUTPUT_DEV_15594_IO_SDA (12) // Label: "~SDA/18" (I2C 0 SDA) // (PWM 2-2)
#define OUTPUT_DEV_15594_IO_SCL (13) // Label: "~SCL/19" (I2C 0 SCL) // (PWM 2-3)
#define OUTPUT_DEV_15594_IO_15 (9)   // Label: "15" (SPI 1 SS2)      // (PWM 2-0)
#define OUTPUT_DEV_15594_IO_16 (10)  // Label: "16" (SPI 1 SS3)      // (PWM 2-1)
#define OUTPUT_DEV_15594_IO_17 (11)  // Label: "~17" (SPI 1 SS3*)    // (PWM 2-2)
#define OUTPUT_DEV_15594_IO_18 (12)  // Is connected to: OUTPUT_DEV_15594_IO_SDA
#define OUTPUT_DEV_15594_IO_19 (13)  // Is connected to: OUTPUT_DEV_15594_IO_SCL


void delay_us(int delayTime_us) {
    // Convert the requested time to multiples of 100 nanoseconds
    int requestedDelay_us_mul_10 = 10 * delayTime_us;

    // Store the start time
    clock_t startTime_us = clock();

    // Loop until the required time delay is achieved
    while( clock() < startTime_us + requestedDelay_us_mul_10);
}


int main() {
    delay_us(STARTUP_DELAY_US);
    printf("[ OK ] Power-on reset\n");

    //---Bare Metal Library: GPIO Setup
    struct metal_gpio* gpio_device = metal_gpio_get_device(0);

    //---Initialize GPIO directions
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_0); // Red LED
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_1); // Green LED
    metal_gpio_enable_output(gpio_device, OUTPUT_DEV_15799_IO_SCK); // Blue LED
    metal_gpio_disable_pinmux(gpio_device, OUTPUT_DEV_15799_IO_SCK); // Disable SPI 

    while(1)
    {
    	metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_0, 1); // Red On
    	delay_us(DWELL_TIME_US);
    	metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_0, 0); // Red off
    	metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_1, 1); // Green On
    	delay_us(DWELL_TIME_US);
    	metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_1, 0); // Green off
    	metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_SCK, 1); // Blue On
    	delay_us(DWELL_TIME_US);
    	metal_gpio_set_pin(gpio_device, OUTPUT_DEV_15799_IO_SCK, 0); // Blue Off
    	printf("[INFO] Cycle complete.\n");
    }
    return 0;
}

Save.  Then build (compile) the project by clicking the “build” button (the hammer icon).  Look to the “Console” and “Problems” window for any errors.

Figure 5: The Freedom Studio Console showing a Successful Build

If you make any corrections, save afterward to apply changes; and then rebuild.

Step 9: Edit Run Configuration

It’s generally a good idea to reprogram or erase a microcontroller before you attach it to a new circuit.  So, we’ll do that now by connecting the RISC-V development board to your USB port and computer.

Go to the Project Explorer on the left side of the Freedom Studio IDE.  Then right-click the project name and navigate to:

Run –> Run Configurations …

In the Run Configuration window, select your project on the left side.  Then click “Run.”

Figure 6: Adding the Current Project Run Configuration

After a short delay, the GNU Debugger (GDB) server will start up, and your program will begin to execute on the RISC-V chip. 

From this point forward, you can re-upload using the “Run” button (▶) or the drop-down symbol next to it.  You can pause or terminate by pressing the ⏸️ or ⏹️ buttons.

Open the Freedom Studio terminal, and you should be able to see the device pushing our own “Cycle complete” text messages repeating in 3-second intervals.

To disconnect your RISC-V device from USB, remember to press the disconnect button, and then eject your RISC-V board like you would with any other USB mass storage device.

Step 10: Build the Circuit

Next, we need to build the circuit by attaching the red and green LEDs to the RISC-V, before adding current-limiting resistors to each one.

Figure 7: Schematic Representation of the Project

Side Note: Acceptable Resistor Values

The 1 kΩ resistors shown in Figure 7 above were selected to limit the output current (IOL and IOH).

According to the “Typical Electrical Specifications” of the FE310-G002 preliminary datasheet, there was no explicit absolute maximum current per GPIO pin.  So I assumed that the “typical values” of +21 mA to -21 mA per pin is the higher level I should allow.  The 21 mA figure is similar to the max allowable currents of popular microcontrollers like the PIC series, AVR series, Arm devices, and so on, so we’ll make sure each LED draws less than 21 mA or even 20 mA.

Likewise, the total current for all I/O pins combined should not exceed a certain amount.  Looking again to the same datasheet, this wasn’t explicitly called out, but we can at least reference the VDD Supply Current (IVDD) specification of 150 mA.

For this, we can select a range of current-limiting resistors using Ohm’s Law:

Where:

  • VDDIO is 3.3 volts (as shown in Sparkfun’s schematic for the RedFive board)
  • Vf is the expected forward voltage of your LED (in my case: 1.8 v for red, 2.2 v for green)
  • If is the desired forward current of your LED (in my case: 2 mA for red and green)

For my LEDs, this yielded Rlimiter of greater than 750 Ω for the red LED, and 550 Ω or greater for the green.  The common 1 kΩ resistor meets the requirement for both, so I will use two 1 kΩ resistors.

Step 11: Apply Power

Now that you have the LEDs and resistors connected, power on the module.  If things are set up correctly, then you should see the LEDs blink in sequence!

Step 12: Adjust the Delay

After watching the red –> green –> blue sequence and the “cycle complete” messages in the debugging terminal, it was clear that the delays created by delay_us() were about half the intended duration.

This is something I hope to cover in more detail in the future, but for now the simplest adjustment was to double the values of STARTUP_DELAY_US and DWELL_TIME_US.

Closing Remarks

So far, this was a good starting point.  And hopefully, I can get a few new projects that make full use of the microcontroller’s versatile clock and instructions.

Use an External Driver if you Need More Current

Like any other microcontroller or microprocessor, the FE310-G002 chip can only carry so much electric current through its pins without sustaining damage.  If you need to drive larger loads such as a motor or large LED, consider using a low-side driver, high-side driver, or other external assistance.  These are topics I hope to cover very soon, so stay posted to see them used with the FE310-G002.

Save to Apply Changes

Apparently, my copy of Freedom Studio does not apply changes and recompile when you press the “build” button (the hammer icon), or when you select “Build Project” in the project explorer.  This led to the compiler pushing errors and warning messages after the problem was apparently fixed.

Accuracy Limits of the Delay Function

When using the clock() function, keep in mind that it is only as accurate as the system clock.  If you need higher accuracy, consider using a real-time clock (RTC), real-time clock and calendar (RTCC) chip, or external time sources.

More About the Metal Library

The metal library provides hardware abstraction for all standard and custom SiFive RISC-V core intellectual properties and simulation tools.  Beyond what we saw today, that includes a real-time clock (RTC), pulse width modulation (PWM), serial peripheral interfaces (SPI), and other common embedded peripherals.  For documentation, check out SiFive’s Freedom Metal GitHub repository.

This guide was made for the FE310-G002, which is the second revision of the Freedom E310 family.  The chip itself was made using the Taiwan Semiconductor Manufacturing Company (TSMC) 180 nm process.  These days, you can still find many of the older FE310-G00x revisions and related kits, so be sure to check those out and configure your Freedom Studio setup accordingly.

References

[1]RISC-V Foundation, “Members – RISC-V International,” 23 Oct. 2020. [Online]. Available: https://riscv.org/members/. [Accessed 22 Nov. 2020].
[2]B. R. Mayes, “Unboxing-Tomorrow.com,” 20 Feb. 2020. [Online]. Available: https://unboxing-tomorrow.com/risc-v-development-for-linux-users/. [Accessed 24 Nov. 2020].
[3]SiFive, Inc., “SiFive E31 Manual 20G1.03.00,” 13 June 2020. [Online]. Available: https://sifive.cdn.prismic.io/sifive/416844a3-1580-48a1-a70f-5b637c217819_sifive_E31_rtl_full_20G1.03.00_manual.pdf. [Accessed 22 Nov. 2020].
[4]B. Mayes, “Unboxing-Tomorrow.com,” 15 Dec. 2019. [Online]. Available: https://unboxing-tomorrow.com/unboxing-the-sparkfun-risc-v-redboard/. [Accessed 24 Nov. 2020].
[5]bboyho, “sifive_Hifive1blink.c,” bboyho, 22 Nov. 2019. [Online]. Available: https://gist.github.com/bboyho/34aa001a8df8429d0cdddcbf80ae5be9. [Accessed 10 Nov. 2020].
[6]“SiFive FE310-G002 Preliminary Datasheet, v1p0,” 12 Apr. 2019. [Online]. Available: https://cdn.sparkfun.com/assets/5/b/e/6/2/fe310-g002-ds.pdf. [Accessed 22 Nov. 2020].
[7]Sparkfun, “RedFive Datasheet,” 10 Dec. 2019. [Online]. Available: https://cdn.sparkfun.com/assets/d/d/1/e/7/RedFive.pdf. [Accessed 22 Nov. 2020].
[8]SiFive, Inc., “Freedom Metal; Freedom Metal v20.05.01.00 documentation,” [Online]. Available: https://sifive.github.io/freedom-metal-docs/. [Accessed 22 Nov. 2020].
[9]SiFive, Inc., “SiFive FE310-G002 Manual v19p04,” SiFive, Inc., 11 Apr. 2019. [Online]. Available: https://sifive.cdn.prismic.io/sifive%2F9ecbb623-7c7f-4acc-966f-9bb10ecdb62e_fe310-g002.pdf. [Accessed 10 Nov. 2020].

Important Notice: This article and its contents (the “Information”) belong to Unboxing-tomorrow.com and Voxidyne Media LLC. No license is granted for the use of it other than for information purposes. No license of any intellectual property rights is granted.  The Information is subject to change without notice. The Information supplied is believed to be accurate, but Voxidyne Media LLC assumes no responsibility for its accuracy or completeness, any error in or omission from it or for any use made of it.  Liability for loss or damage resulting from any reliance on the Information or use of it (including liability resulting from negligence or where Voxidyne Media LLC was aware of the possibility of such loss or damage arising) is excluded.