RT-Thread BSP qemu-virt64-aarch64 的编译环境搭建

时间:2023-03-25 14:08:33


前言

  • 最近需要使用 RT-Thread smart 开发调试一些软件功能,由于软件功能平台无关,使用实际硬件操作,会耗费较多的时间在程序烧写环节。
  • 打算使用 BSP qemu-virt64-aarch64 搭建一个 RT-Thread smart 的开发调试环境,可以开发验证一些平台无关的软件功能

环境搭建

  • Win10 64位
  • VMware Workstation Pro
  • VS Code (ssh 远程)
  • ubuntu 20.04
  • RT-Thread master 最新分支: BSP qemu-virt64-aarch64

下载 RT-Thread

  • 这里使用 gitee 的 RT-Thread 仓库,先通过 fork 的方式,把 RT-Thread fork 到自己的账号下
  • ubuntu 中安装好 git qemu,通过 git 克隆一份 RT-Thread 最新代码
  • 可以直接克隆 RT-Thread 官方的 git clone https://gitee.com/rtthread/rt-thread.git

scons 构建

  • 进入 rt-thread/bsp/qemu-virt64-aarch64,直接 scons 可能会提示 scons 找不到,如果找不到,就安装一下 scons
zhangsz@zhangsz:~/rtt/smart/rt-thread/bsp/qemu-virt64-aarch64$ scons

Command 'scons' not found, but can be installed with:

sudo apt install scons
  • 安装 scons 的方法: $ sudo apt install scons
  • 运行 $ scons --menuconfig,进入Kconfig 图形配置界面,初步运行,会克隆 Linux 下的 RT-Thread env 工具 与 packages 软件包

RT-Thread BSP qemu-virt64-aarch64 的编译环境搭建

RT-Thread BSP qemu-virt64-aarch64 的编译环境搭建

交叉编译工具链

  • 再次运行 scons 后,发现提示找不到 gcc 交叉编译工具链, aarch64-none-elf-gcc: not found
zhangsz@zhangsz:~/rtt/smart/rt-thread/bsp/qemu-virt64-aarch64$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building associated VariantDir targets: build
CC build/applications/console.o
sh: 1: aarch64-none-elf-gcc: not found
scons: *** [build/applications/console.o] Error 127
scons: building terminated because of errors.
  • 下载工具链:可以使用 get_toolchain.py 下载,不过这个脚本默认没有在 RT-Thread 工程里面,需要手动创建一个
  • rt-thread/bsp/qemu-virt64-aarch64 目录下,新建一个 tools 目录,然后进入这个 rt-thread/bsp/qemu-virt64-aarch64/tools 目录,创建 get_toolchain.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# Copyright (c) 2022, RT-Thread Development Team
#
# SPDX-License-Identifier: GPL-2.0
#
# Change Logs:
# Date           Author       Notes
# 2022-02-1      Bernard      The first version
#

import os
import sys
import platform

from ci import CI

toolchains_config = {
    'arm':
    {
        'Linux': 'arm-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2',
        'Windows': 'arm-linux-musleabi_for_i686-w64-mingw32_latest.zip'
    },
    'aarch64':
    {
        'Linux' : 'aarch64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2',
        'Windows' : 'aarch64-linux-musleabi_for_i686-w64-mingw32_latest.zip'
    },
    'riscv64':
    {
        'Linux': 'riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2',
        'Windows': 'riscv64-linux-musleabi_for_i686-w64-mingw32_latest.zip'
    }
}

if __name__ == '__main__':
    # download toolchain
    if len(sys.argv) > 1:
        target = sys.argv[1]
    else:
        target = 'arm'

    ci = CI()
    toolchain_path = os.path.join(os.path.abspath('.'), 'gnu_gcc')

    platform = platform.system()

    try:
        zfile = toolchains_config[target][platform]
        URL = 'http://117.143.63.254:9012/www/rt-smart/' + zfile
    except:
        print('not found target')
        exit(0)

    ci.downloadFile(zfile, URL)
    ci.extractZipFile(zfile, toolchain_path)
    ci.delFile(zfile)
  • rt-thread/bsp/qemu-virt64-aarch64/tools目录下,创建一个 ci.py 脚本
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# Copyright (c) 2022, RT-Thread Development Team
#
# SPDX-License-Identifier: GPL-2.0
#
# Change Logs:
# Date           Author       Notes
# 2022-02-1      Bernard      The first version
#

import os
import sys
import shutil
import platform
import requests
import time
import zipfile

