基于Qemu LMT虚拟平台的bringup

在芯片开发的前期,RTL代码还在开发中,FPGA原型验证平台也没有可用的环境。这时候我们可以根据SoC spec在Qemu中开发一个新的Virtual Platform,那么BSP软件团队就可以提前完成一些软件环境的准备,比如最小系统。这样当RTL0.1释放后,我们可以直接复用这些软件在FPGA验证,从而减小搭建软件环境的压力和缩小验证时间。随着Qemu里面模拟的外设越来越多,我们可以把更多的软件function在Qemu里面完成开发和验证,然后再迁移到FPGA原型平台上进行验证,从而完善软件的时序方面的逻辑。通过这样的验证手段,让我们软件准备提前,缩短FPGA的占用时间,减少验证成本。


  • Qemu: v7.2.0
  • U-Boot: v2022.10
  • ATF: v2.10
  • OPTEE: v4.0.0
  • Linux Kernel: linux-6.1.12
  • aarch64-none-linux-gnu-gcc: 10.3.1 20210621
  • riscv32-unknown-linux-gnu-gcc: 12.2.0

芯片介绍

lmt-arch

图1 lmt芯片的架构
如上图所示,展示了芯片产品的框图,左边是基于ARM的主域,右边是基于RISC-V的从域。这样的芯片架构是目前很多自动驾驶ADAS芯片的解决方案,通过MCU Domain来实现芯片的故障诊断、甚至承接一部分车规相关的规控功能。

芯片是异构系统架构的,包含两种指令集CPU(ARM和RISC-V),因此我们在Qemu里面添加了两个machine:lmt-virt和lmt-safety-virt,前者用于模拟ARM系统部分,后面用于模拟RISC-V系统部分,最后通过Qemu里面的共享内存和chardev socket实现两个machine的联动(multi-process Qemu的交互)。

mult-process-qemu

图2 multi-process QEMU的架构

启动介绍

在运行起来之前,我们先明确最小系统涉及的组件以及它们的启动顺序

  • ATF:提供EL3 runtime service,具有整个硬件系统的全部权限
  • OPTEE:提供可信执行环境
  • SPL:完成Bootloader镜像的加载,以及DDR、Clock等初始化
  • U-Boot:加载和启动不同OS的镜像
  • Kernel:Linux kernel OS,常用的OS
  • initramfs:最小的文件系统,用作临时跳转
  • Ubuntu Rootfs:根文件系统,存放在存储介质上,里面包含很多应用、库和工具

启动顺序

lmt-bootflow

图3 启动流程
  • SPL将BL31 ATF、BL32 OPTEE和BL33 U-Boot三个镜像加载到DDR,将RISC-V Firmware加载到Safety SRAM中,然后跳转到BL31 ATF执行
  • ATF完成基本的初始化,然后跳转到BL32 OPTEE执行
  • OPTEE完成基本的初始化,然后返回到ATF的执行环境中,最后ATF直接跳转到U-Boot执行
  • U-Boot完成初始化,外设的初始化,从eMMC/Nor Flash等媒介中加载Kernel、initramfs和dtb等镜像文件,然后跳转到Kernel开始执行
  • Kernel完成启动初始化,拉起应用程序

镜像构建

ATF

1
2
3
4
5
6
7
8
9
10
11
12
13
git clone -b atf-v2.10 https://github.com/chasinglulu/arm-trusted-firmware.git
cd arm-trusted-firmware
make PLAT=lmt CROSS_COMPILE=aarch64-none-linux-gnu-

or
make PLAT=lmt CROSS_COMPILE=aarch64-none-linux-gnu- DEBUG=1 #debug

or
make PLAT=lmt CROSS_COMPILE=aarch64-none-linux-gnu- DEBUG=1 LOG_LEVEL=50 #verbose mode

or
make PLAT=lmt CROSS_COMPILE=aarch64-none-linux-gnu- SPD=opteed #with BL32 optee

OPTEE

1
2
3
4
5
6
7
8
git clone -b optee_os-4.0.0 https://github.com/chasinglulu/optee_os.git
cd optee_os
export ARCH=arm

make CROSS_COMPILE64=aarch64-none-linux-gnu- CROSS_COMPILE_ta_arm64=aarch64-none-linux-gnu- CROSS_COMPILE_ta_arm32=arm-linux-gnueabi- PLATFORM=ax-lmt

or
make CROSS_COMPILE64=aarch64-none-linux-gnu- CROSS_COMPILE_ta_arm64=aarch64-none-linux-gnu- CROSS_COMPILE_ta_arm32=arm-linux-gnueabi- PLATFORM=ax-lmt DEBUG=1 #debug

U-Boot

1
2
3
4
git clone -b uboot-2022.10 https://github.com/chasinglulu/u-boot.git
cd u-boot
make CROSS_COMPILE=aarch64-none-linux-gnu- ARCH=arm64 lmt-qemu-virt_defconfig
make -j4

Linux Kernel

1
2
3
4
git clone -b linux-v6.1-rt https://github.com/chasinglulu/linux.git
cd linux
make CROSS_COMPILE=aarch64-none-linux-gnu- ARCH=arm64 lmt-tiny_defconfig
make -j64

lmt-virt运行验证

启动U-Boot+Kernel

