User:Vazhnov/PlatformIO + VIM + ccls + Sipeed Longan nano RISC-V

From Gentoo Wiki
Jump to:navigation Jump to:search
Note
Even though this page is in the user namespace, corrections and additions are much appreciated! This is simply wiki policy, this page can be moved to the main wiki as soon as it achieves critical mass more.

Intro

I'm not a developer.

For my job, I have to edit some shell and Python scripts. I use Vim + vim-syntastic + linters (pylint, shellcheck).

As a hobby, I'm trying to learn how to program in Asm and C for architecture RISC-V.

PlatformIO

PlatformIO is a popular platform with good support of multiple boards (like Arduino, STM32, …).

PlatformIO install

PlatformIO can be installed by Python's package management system pip. Here is an example how to install PlatformIO with Python virtualenv venv, to not interfere with your system:

user $python3 -m venv --system-site-packages /tmp/venv_platformio
user $source /tmp/venv_platformio/bin/activate
user $pip3 install platformio

Disable telemetry, if you want:

user $pio settings set enable_telemetry No
user $pio settings get

Test:

user $pio boards
Platform: gd32v
============================================================================================
ID                       MCU            Frequency    Flash    RAM    Name
-----------------------  -------------  -----------  -------  -----  -----------------------
gd32vf103v-eval          GD32VF103VBT6  108MHz       128KB    32KB   GD32VF103V-EVAL
sipeed-longan-nano       GD32VF103CBT6  108MHz       128KB    32KB   Sipeed Longan Nano
sipeed-longan-nano-lite  GD32VF103C8T6  108MHz       64KB     20KB   Sipeed Longan Nano Lite
wio_lite_risc-v          GD32VF103CBT6  108MHz       128KB    32KB   Wio Lite RISC-V

PlatformIO init

user $mkdir -pv /tmp/longan-sandbox && cd /tmp/longan-sandbox
user $pio project init --ide vim --board sipeed-longan-nano

When run at the first time, PlatformIO downloads:

After the last command, pio creates an empty structure + file .ccls which is important for a syntax checker(s):

FILE .ccls
clang

%c -std=gnu11 -Os -Wall -march=rv32imac -mabi=ilp32 -mcmodel=medlow -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fno-common
%cpp -std=gnu++17 -Os -Wall -march=rv32imac -mabi=ilp32 -mcmodel=medlow -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fno-common

-I/tmp/longan-sandbox/include
-I/tmp/longan-sandbox/src
-I/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_standard_peripheral
-I/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_standard_peripheral/Include
-I/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_usbfs_driver
-I/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_usbfs_driver/Include
-I/home/user/.platformio/packages/framework-gd32vf103-sdk/RISCV/drivers
-I/home/user/.platformio/packages/framework-gd32vf103-sdk/RISCV/env_Eclipse
-I/home/user/.platformio/packages/framework-gd32vf103-sdk/RISCV/stubs
-I/home/user/.platformio/packages/toolchain-gd32v/riscv-nuclei-elf/include/c++/9.2.0
-I/home/user/.platformio/packages/toolchain-gd32v/riscv-nuclei-elf/include/c++/9.2.0/riscv-nuclei-elf
-I/home/user/.platformio/packages/toolchain-gd32v/lib/gcc/riscv-nuclei-elf/9.2.0/include
-I/home/user/.platformio/packages/toolchain-gd32v/lib/gcc/riscv-nuclei-elf/9.2.0/include-fixed
-I/home/user/.platformio/packages/toolchain-gd32v/riscv-nuclei-elf/include

-DPLATFORMIO=50203
-DUSE_STDPERIPH_DRIVER
-DHXTAL_VALUE=8000000U

PlatformIO integration with different IDE

Inside a directory with source code, it is possible to create a file structure to help your favourite IDE to catch settings.

user $pio project init --ide [atom|clion|codeblocks|eclipse|emacs|netbeans|qtcreator|sublimetext|vim|visualstudio|vscode]

In common, PlatformIO creates such structure:

user $tree -a
.
├── .gitignore
├── include
│   └── README
├── lib
│   └── README
├── .pio
│   └── build
│       ├── project.checksum
│       └── sipeed-longan-nano
│           └── idedata.json
├── platformio.ini
├── src
└── test
    └── README

7 directories, 7 files

Differences described below:

  • --ide atom: creates configuration files .clang_complete and .gcc-flags.json,
  • --ide clion: creates configuration files CMakeLists.txt and CMakeListsPrivate.txt,
  • --ide codeblocks: creates configuration file platformio.cbp (XML),
  • --ide eclipse: creates configuration files .cproject and .project (both are XML) and a directory .settings/,
  • --ide emacs: creates a configuration file .ccls with clang options,
  • --ide netbeans: creates a configuration files nbproject/configurations.xml and nbproject/project.xml,
  • --ide qtcreator: creates a configuration file platformio.pro,
  • --ide sublimetext: creates a configuration files .ccls and platformio.sublime-project (JSON),
  • --ide vim: creates a configuration file .ccls with clang options,
  • --ide visualstudio: creates a configuration files platformio.vcxproj and platformio.vcxproj.filters (both are XML),
  • --ide vscode: creates a configuration files .vscode/c_cpp_properties.json, .vscode/extensions.json and .vscode/launch.json,

vim

When using Vim + vim-syntastic, it is hard to work with C/C++ code because of errors like

/usr/lib/gcc/riscv64-linux-gnu/11/include/stdint.h|9 col 16 error| fatal error: stdint.h: No such file or directory [c/gcc]

Solution: use language server + LSP client. As an option, use language server dev-util/ccls + Native LSP client from app-editors/neovim.

Assembly

Attempt to open RISC-V assembly file with Vim will result with a bunch of errors:

hello.s|4 error| Error: no such instruction: `li s1,0x10000000' [asm/gcc]
hello.s|5 error| Error: no such instruction: `la s2,message' [asm/gcc]
hello.s|6 error| Error: no such instruction: `addi s3,s2,14' [asm/gcc]
hello.s|8 error| Error: no such instruction: `lb s4,0(s2)' [asm/gcc]
hello.s|9 error| Error: no such instruction: `sb s4,0(s1)' [asm/gcc]

Because vim-syntastic tries to check syntax with system gcc, which doesn't know about RISC-V commands.

Put one of examples into any vimrc file:

FILE ~/.config/nvim/filetype.vim
augroup AsmRISCV
  autocmd FileType asm
   \ let g:syntastic_asm_generic = 1 |
   \ let b:syntastic_asm_gcc_exec = '~/.platformio/packages/toolchain-gd32v/bin/riscv-nuclei-elf-gcc'
augroup END
FILE ~/.config/nvim/filetype.vim
augroup AsmRISCV
  autocmd FileType asm
    \ if stridx(expand("%:path"), $HOME.'/path/to/your/asm/project/') == 0 |
    \   let g:syntastic_asm_generic = 1 |
    \   let b:syntastic_asm_gcc_exec = '~/.platformio/packages/toolchain-gd32v/bin/riscv-nuclei-elf-gcc' |
    \ endif
augroup END

ccls

dev-util/ccls is required by Neovim to check code syntax/style. It is still in "testing" state in Gentoo Portage, so:

root #echo "dev-util/ccls ~amd64" >> /etc/portage/package.accept_keywords
root #emerge --ask dev-util/ccls

Check:

user $ccls --version
ccls version 0.20210330
clang version 13.0.0

Neovim

app-editors/neovim v0.5 supports the Language Server Protocol (LSP), which is needed, so testing versions should be allowed:

root #echo "app-editors/neovim ~amd64" >> /etc/portage/package.accept_keywords

Install Neovim and Vim plug as it described in Neovim.

Create a configuration file:

FILE ~/.config/nvim/init.vim
call plug#begin('~/.local/share/nvim/site/plugged')
Plug 'vim-syntastic/syntastic'
Plug 'neovim/nvim-lspconfig'
call plug#end()

Run nvim, execute:

:PlugInstall
:UpdateRemotePlugins
:q

Then add a code from https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#ccls at the end of the configuration file:

FILE ~/.config/nvim/init.vim
local lspconfig = require'lspconfig'
lspconfig.ccls.setup {
  init_options = {
    compilationDatabaseDirectory = "build";
    index = {
      threads = 0;
    };
    clang = {
      excludeArgs = { "-frounding-math"} ;
    };
  }
}

udev rules

Create an udev configuration file:

FILE /etc/udev/rules.d/99-platformio-udev.rules
# GD32V DFU Bootloader
ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

Or follow the official documentation: 99-platformio-udev.rules.

dfu-util

Note
PlatformIO uses its own binary from ~/.platformio/packages/tool-dfuutil/, so the step below is not mandatory.

app-mobilephone/dfu-util — device firmware update (DFU) USB programmer.

root #emerge --ask app-mobilephone/dfu-util

Check:

Boot your Sipeed Longan nano board with "Boot0" button pressed.

user $dfu-util -l
dfu-util 0.11

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2021 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Found DFU: [28e9:0189] ver=1000, devnum=2, cfg=1, intf=0, path="5-1", alt=1, name="@Option Bytes  /0x1FFFF800/01*016 g", serial="3CBJ"
Found DFU: [28e9:0189] ver=1000, devnum=2, cfg=1, intf=0, path="5-1", alt=0, name="@Internal Flash  /0x08000000/512*002Kg", serial="3CBJ"

QEMU

TBD.

Let's play with assembler without a real hardware:

FILE hello_poweroff_qemu.s
# Based on https://git.sr.ht/~matthias_t/hello-riscv/tree/master/item/src/hello.s

.section .init
.global _start
_start:
    li s1, 0x10000000 # s1 := 0x1000_0000
    la s2, message    # s2 := <message>
    addi s3, s2, 14   # s3 := s2 + 14
L1:
    lb s4, 0(s2)      # s4 := (s2)
    sb s4, 0(s1)      # (s1) := s4
    addi s2, s2, 1    # s2 := s2 + 1
    blt s2, s3, L1b   # if s2 < s3, branch back to L1

    # Exit from QEMU, see QEMU sources: `hw/misc/sifive_test.c`, `include/hw/misc/sifive_test.h`.
    # Based on https://github.com/rtfb/riscv64-in-qemu/blob/master/src/baremetal-poweroff.s
    .equ FINISHER_FAIL,  0x3333
    .equ FINISHER_PASS,  0x5555
    .equ FINISHER_RESET, 0x7777
    li t0, FINISHER_PASS
    li t1, 0x100000
    sw t0, 0(t1)

# If QEMU exit doesn't work:
# Infinitive wait for interrupt — good to not heat up CPU
L2:
    wfi
    j L2b

.section .data
message:
  .string "Hello, world!\n"

Test project: gd32v-lcd

Prepare a test project:

user $git clone -b dev 'git@github.com:vazhnov/gd32v-lcd.git'
user $cd gd32v-lcd
user $pio project init --ide vim --board sipeed-longan-nano

Run neovim:

user $nvim -n -- src/main.c

Execute:

:LspInfo

You should see:

Language client log: /home/user/.cache/nvim/lsp.log
Detected filetype:   c


Client: ccls (id: 1, pid: 5947, bufnr: [1])
 filetypes:       c, cpp, objc, objcpp
 autostart:       true
 root directory:  /tmp/longan-sandbox/gd32v-lcd
 cmd:             ccls

Configured servers list: ccls

Compile project with PlatformIO:

user $pio run
Processing sipeed-longan-nano (platform: gd32v; framework: gd32vf103-sdk; board: sipeed-longan-nano)
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/gd32v/sipeed-longan-nano.html
PLATFORM: GigaDevice GD32V (1.2.1) > Sipeed Longan Nano
HARDWARE: GD32VF103CBT6 108MHz, 32KB RAM, 128KB Flash
DEBUG: Current (altera-usb-blaster) External (altera-usb-blaster, gd-link, jlink, rv-link, sipeed-rv-debugger, um232h)
PACKAGES:
 - framework-gd32vf103-sdk 1.0.0
 - tool-dfuutil 1.9.200310
 - toolchain-gd32v 9.2.0
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Compiling .pio/build/sipeed-longan-nano/src/lcd.o
Compiling .pio/build/sipeed-longan-nano/src/led.o
Compiling .pio/build/sipeed-longan-nano/src/main.o
Compiling .pio/build/sipeed-longan-nano/src/systick.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_adc.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_bkp.o
src/lcd.c: In function 'lcd_init':
src/lcd.c:176:5: warning: implicit declaration of function 'delay_1ms' [-Wimplicit-function-declaration]
  176 |     delay_1ms(1);
      |     ^~~~~~~~~
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_can.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_crc.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_dac.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_dbg.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_dma.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_eclic.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_exmc.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_exti.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_fmc.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_fwdgt.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_gpio.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_i2c.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_pmu.o
/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_standard_peripheral/Source/gd32vf103_i2c.c: In function 'i2c_flag_clear':
/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_standard_peripheral/Source/gd32vf103_i2c.c:610:14: warning: variable 'temp' set but not used [-Wunused-but-set-variable]
  610 |     uint32_t temp;
      |              ^~~~
/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_standard_peripheral/Source/gd32vf103_i2c.c: In function 'i2c_interrupt_flag_clear':
/home/user/.platformio/packages/framework-gd32vf103-sdk/GD32VF103_standard_peripheral/Source/gd32vf103_i2c.c:718:14: warning: variable 'temp' set but not used [-Wunused-but-set-variable]
  718 |     uint32_t temp;
      |              ^~~~
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_rcu.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_rtc.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_spi.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_timer.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_usart.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/Source/gd32vf103_wwdgt.o
Compiling .pio/build/sipeed-longan-nano/standard_peripheral/system_gd32vf103.o
Compiling .pio/build/sipeed-longan-nano/RISCV/drivers/n200_func.o
Compiling .pio/build/sipeed-longan-nano/RISCV/env_Eclipse/entry.o
Compiling .pio/build/sipeed-longan-nano/RISCV/env_Eclipse/handlers.o
Compiling .pio/build/sipeed-longan-nano/RISCV/env_Eclipse/init.o
Compiling .pio/build/sipeed-longan-nano/RISCV/env_Eclipse/start.o
Compiling .pio/build/sipeed-longan-nano/RISCV/env_Eclipse/your_printf.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/_exit.o
/home/user/.platformio/packages/framework-gd32vf103-sdk/RISCV/env_Eclipse/your_printf.c: In function '__wrap_printf':
/home/user/.platformio/packages/framework-gd32vf103-sdk/RISCV/env_Eclipse/your_printf.c:4:1: warning: control reaches end of non-void function [-Wreturn-type]
    4 | }
      | ^
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/close.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/fstat.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/isatty.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/lseek.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/read.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/sbrk.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/write.o
Compiling .pio/build/sipeed-longan-nano/RISCV/stubs/write_hex.o
Archiving .pio/build/sipeed-longan-nano/libRISCV.a
Indexing .pio/build/sipeed-longan-nano/libRISCV.a
Archiving .pio/build/sipeed-longan-nano/libstandard_peripheral.a
Indexing .pio/build/sipeed-longan-nano/libstandard_peripheral.a
Linking .pio/build/sipeed-longan-nano/firmware.elf
Building .pio/build/sipeed-longan-nano/firmware.bin
Checking size .pio/build/sipeed-longan-nano/firmware.elf
Adding dfu suffix to firmware.bin
Building .pio/build/sipeed-longan-nano/firmware.hex
dfu-suffix (dfu-util) 0.9

Copyright 2011-2012 Stefan Schmidt, 2013-2014 Tormod Volden
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Suffix successfully added to file
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [========= ]  85.2% (used 27926 bytes from 32768 bytes)
Flash: [=         ]   7.1% (used 9338 bytes from 131072 bytes)
================================================================= [SUCCESS] Took 2.01 seconds =================================================================

Flash the SoC (will also download and install ~/.platformio/packages/tool-openocd-gd32v):

user $pio run --target upload
Processing sipeed-longan-nano (platform: gd32v; framework: gd32vf103-sdk; board: sipeed-longan-nano)
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/gd32v/sipeed-longan-nano.html
PLATFORM: GigaDevice GD32V (1.2.1) > Sipeed Longan Nano
HARDWARE: GD32VF103CBT6 108MHz, 32KB RAM, 128KB Flash
DEBUG: Current (altera-usb-blaster) External (altera-usb-blaster, gd-link, jlink, rv-link, sipeed-rv-debugger, um232h)
PACKAGES:
 - framework-gd32vf103-sdk 1.0.0
 - tool-dfuutil 1.9.200310
 - tool-gd32vflash 0.1.0
 - tool-openocd-gd32v 0.1.1
 - toolchain-gd32v 9.2.0
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Checking size .pio/build/sipeed-longan-nano/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [========= ]  85.2% (used 27926 bytes from 32768 bytes)
Flash: [=         ]   7.1% (used 9338 bytes from 131072 bytes)
Configuring upload protocol...
AVAILABLE: altera-usb-blaster, dfu, gd-link, jlink, rv-link, serial, sipeed-rv-debugger, um232h
CURRENT: upload_protocol = dfu
Uploading .pio/build/sipeed-longan-nano/firmware.bin
dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2020 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Opening DFU capable USB device...
ID 28e9:0189
Run-time device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Setting #0 ...
Determining device status: state = dfuERROR, status = 10
dfuERROR, clearing status
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 011a
Device returned transfer size 2048
DfuSe interface name: "Internal Flash  "
Found GD32VF103, which reports a bad page size and count for its internal memory.
Fixed layout based on part number: page size 1024, count 128.
Downloading to address = 0x08000000, size = 9344

Erase   	[                         ]   0%            0 bytes
Erase   	[=====                    ]  21%         2048 bytes
Erase   	[==========               ]  43%         4096 bytes
Erase   	[================         ]  65%         6144 bytes
Erase   	[=====================    ]  87%         8192 bytes
Download	[                         ]   0%            0 bytes
Download	[=====                    ]  21%         2048 bytes
Download	[==========               ]  43%         4096 bytes
Download	[================         ]  65%         6144 bytes
Download	[=====================    ]  87%         8192 bytes
Download	[=========================] 100%         9344 bytes
Download done.
File downloaded successfully
dfu-util: dfuse_download: libusb_control_transfer returned -4
*** [upload] Error 74
================================================================= [FAILED] Took 3.21 seconds =================================================================

(error "dfuse_download: libusb_control_transfer returned -4" is OK).

Board will restart automatically with a new code, and you will see an bouncing amiga ball.

Dig inside

PlatformIO uses ~/.platformio/packages/toolchain-gd32v/bin/riscv-nuclei-elf-gcc to compile the code.

It is possible to pass additional options to the GCC with build_flags option of platformio.ini in your project. For example, lets keep all assembly source files after compilation:

FILE platformio.ini
[env:sipeed-longan-nano]
platform = gd32v
framework = gd32vf103-sdk
board = sipeed-longan-nano
monitor_speed = 115200
upload_protocol = dfu
build_flags =
  -save-temps
  -fverbose-asm

Also, let's create a disassemble of whole binary firmware:

user $~/.platformio/packages/toolchain-gd32v/bin/riscv-nuclei-elf-objdump -D -S --wide .pio/build/sipeed-longan-nano/firmware.elf > firmware.lss

See also

  • Arduino — how to prepare the toolchain, how to use Eclipse IDE;
  • RISC-V Multilib — but rv32i part is not written yet;
  • RISC-V ABIs — describe their properties: pointer sizes, compatibility, gcc flags, default linker path.

External links