class CI:
    def downloadFile(self, name, url):
        headers = {'Proxy-Connection':'keep-alive'}
        r = requests.get(url, stream=True, headers=headers)
        length = float(r.headers['content-length'])
        f = open(name, 'wb')
        count = 0
        count_tmp = 0
        time1 = time.time()
        for chunk in r.iter_content(chunk_size = 512):
            if chunk:
                f.write(chunk)
                count += len(chunk)
                if time.time() - time1 > 2:
                    p = count / length * 100
                    speed = (count - count_tmp) / 1024 / 1024 / 2
                    count_tmp = count
                    print(name + ': ' + '{:.2f}'.format(p) + '%')
                    time1 = time.time()
        print(name + ': 100%')
        f.close()

    def extractZipFile(self, zfile, folder):
        # self.delTree(folder)
        if not os.path.exists(folder):
            os.makedirs(folder)
        if platform.system() == 'Windows':
            zip_file = zipfile.ZipFile(zfile)
            zip_list = zip_file.namelist()
            for item in zip_list:
                print(item)
                zip_file.extract(item, folder)
            zip_file.close()
        elif platform.system() == 'Linux':

            if zfile.endswith('tar.gz'):
                os.system('tar zxvf %s -C %s' % (zfile, folder))
            elif zfile.endswith('tar.bz2'):
                os.system('tar jxvf %s -C %s' % (zfile, folder))
            elif zfile.endswith('.zip'):
                os.system('unzip %s -d %s' % (zfile, folder))

        return

    def zipFolder(self, folder, zfile):
        zip_filename = os.path.join(folder)
        zip = zipfile.ZipFile(zfile, 'w', compression=zipfile.ZIP_BZIP2)
        pre_len = len(os.path.dirname(folder))

        for parent, dirnames, filenames in os.walk(folder):
            for filename in filenames:
                pathfile = os.path.join(parent, filename)
                arcname = pathfile[pre_len:].strip(os.path.sep)
                zip.write(pathfile, arcname)

        zip.close()
        return

    def touchDir(self, d):
        if not os.path.exists(d):
            os.makedirs(d)

    def gitUpdate(self, url, folder, branch = 'master'):
        cwd = os.getcwd()

        if os.path.exists(folder):
            os.chdir(folder)
            os.system('git pull origin')
            if branch != 'master':
                os.system('git checkout -b %s origin/%s' % (branch, branch))
            os.system('git submodule init')
            os.system('git submodule update')
        else:
            os.system('git clone %s %s' % (url, folder))
            os.chdir(folder)
            os.system('git submodule init')
            os.system('git submodule update')

        os.chdir(cwd)

    def installEnv(self, folder):
        env_path = folder

        cwd = os.getcwd()
        os.chdir(env_path)

        self.touchDir(os.path.join(env_path, 'local_pkgs'))
        self.touchDir(os.path.join(env_path, 'packages'))
        self.touchDir(os.path.join(env_path, 'tools'))

        self.gitUpdate('https://gitee.com/RT-Thread-Mirror/env.git', 'tools/script')
        self.gitUpdate('https://gitee.com/RT-Thread-Mirror/packages.git', 'packages/packages')

        kconfig = open(os.path.join(env_path, 'packages', 'Kconfig'), 'w')
        kconfig.write('source "$PKGS_DIR/packages/Kconfig"')
        kconfig.close()

        os.chdir(cwd)

        return

    def pkgsUpdate(self, env_folder):
        self.touchDir(env_folder)
        self.installEnv(env_folder)

        os.environ['PKGS_DIR'] = env_folder
        os.system('python %s package --update' % (os.path.join(env_folder, 'tools', 'script', 'env.py')))

        return

    def delTree(self, folder):
        if os.path.exists(folder):
            shutil.rmtree(folder)

    def delFile(self, file):
        if os.path.exists(file):
            os.remove(file)

    def appendFile(self, srcFile, otherFile):
        f = open(otherFile, 'r')
        s = f.read()
        f.close()

        f = open(srcFile, 'a')
        f.write(s)
        f.close()

    def copyTree(self, srcTree, dstTree):
        if os.path.exists(dstTree):
            shutil.rmtree(dstTree)
        shutil.copytree(srcTree, dstTree)

    def run(self, cmds):
        cwd = os.getcwd()

        cmds = cmds.split('\n')
        for item in cmds:
            item = item.lstrip()
            if item == '':
                continue

            if item[0] == '-':
                os.system(item[1:].lstrip())

        # keep current directory
        os.chdir(cwd)
        return

if __name__ == '__main__':
    ci = CI()
    env_folder = os.path.abspath(os.path.join('.', 'env_test'))

    # ci.pkgsUpdate(env_folder)
    cmds = '''
        # test
        - dir
        - dir tools
    '''

    ci.run(cmds)
  • 下载gcc 交叉编译工具链: qemu-virt64-aarch64aarch64 平台

$ python3 get_toolchain.py aarch64 就可以下载 aarch64 的 gcc 交叉编译工具链了

  • rt-thread/bsp/qemu-virt64-aarch64 目录下创建 一个设置环境变量的 shell 脚本,如 smart_env.sh