1
qemu-system-aarch64 -M lmt-virt,virt=on,emmc=on -m 4G -display none -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin,cpu-num=0 -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio

virt=on:使能CPU的EL2
secure=off:关闭CPU的EL3
emmc=on:使能eMMC模拟支持

U-Boot既可以运行在EL2也可以运行在EL3上,但是打开EL3的时候,需要运行ATF,不然Kernel发起PSCI访问会触发panic。

uboot_kernel

启动日志附件:U-Boot->kernel Booting

启动SPL+U-Boot+Kernel

1
qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x00,file=/path/to/u-boot/spl/u-boot-spl.bin,cpu-num=0 -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.img -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio

virt=on:使能CPU的EL2
secure=on:使能CPU的EL3
emmc=on:使能eMMC模拟支持

因为SPL和U-Boot既可以运行在EL2也可以运行在EL3,因此virt=secure=两个选项既可以on也可以off,两个选项可以随意组合,用于验证不同的使用场景。

secure=off:关闭CPU的EL3,Kernel下的PSCI访问请求都由Qemu捕获进行处理。如果使能EL3,必须运行ATF软件,不然Kernel发出PSCI请求会触发panic。

spl_uboot_kernel

启动日志附件:ATF->U-Boot->kernel Booting

启动ATF+U-Boot+Kernel

1
qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin,cpu-num=0 -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio

virt=on:使能CPU的EL2
secure=on:使能CPU的EL3
emmc=on:使能eMMC模拟支持

atf_uboot_kernel

启动日志附件:ATF->U-Boot->kernel Booting

启动SPL+ATF+U-Boot+Kernel

1
qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x0,file=/path/to/u-boot/spl/u-boot-spl.bin,cpu-num=0 -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio

virt=on:使能CPU的EL2
secure=on:使能CPU的EL3
emmc=on:使能eMMC模拟支持

spl_atf_uboot_kernel

启动日志附件:SPL->ATF->U-Boot->kernel Booting

启动ATF+OPTEE+U-Boot+Kernel

1
qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin,cpu-num=0 -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -device loader,addr=0x404000000,file=/path/to/optee_os/out/arm-plat-ax/core/tee-pager_v2.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio

virt=on:使能CPU的EL2
secure=on:使能CPU的EL3
emmc=on:使能eMMC模拟支持

atf_optee_uboot_kernel

启动日志附件:ATF->OPTEE->U-Boot->kernel Booting

启动SPL+ATF+OPTEE+U-Boot+Kernel

1
qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x0,file=/path/to/u-boot/spl/u-boot-spl.bin,cpu-num=0 -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -device loader,addr=0x404000000,file=/path/to/optee_os/out/arm-plat-ax/core/tee-pager_v2.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio

virt=on:使能CPU的EL2
secure=on:使能CPU的EL3
emmc=on:使能eMMC模拟支持

spl_atf_optee_uboot_kernel

启动日志附件:SPL->ATF->OPTEE->U-Boot->kernel Booting

lmt-safety-virt运行验证

lmt-safety-virt虚拟平台是基于RISC-V 32-bit架构开发的,在U-Boot里面添加了lmt-safety-virt平台的支持。下面是U-Boot编译构建命令:

1
2
3
cd u-boot
make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- lmt-qemu-riscv-without-dram_defconfig
make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- -j4

下面是运行验证命令:

1
qemu-system-riscv32 -M lmt-safety-virt -m 1M -display none -device loader,file=/path/to/u-boot/u-boot-with-spl.bin,addr=0x60c00000,cpu-num=0 -serial stdio

lmt-safety-virt

lmt-virt和lmt-safety-virt联合运行

SoC是异构的,里面即集成了ARM Cortex-A系列的CPU,也集成了RISC-V系列的CPU。因此一种架构的CPU开发一个对应的虚拟平台,两个及以上的虚拟平台对应多个Qemu进程,就涉及到多个Qemu之间的联动,用于仿真异构核之间的交互和控制逻辑。

下面是启动lmt-virt的命令:

1
qemu-system-aarch64 -M lmt-virt,riscv-memdev=iram-safety -m 4G -display none -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin,cpu-num=0 -object memory-backend-file,size=512K,id=iram-safety,share=on,mem-path=/path/to/qemu-shm,discard-data=on -rp-path /tmp/coemu -serial stdio -semihosting

/tmp/coemu:如果目录不存在,mkdir创建
riscv-memdev:指定IRAM的内存后端ID,由machine使用

下面是启动lmt-safety-virt的命令:

1
qemu-system-riscv32 -M lmt-safety-virt,standalone=off,memdev=iram-safety -m 2M -display none -chardev socket,id=coemu,path=/tmp/coemu/qemu_rport_machine_lmt_soc_cosim_rp -object memory-backend-file,size=512K,id=iram-safety,share=on,mem-path=/path/to/qemu-shm,discard-data=on -serial stdio

standalone=:指定machine的运行模式(单独或者联动)
memdev=:指定machine所使用内存后端的ID

both-virt

AArch64侧的启动日志附件:AArch64 Booting

RISC-V侧的启动日志附件:RISC-V Booting

目前只是做到了AArch64侧能够控制RISC-V侧的启动,两侧的通信交互还没有开发,比如在Linux内核下AArch64与RISC-V进行交互访问等。