Five EmbedDev logo Five EmbedDev

An Embedded RISC-V Blog

Code Samples

These code samples relate to this article:

Note, this page is based on my article on Medium.

Cross Compiling to Bare Metal RISC-V with PlatformIO/Scons.

Platform IO is a development environment for embedded systems. It is built with Python and uses Scons as a build tool. Scons is itself a DSL built on Python. It is integrated with VSCode and this makes it a nice vendor independent and device independent embedded IDE.

Platform IO can give you RISC-V board support out of the box, what I’d like to cover here is:

Choice of Board

To setup up a new project in Platform IO just select the board, framework and path for the files. For this exercise the choices are HiFive1 Rev B, Freedom E SDK and a custom path.

Customizing the Build Flags

Customization is done via the platformio.ini file in the project top level directory. A build_flags parameter has been added with options to target for embedded C++17. In particular:

The configuration build_flags captures compiler, pre-processor and linker options, so -Wl,-Map,blinky.map to generate a map file can be added here along side compiler options.

An example of build flags is:

build_flags = 
    -std=c++17
    -O2
    -g
    -Wall 
    -ffunction-sections 
    -fno-exceptions 
    -fno-rtti 
    -fno-nonansi-builtins 
    -fno-use-cxa-atexit 
    -fno-threadsafe-statics
    -nostartfiles 
    -Wl,-Map,blinky.map

Adding a Post Compile Action

To add post compile options we need to edit platform.ini again, this time adding a helper script and additional targets.

extra_scripts = post_build.py

targets = disasm

The build system behind Platform IO is Scons. This is a Python based system, so the post build script is a python file.

The Scons environment is used to find the path to the output file, ${BUILD_DIR}/${PROGNAME}.elf, and the toolchain objdump is found by modifying the path to ${OBJDUMP}. The env.subst() will expand the environment variables, and env.Execute() will run the command.

def after_build(source, target, env): 
    """ Run objdump on the target elf file and save the output in the top dir.
    """
    objdump=env.subst("${OBJCOPY}").replace("objcopy","objdump")
    src_elf=env.subst("${BUILD_DIR}/${PROGNAME}.elf")
    cmd=" ".join([
        objdump, "-SC","--file-start-context", "-w",
        src_elf,">","${PROGNAME}.disasm"]) # 
    env.Execute(cmd)

The target is given a name disasm via the env.AddCustomTarget() method. This ties into the platformio.ini option targets = disasm above.

Import("env")

env.AddCustomTarget(
    "disasm",
    "${BUILD_DIR}/${PROGNAME}.elf",
    after_build,
    title="Disasm 2",
    description="Generate a disassembly file on demand",
    always_build=True
)

Adding custom compilers

TODO

Adding custom targets/boards

TODO