嵌入式技术论坛
直播中

世态薄凉

8年用户 1071经验值
私信 关注
[经验]

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

  前言
  最近需要使用 RT-Thread smart 开发调试一些软件功能,由于软件功能平台无关,使用实际硬件操作,会耗费较多的时间在程序烧写环节。
  打算使用 BSP qemu-virt64-aarch64 搭建一个 RT-Thread smart 的开发调试环境,可以开发验证一些平台无关的软件功能,但是当前的 RT-Thread smart 开放出来的 userapps 不支持 aarch64 平台。
  所以选择 qemu-virt64-riscv 平台进行编译环境的搭建,搭建的流程跟 qemu-virt64-aarch64 基本一致。
  环境搭建
  Win10 64位
  VMware Workstation Pro
  VS Code (ssh 远程)
  ubuntu 20.04
  RT-Thread master 最新分支: BSP qemu-virt64-riscv
  下载 RT-Thread
  这里使用 gitee 的 RT-Thread 仓库,先通过 fork 的方式,把 RT-Thread fork 到自己的账号下
  ubuntu 中安装好 git qemu,通过 git 克隆一份 RT-Thread 最新代码
  建议手动搭建一个 qemu-virt64-riscv 的独立工程,修改一下构建与配置脚本的路径即可。
  scons 构建
  进入 rt-thread/bsp/qemu-virt64-riscv,直接 scons编译,如果第一次,可能提示 scons 找不到,找不到就安装一下 scons
  $ 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 软件包
1.jpg
  交叉编译工具链
  再次运行 scons 后,发现提示找不到 gcc 交叉编译工具链, riscv64-unknown-linux-musl-gcc: not found
  下载工具链:可以使用 get_toolchain.py 下载,不过这个脚本默认没有在 RT-Thread 工程里面,需要手动创建一个
  备注:可以在 RT-Thread userapps 仓库中 copy 一份出来
  在 rt-thread/bsp/qemu-virt64-riscv 目录下,新建一个 tools 目录,然后进入这个 rt-thread/bsp/qemu-virt64-riscv/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 =  + 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-riscv/tools 目录下,创建一个 ci.py 脚本,因为 get_toolchain.py 依赖这个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( ‘tools/script’)
  self.gitUpdate( ‘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-riscv 是 riscv64 平台
  $ python3 get_toolchain.py riscv64 就可以下载 riscv64 的 gcc 交叉编译工具链了
  在 rt-thread/bsp/qemu-virt64-riscv 目录下创建 一个设置环境变量的 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}”
  设置 smart_env.sh 的执行权限 $ chmod +x smart_env.sh
  下载 gcc 交叉编译工具链后, 在 rt-thread/bsp/qemu-virt64-riscv 运行
  $ source smart_env.sh riscv64,即可设置 qemu-virt64-riscv 的 gcc 交叉编译工具链
  编译 qemu-virt64-aarch64
  配置好 gcc 交叉编译工具链后,就可以 scons 编译了
  运行 qemu 无法启动
  qemu-virt64-riscv 目录下有个 qemu-nographic.sh,可以在 Linux shell 里面直接运行
1.jpg
  当前 qemu 启动失败,报如下的错误
  zhangsz@zhangsz:~/rtt/smart/rtt_qemu_aarch64/qemu-virt64-riscv$ 。/qemu-nographic.sh
  qemu-system-riscv64: warning: No -bios option specified. Not loading a firmware.
  qemu-system-riscv64: warning: This default will change in a future QEMU release. Please use the -bios option to avoid breakages when this happens.
  qemu-system-riscv64: warning: See QEMU‘s deprecation documentation for details.
  QEMU: Terminated
  退出 qemu 的方法: CTRL + a 组合按一下,松开按键,再 按一下 x 键即可退出 qemu
  qemu 更新解决启动问题
  经过验证,确认 ubuntu 20.04 默认安装的 qemu 版本比较的老,需要更新最新的 qemu 版本,直接使用 sudo apt install qemu-system-riscv64 无法更新,只能手动更新。
  解决方法:下载 qemu 的代码,手动编译更新 qemu,使用新版本的 qemu-system-riscv64
  qemu 下载地址:可以再 github 上下载,注意拉取更新 git 子仓库
  下载 qemu : $ git clone https://github.com/qemu/qemu.git
  qemu 编译依赖: $ sudo apt-get install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build
  qemu git 子仓库 : $ git submodule update --init --force --recursive
  编译 qemu 的方法
  进入 qemu 目录
  $ mkdir build
  $ cd build
  $ 。。/configure
  $ make -j8
1.jpg
  编译完 qemu 后,会在 生成目录,如 build 目录下,生成新版本的 qemu 系列工具,就是有点耗时。
  zhangsz@zhangsz:~/rtt/qemu/build$ 。/qemu-system-riscv64 --version
  QEMU emulator version 7.2.90 (v8.0.0-rc0-27-g74c581b645-dirty)
  Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
