将 Arduino IDE 移植到 RISC-V 64

June 09, 2023 · 14 min read

建议 Tips

您正在查看印刷版本的博客, 印刷版本的博客可能会缺少部分交互功能, 部分内容显示不全或过时. 如果您在查看该印刷版博客时遇到了任何问题, 欢迎来此链接查看在线版: https://www.kxxt.dev/blog/porting-arduino-ide-to-riscv64/

You are viewing a printed version of this blog. It may lack some interactive features and some content might now be fully displayed or outdated. If you encounter any problems while viewing this printed version of the blog, feel free to view the online version here: https://www.kxxt.dev/blog/porting-arduino-ide-to-riscv64/

Arch Linux RISC-V 实习期间,我尝试将 第一代 Arduino IDE 移植到 RISC-V 64 上。这篇文章记录了我在移植过程中遇到的问题和解决方案。

首先, 我们来看一下 Arch Linux 怎么给 Arduino 写的 PKGBUILD (下面为节选):

呦,这也太不清真了吧。有两个共享库是直接从网上拉下来的,完全没有从源码编译。不过从注释来看显然这个包的维护者也意识到了这个问题。

PKGBUILD(part)

source=("${pkgname}-${pkgver}.tar.xz::https://github.com/arduino/Arduino/releases/download/${pkgver}/arduino-${pkgver}.tar.xz"
"${pkgname}-${pkgver}.tar.xz.asc::https://github.com/arduino/Arduino/releases/download/${pkgver}/arduino-${pkgver}.tar.xz.asc"
# GPG signatures are not required as zip shasum is already provided by the buildfile
# https://github.com/arduino/Arduino/issues/11522#issuecomment-840135044
"https://github.com/arduino-libraries/WiFi101-FirmwareUpdater-Plugin/releases/download/v0.12.0/WiFi101-Updater-ArduinoIDE-Plugin-0.12.0.zip"
"arduino-examples-1.9.1.zip::https://github.com/arduino/arduino-examples/archive/refs/tags/1.9.1.zip"
"https://downloads.arduino.cc/libastylej-2.05.1-5.zip"
"https://downloads.arduino.cc/libastylej-2.05.1-5.zip.asc"
"https://downloads.arduino.cc/liblistSerials/liblistSerials-1.4.2-2.zip"
"https://downloads.arduino.cc/liblistSerials/liblistSerials-1.4.2-2.zip.asc"
"arduino.sh")
package() {
cd "arduino-${pkgver}/build/linux/work"
# Create directories
install -dm755 "${pkgdir}/usr/share/"{doc,icons/hicolor,applications,mime/packages}
# Copy the whole SDK
cp -a . "${pkgdir}/usr/share/arduino"

整体上,感觉把 Arduino IDE 移植到 RISC-V 64 应该是挺简单的,毕竟它主要是用跨平台的 Java 语言写的。

那么, 我们再去 Arduino IDE 的 GitHub 仓库 看看,有没有什么跟平台/架构绑死的代码。

这一看可不得了,这仓库比刚才的 PKGBUILD 还不清真,这个仓库里存了一大堆预先编译好的二进制文件的压缩文件的 checksum, 然后在构建的时候直接从网上拉下来,解压缩。

这里面并没有给 riscv64 的预构建文件,所以我们通通需要自己编译。

image-20230609115845641

不过幸运的是,有一些组件已经被 Arch Linux 拆出来单独打包了,比如 avr-gcc, arduino-builder, arduino-avr-core 等等.

所以我们只需要自己编译一些没有被拆出来的组件就好了。

编译两个共享库

从表面上来看,只有两个共享库需要编译。

listSerialPortsC

首先是 listSerialPortsC 这个库.

没有什么需要改的,把 compile_linux.sh 里的命令稍微改一改就好了。因为是直接在 riscv64 上编译,所以不需要交叉编译。


git clone https://github.com/arduino/listSerialPortsC
cd listSerialPortsC/
git submodule update --init
cd libserialport
./autogen.sh
./configure
make clean
make
cd ..
JAVA_INCLUDE_PATH=/usr/lib/jvm/java-8-openjdk/include
gcc main.c libserialport/linux_termios.c libserialport/linux.c libserialport/serialport.c -Ilibserialport/ -o listSerialC
gcc jnilib.c libserialport/linux_termios.c libserialport/linux.c libserialport/serialport.c -Ilibserialport/ -I$JAVA_INCLUDE_PATH -I$JAVA_INCLUDE_PATH/linux/ -shared -fPIC -o liblistSerialsj.so

astyle

然后是 astyle, 这个库是用来格式化代码的. Arduino 维护了一个仓库 来存放他们的构建脚本和 patches. 对于 riscv64 来说,基本没有什么需要改的。


git clone https://github.com/arduino/astyle && cd astyle
svn co svn://svn.code.sf.net/p/astyle/code/tags/2.05.1 astyle-code && cd astyle-code
for f in `ls ../patches/*.patch` ; do
patch -p0 < ../patches/$f
done
cd AStyle/build/gcc/
make clean
JAVA_HOME=/usr/lib/jvm/java-8-openjdk CFLAGS="-Os" LDFLAGS="-s" make java
cp bin/libastyle*.so ../../../../libastylej64.so

构建 Arduino IDE

Arduino 使用了一个很 old fashion 的 xml 文件来描述构建过程,这个文件即 build.xml.

感觉这个文件的抽象程度不太够,很多同样的逻辑被写了很多遍,不过这也在一定程度上方便了我们的修改。只需要把 linuxaarch64 的部分复制一份,改成 linuxriscv64, 再稍作修改就好了。

arduino/arduino-add-riscv64-support.patch

_138
diff --git a/build/build.xml b/build/build.xml
_138
index c4de6aecf..418be1f1e 100644
_138
--- a/build/build.xml
_138
+++ b/build/build.xml
_138
@@ -24,6 +24,7 @@
_138
<condition property="platform" value="linux64"><os family="unix" arch="amd64" /></condition>
_138
<condition property="platform" value="linuxarm"><os family="unix" arch="arm" /></condition>
_138
<condition property="platform" value="linuxaarch64"><os family="unix" arch="aarch64" /></condition>
_138
+ <condition property="platform" value="linuxriscv64"><os family="unix" arch="riscv64" /></condition>
_138
_138
<condition property="windows_host" value="true"><os family="windows" /></condition>
_138
_138
@@ -35,6 +36,7 @@
_138
<condition property="linux"><equals arg1="${platform}" arg2="linux64" /></condition>
_138
<condition property="linux"><equals arg1="${platform}" arg2="linuxarm" /></condition>
_138
<condition property="linux"><equals arg1="${platform}" arg2="linuxaarch64" /></condition>
_138
+ <condition property="linux"><equals arg1="${platform}" arg2="linuxriscv64" /></condition>
_138
_138
<condition property="staging_folder" value="macosx"><equals arg1="${platform}" arg2="macosx" /></condition>
_138
<condition property="staging_hardware_folder" value="Arduino.app/Contents/Java/hardware"><equals arg1="${platform}" arg2="macosx" /></condition>

主要修改的地方就是 astylelistSerialPortsC 的部分, 让构建脚本去复制我们编译出来的文件而不是从网上下载解压得到的文件,其他的就是复制粘贴查找替换了。

这样我们就得到了第一版 PKGBUILD:

riscv64.patch
PKGBUILD

--- PKGBUILD
+++ PKGBUILD
@@ -17,7 +17,7 @@ arch=('x86_64')
url="https://github.com/arduino/Arduino"
license=('GPL' 'LGPL')
depends=('gtk3' 'desktop-file-utils' 'shared-mime-info' 'java-runtime=8' 'arduino-builder')
-makedepends=('gtk2-compat' 'java-environment=8' 'ant' 'unzip' 'asciidoc')
+makedepends=('gtk2-compat' 'java-environment=8' 'ant' 'unzip' 'asciidoc' 'git' 'subversion')
optdepends=('arduino-docs: Offline documentation for arduino'
'arduino-avr-core: AVR core with upstream avr-gcc and avrdude')
options=(!strip)
@@ -32,7 +32,12 @@ source=("${pkgname}-${pkgver}.tar.xz::https://github.com/arduino/Arduino/release
"https://downloads.arduino.cc/libastylej-2.05.1-5.zip.asc"
"https://downloads.arduino.cc/liblistSerials/liblistSerials-1.4.2-2.zip"
"https://downloads.arduino.cc/liblistSerials/liblistSerials-1.4.2-2.zip.asc"
- "arduino.sh")
+ "arduino.sh"
+ "$pkgname-add-riscv64-support.patch"
+ "git+https://github.com/arduino/listSerialPortsC#commit=e4427fa91eb09dcb27541221e134e3333311d32d"
+ gcc jnilib.c libserialport/linux_termios.c libserialport/linux.c libserialport/serialport.c -Ilibserialport/ -I$JAVA_INCLUDE_PATH -I$JAVA_INCLUDE_PATH/linux/ -shared -fPIC -o liblistSerialsj.so

调试

然后启动 qemu-user 测试了一下,发现 Arduino IDE 能顺利的跑起来.

Tip

这里使用的 archriscv qemu-user 容器可以参考这个教程搭建,注意需要修改 start-user 脚本来

  1. 挂载串口设备 /dev/ttyACM0 (设备名视情况而定)
  2. 共享 /run/user/1000 目录 (可能不是必须的)
  3. 共享 X11 unix socket /tmp/.X11-unix 目录
commands-to-run

xhost +local:
sudo ./start-user

start-user

systemd-nspawn -a -D ./archriscv \
--bind=/home/kxxt/Workspaces/arch-riscv:/home/kxxt/archriscv \
--bind=/run/user/1000 \
--bind=/dev/ttyACM0 \
--bind-ro=/tmp/.X11-unix \
--capability=CAP_SYS_ADMIN

arduino

但是串口监视器不工作。

jssc err

Google 搜索结果 显示确实有 Arduino 用户遇到过这个问题,但是搜出来的解决方案并不适用于我们的情况。

只好去看 jssc 的源码了. 一看,卧槽,竟然还有这种操作!

This version contains native libs for Windows(x86, x86-64), Linux(x86, x86-64, ARM soft & hard float), Solaris(x86, x86-64), Mac OS X(x86, x86-64, PPC, PPC64). All native libs contains in the jssc.jar file and you don’t need manage native libs manually.

jssc libs

jSSC 把各种平台的 native 库都打包到了 jar 里面去, 自然里面没有适用于 riscv64 的。

这严重的破坏了 jar 文件格式的平台无关性 (platform-independent). kxxt 在此强烈谴责这种行为!! 👿😡😤

解决

那么应该把 riscv64 的 native 库也打包到 jar 里面去就能解决问题了。

jSSC 2.8.0 的仓库里就只有光秃秃的源码和预编译好的 native libs, 也没有 Makefile, 也没有 CMakeLists.txt 之类的构建脚本, README 和 Wiki 里也没有构建的命令。

只好自己去猜一猜试一试了:


git clone https://github.com/java-native/jssc -b v2.8.0 && cd jssc
mkdir build
cp -r src/java/libs build
JAVA_INCLUDE_PATH=/usr/lib/jvm/java-8-openjdk/include
g++ src/cpp/_nix_based/jssc.cpp -I$JAVA_INCLUDE_PATH -I$JAVA_INCLUDE_PATH/linux/ \
-shared -fPIC -o build/libs/linux/libjSSC-2.8_riscv64.so -static-libstdc++ -static-libgcc
find src/java/jssc -type f -name '*.java' | xargs javac -d build
jar cf jssc-2.8.0-arduino4.jar -C build .

使用我们自己编译出来的 jssc-2.8.0-arduino4.jar 替换掉 /usr/share/arduino/lib/jssc-2.8.0-arduino4.jar 之后, 再次启动 Arduino IDE,打开串口监视器,它就 work 辣!

serial

结尾

因为 Arduino 和 ESP32 的 board registry 里面并没有 riscv64 的工具链,所以我们只能去使用包仓库里的 arduino-avr-core 了。

然而我手头并没有 AVR 的板子,只有两个 ESP32 板子,所以并没有办法去把编译出来的程序烧录到板子上去测试。

另外我们仓库里的 avr-gcc 还有个小问题有待解决,不知道为什么它在 riscv64 上会不支持 -fno-fat-lto-objects 选项, 而 Arduino 在编译的时候会用到这个选项。

err