#!/bin/bash

# usage:
# source smart-env.sh [arch]
# example: source smart-env.sh          # arm
# example: source smart-env.sh aarch64  # aarch64

# supported arch list
supported_arch="arm aarch64 riscv64 i386"

def_arch="unknown"

# find arch in arch list
if [ -z $1 ]
then
    def_arch="arm" # default arch is arm
else
    for arch in $supported_arch
    do
        if [ $arch = $1 ]
        then
            def_arch=$arch
            break
        fi
    done
fi

# set env
case $def_arch in 
    "arm")
        export RTT_CC=gcc
        export RTT_EXEC_PATH=$(pwd)/tools/gnu_gcc/arm-linux-musleabi_for_x86_64-pc-linux-gnu/bin
        export RTT_CC_PREFIX=arm-linux-musleabi-
        ;;
    "aarch64")
        export RTT_CC=gcc
        export RTT_EXEC_PATH=$(pwd)/tools/gnu_gcc/aarch64-linux-musleabi_for_x86_64-pc-linux-gnu/bin
        export RTT_CC_PREFIX=aarch64-linux-musleabi-
        ;;
    "riscv64")
        export RTT_CC=gcc
        export RTT_EXEC_PATH=$(pwd)/tools/gnu_gcc/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin
        export RTT_CC_PREFIX=riscv64-unknown-linux-musl-
        ;;
    "i386")
        export RTT_CC=gcc
        export RTT_EXEC_PATH=$(pwd)/tools/gnu_gcc/i386-linux-musleabi_for_x86_64-pc-linux-gnu/bin
        export RTT_CC_PREFIX=i386-unknown-linux-musl-
        ;;
    *)  echo "unknown arch!"
        return 1
esac

# export RTT_EXEC_PATH
export PATH=$PATH:$RTT_EXEC_PATH

echo "Arch      => ${def_arch}"
echo "CC        => ${RTT_CC}"
echo "PREFIX    => ${RTT_CC_PREFIX}"
echo "EXEC_PATH => ${RTT_EXEC_PATH}"
  • 下载 gcc 交叉编译工具链后, 在 rt-thread/bsp/qemu-virt64-aarch64 运行
  • 设置 smart_env.sh 的执行权限 $ chmod +x smart_env.sh

$ source smart_env.sh aarch64,即可设置 qemu-virt64-aarch64 的 gcc 交叉编译工具链

RT-Thread BSP qemu-virt64-aarch64 的编译环境搭建

RT-Thread BSP qemu-virt64-aarch64 的编译环境搭建

编译 qemu-virt64-aarch64

  • 配置好 gcc 交叉编译工具链后,就可以 scons 编译了

RT-Thread BSP qemu-virt64-aarch64 的编译环境搭建

运行 qemu

  • qemu-virt64-aarch64 目录下有个 qemu.sh,可以在 Linux shell 里面直接运行
zhangsz@zhangsz:~/rtt/smart/rt-thread/bsp/qemu-virt64-aarch64$ ./qemu.sh
65536+0 records in
65536+0 records out
67108864 bytes (67 MB, 64 MiB) copied, 0.0898412 s, 747 MB/s
[I/libcpu.aarch64.cpu] Using MPID 0x0 as cpu 0
[I/libcpu.aarch64.cpu] Using MPID 0x1 as cpu 1
[I/libcpu.aarch64.cpu] Using MPID 0x2 as cpu 2
[I/libcpu.aarch64.cpu] Using MPID 0x3 as cpu 3

 \ | /
- RT -     Thread Operating System
 / | \     5.0.0 build Mar 18 2023 19:11:05
 2006 - 2022 Copyright by RT-Thread team
[I/libcpu.aarch64.cpu] Secondary CPU 1 booted
[I/libcpu.aarch64.cpu] Sec />ry CPU 2 booted
[I/libcpu.aarch64.cpu] Secondary CPU 3 booted
m[0m
hello rt-thread

msh />
msh />ls
No such directory
msh />QEMU: Terminated
  • 退出 qemu 的方法: CTRL + a 组合按一下,松开按键,再 按一下 x 键即可退出 qemu

小结

  • 由于 Linux 的环境存在差异,如qemu 之前安装过,所以 qemu-virt64-aarch64 的交叉编译环境 搭建起来并没有遇到太多的问题,如果遇到环境依赖,需要根据提示,安装 ubuntu 相应的 软件包,如 qemuqemu-system-aarch64
  • 当前 RT-Thread master 分支的 qemu-system-aarch64 跑的是 RT-Thread,而不是 RT-Thread Smart,后面研究如何运行 RT-Thread Smart,并用于软件组件的开发调试