1.jpg
  把 qemu 新版本 qemu-system-riscv64 的执行路径,替换 qemu-virt64-riscv 中 qemu 的执行脚本中的 qemu-system-riscv64 即可。
  我当前可以运行的脚本,把 qemu-system-riscv64 替换为 /home/zhangsz/rtt/qemu/build/qemu-system-riscv64,由于提示 网络设备部分参数不支持,我暂时先把网络设备部分去掉了
  if [ ! -f “sd.bin” ]; then
  dd if=/dev/zero of=sd.bin bs=1024 count=65536
  fi
  /home/zhangsz/rtt/qemu/build/qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin \\
  -drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \\
  -device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
1.jpg
  运行 qemu 效果
  zhangsz@zhangsz:~/rtt/smart/rtt_qemu_aarch64/qemu-virt64-riscv$ 。/qemu-nographic.sh
  OpenSBI v1.2
  ____ _____ ____ _____
  / __ \\ / ____| _ \\_ _|
  | | | |_ __ ___ _ __ | (___ | |_) || |
  | | | | ’_ \\ / _ \\ ‘_ \\ \\___ \\| _ 《 | |
  | |__| | |_) | __/ | | |____) | |_) || |_
  \\____/| .__/ \\___|_| |_|_____/|____/_____|
  | |
  |_|
  Platform Name : riscv-virtio,qemu
  Platform Features : medeleg
  Platform HART Count : 1
  Platform IPI Device : aclint-mswi
  Platform Timer Device : aclint-mtimer @ 10000000Hz
  Platform Console Device : uart8250
  Platform HSM Device : ---
  Platform PMU Device : ---
  Platform Reboot Device : sifive_test
  Platform Shutdown Device : sifive_test
  Firmware Base : 0x80000000
  Firmware Size : 212 KB
  Runtime SBI Version : 1.0
  Domain0 Name : root
  Domain0 Boot HART : 0
  Domain0 HARTs : 0*
  Domain0 Region00 : 0x0000000002000000-0x000000000200ffff (I)
  Domain0 Region01 : 0x0000000080000000-0x000000008003ffff ()
  Domain0 Region02 : 0x0000000000000000-0xffffffffffffffff (R,W,X)
  Domain0 Next Address : 0x0000000080200000
  Domain0 Next Arg1 : 0x000000008fe00000
  Domain0 Next Mode : S-mode
  Domain0 SysReset : yes
  Boot HART ID : 0
  Boot HART Domain : root
  Boot HART Priv Version : v1.12
  Boot HART Base ISA : rv64imafdch
  Boot HART ISA Extensions : time,sstc
  Boot HART PMP Count : 16
  Boot HART PMP Granularity : 4
  Boot HART PMP Address Bits: 54
  Boot HART MHPM Count : 16
  Boot HART MIDELEG : 0x0000000000001666
  Boot HART MEDELEG : 0x0000000000f0b509
  heap: [0x802bbcb8 - 0x842bbcb8]
  \\ | /
  - RT - Thread Smart Operating System
  / | \\ 5.0.0 build Mar 19 2023 16:40:43
  2006 - 2022 Copyright by RT-Thread team
  lwIP-2.0.3 initialized!
  [I/sal.skt] Socket Abstraction Layer initialize success.
  [I/utest] utest is initialize success.
  [I/utest] total utest testcase num: (0)
  file system initialization done!
  Hello RISC-V
1.jpg
  qemu 支持 elm fat 文件系统
  运行 qemu 后,发现 ls 提示找不到文件,查看代码,发现没有 mnt 文件挂载的操作,所以从其他的bsp 中,如 qemu-virt64-aarch64 copy 过来一份 mnt.c,重新编译。
  第一次运行 qemu 会生成 sd.bin,这个 sd.bin 为 RAW 格式的,可以使用 Linux shell 命令:mkfs.fat sd.bin,格式化为 fat 格式,这样就可以正常的在RT-Thread 中挂载了
  就可以支持 elm fat 格式的文件系统了。
  小结
  由于 ubuntu 20.04 默认安装的 qemu qemu-system-riscv64 版本较低,所以需要手动更新 qemu-system-riscv64 版本到最新,可以通过 qemu git 仓库手动编译
  当前 RT-Thread master 分支的 qemu-virt64-riscv 跑的是 RT-Thread,而不是 RT-Thread Smart,切换为 RT-Thread Smart,当前只需要配置使用 RT-Thread Smart 配置选项即可!
1.jpg
  后面尝试 把 RT-Thread 提供的 userapps 编译后,放到 qemu 中 RT-Smart 的文件系统中,运行用户态程序。



原作者:张世争

更多回帖

发帖
×
20
完善资料,
赚取积分