TECH ARTICLE

dSPACE VEOS QEMU Coupling:
Buildroot 鏡像製作與雙模式配置實務

以 VEOS 2026-A 的 QEMU Coupling(dsQEMU)為基礎,完整說明如何在 Build Server 上把一份 Buildroot 鏡像做成可用的 POSIX-Based V-ECU,以及在 icount 與 idle-detect 兩種時間同步模式下各自需要的編譯環境與鏡像配置——每個步驟都附手動操作指令與對應的驗證方法。

dSPACE VEOS 的 QEMU Coupling(本文簡稱 dsQEMU)讓一顆完整的 Linux 系統以 POSIX-Based V-ECU 的形式加入 VEOS 的模擬時間軸,與 Plant FMU、Classic AUTOSAR vECU、Restbus 容器在同一個 OSA 內協同運作。這使得「跑在 SoC 上的 Linux 應用」得以在沒有實體硬體的階段就進入整合驗證。

實務上最容易被低估的一段,是鏡像本身。一份能在原生 QEMU 開機的最小 Linux,並不等於一份能在 VEOS 時間軸上工作的 V-ECU 鏡像。本文以 Buildroot 為例,把這段差距拆解為「Build Server 端的編譯環境配置」與「鏡像產出後的模擬環境配置」兩個階段,並逐步標示每個動作的驗證方式。

本文適用環境

模擬主機Ubuntu 24.04 LTS + dSPACE VEOS 2026-A(Simulator Console 26.1)、veos-qemu 26.1(QEMU 9.2.3 基礎)
Build ServerUbuntu 24.04 LTS、Buildroot(BR2_VERSION = 2026.08-git,make print-version = 2026.05-586-gdf6f3ad43f)、基準 configs/qemu_x86_64_defconfig
Guestx86_64、Linux 6.18.7(icount 模式使用)、BusyBox init、rootfs 為單一 raw ext2 映像
idle-detect 專用Guest kernel 由 VEOS 強制替換為 veos-linux 6.6.119;模擬主機需具備 /dev/kvm

文中路徑以 $HOME/buildroot(Build Server)與 ~/dsqemu(模擬主機)表示;主機位址、帳號請替換為您自身環境的值。所有指令均為一般使用者權限,不需要 sudo。

1. 為什麼原始鏡像不能直接使用

這個問題的答案必須分成兩種模式來回答,因為兩者的失敗原因完全不同。以未經調整的原始鏡像實測:

項目 icount 模式 idle-detect 模式
能開機嗎 ✅ 能,可開到 buildroot login: ❌ 不能,/dev 是空的
能執行測試嗎 ❌ 不能:沒有受測應用、沒有網路位址 ❌ 不能(尚未進入 userspace)
問題性質 缺少測試載具與網路配置 結構性:開機路徑與原始鏡像的假設不符

1.1 idle-detect 模式的開機路徑差異

idle-detect 是 hypervisor 模式,VEOS 會強制指定自己的 guest kernel,並經由該 kernel 內建的 initramfs 進入您的 rootfs。實測到的關鍵事實是:它使用 chroot() 而非 switch_root,且僅建立了一個裝置節點。

# idle-detect 模式下 veos-linux initramfs 的實際動作(節錄自 guest console) [INFO] Root set to: /dev/vda [CALL] mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)) [INFO] "1" > /sys/dsprobes/init [CALL] chroot("/mnt") ← 是 chroot,不是 switch_root [ 2.22] VEOS: Registered veos-clock (tick duration of 1000000ns). [ 2.25] dsprobes: Userspace enabled veos-clock. Entering VEOS simulator mode. [INFO] Attempting /sbin/init

Linux kernel 的 CONFIG_DEVTMPFS_MOUNT 自動掛載 /dev 只在「kernel 自行掛載 root 檔案系統」時觸發,走 initramfs + chroot 這條路徑不會發生。而 Buildroot 預設的 /etc/fstab 並沒有 devtmpfs 條目——它信賴那個自動掛載。結果是:

can't open /dev/null: No such file or directory (重複 4 次) Starting veos-sut: OK (然後受測應用一行輸出都沒有)

1.2 一句話總結

原始鏡像是「能在 QEMU 裡開機的最小 Linux」;而 dsQEMU 需要的是「能在 VEOS 的時間軸上、透過虛擬 Ethernet 與其他模擬參與者交換資料、且不與模擬時鐘與 console 通道衝突的 V-ECU」。本文其餘篇幅就是在填補這段差距。

2. 兩種同步模式如何決定配置策略

在動手改任何設定之前,必須先理解兩種模式對鏡像的支配關係,否則很容易把「該改 JSON 的事」改到鏡像裡去。

面向 icount(CPU 模擬) idle-detect(Hypervisor)
Guest kernel 您自己編譯的(Buildroot 6.18.7) VEOS 的 veos-linux 6.6.119,由 VEOS 以 -kernel 指定
KVM 不需要 必須具備 /dev/kvm
時間同步程度 部分同步(按指令數截斷) 完全同步(偵測 idle 才推進時間)
kernel config 是否有效 ✅ 有效,是您控制的 ❌ 完全無效,您的 kernel 不會被載入
Guest /lib/modules 可用 ❌ 版本不符,任何 insmod 必然失敗
CPU 數 可 > 1,建議 1(重現性) VEOS 強制 -smp 1
中斷點凍結模擬時間 ❌ 不支援 ✅ 支援
實測加速比(單節點) 0.95 1.000
實測開機至應用啟動 約 9 秒 約 0.4 秒
雙節點時間一致性 各自獨立推進 lockstep(兩 guest 時間完全相同)

策略結論:Build Server 上只需要維護一套 .config 與一份 overlay,即可同時滿足兩種模式。原因是兩種模式對鏡像的要求是聯集而非互斥——idle-detect 的額外要求(明確掛載 devtmpfs、不依賴核心模組)在 icount 下無害;而 icount 專屬的 kernel 設定在 idle-detect 下只是被忽略。切換模式只需更換 JSON 配置檔。

2.1 各模式對鏡像的具體要求

icount 需要的(與 kernel 相關)

  • • VIRTIO_BLK / VIRTIO_NET / VIRTIO_PCI 內建於 kernel
  • • SERIAL_8250_CONSOLE 內建(console 走 ttyS0)
  • • EXT4_FS;Buildroot 的 rootfs.ext2 由 ext4 驅動掛載
  • • 選用:CONFIG_IP_PNP,讓位址可由 kernel 命令列指定

idle-detect 需要的(與 rootfs 相關)

  • • init 第一個動作就掛 devtmpfs(不可依賴 kernel automount)
  • • 不得依賴 /lib/modules(版本必然不符)
  • • 需有熵源,或移除所有熵消費者(由 JSON 提供 virtio-rng)
  • • 不得有任何行程搶佔 /dev/console
  • • 不得有任何時間同步機制(NTP/hwclock)覆寫模擬時鐘

2.2 這五件事不要改鏡像——改 JSON 就好

以下項目常被誤判為需要重建鏡像,實際上都屬於模擬環境的配置:

現象/原始設定 正確的處理位置
Buildroot 原生啟動腳本使用 console=tty1 console=ttyS0JSON 的 -append 只保留 console=ttyS0(VEOS 會加 -nographic,tty1 無用)
原生使用 -net user移除。VEOS 自行以 socket 形式把網卡接上 Ethernet cluster
NIC 型號預設為 e1000JSON 明寫 model=virtio-net-pci——veos-linux kernel 沒有 e1000 驅動
多核心設定JSON 寫 -smp 1(idle-detect 會被 VEOS 強制為 1)
idle-detect 下 console 全黑JSON 補 -serial stdio——該模式 VEOS 會加 -nodefaults,取消了預設 serial 裝置

3. Build Server 端:編譯環境配置

本節從取得 Buildroot 原始碼開始,目標產物是兩個檔案:output/images/bzImage 與 output/images/rootfs.ext2。以下每個步驟都分為操作與驗證兩部分。

STEP 1 / 8

取得 Buildroot 原始碼與建置相依套件

Buildroot 本身不需要安裝——它是一棵以 make 驅動的原始碼樹,解開即可使用。真正需要準備的是建置主機的相依套件與一個明確可追溯的版本。本步驟是整份流程唯一需要 sudo 的地方,且只發生在 Build Server 上。

(a) 建置主機相依套件

以下為 Buildroot 官方要求的必要套件,於 Ubuntu 24.04 的對應安裝指令。libncurses-dev 是 make menuconfig 的前提,缺少時只有互動式設定會失敗,命令列設定不受影響。

操作
sudo apt update sudo apt install -y build-essential file wget cpio unzip rsync bc \ bzip2 patch python3 git libncurses-dev # build-essential 已涵蓋 gcc / g++ / make / libc 開發檔案。 # perl / tar / gzip / sed / findutils / which 屬 Ubuntu 標準安裝,通常無須另裝。

(b) 取得原始碼——方式一:git(建議)

git 是建議方式,因為它讓「用了哪一版」成為可查證的事實,而不是文件裡的一句描述。上游正式位置在 GitLab,GitHub 為官方鏡像。

操作
cd $HOME # 官方 upstream git clone https://gitlab.com/buildroot.org/buildroot.git # 官方鏡像(GitLab 不可達時使用) # git clone https://github.com/buildroot/buildroot.git cd buildroot git tag -l '2026.*' | tail -6 # 檢視可用版本標籤 # → 2026.05 2026.05-rc1..rc4 2026.05.1 # 固定到特定發行版(生產環境建議): git checkout 2026.05.1 # 或停留在預設分支使用開發快照(本文基準即為此情形)

(c) 取得原始碼——方式二:tarball(離線/受管環境)

操作
V=2026.05.1 cd $HOME wget https://buildroot.org/downloads/buildroot-$V.tar.gz wget https://buildroot.org/downloads/buildroot-$V.tar.gz.sign # .sign 內含雜湊值與 GPG 簽章,據以驗證完整性 cat buildroot-$V.tar.gz.sign sha256sum buildroot-$V.tar.gz # 與 .sign 記載的值比對 tar -xf buildroot-$V.tar.gz cd buildroot-$V

(d) 版本識別:兩個值,用途不同

這一點值得特別說明,因為它常導致文件與實機對不上。Buildroot 的 BR2_VERSION 是開發中版本字串,指向「下一個」發行版;而在 git 樹內,make print-version 會展開為 git describe 的結果——那才是可重現的識別。以本文的基準環境實測:

grep -m1 '^export BR2_VERSION' Makefile # → export BR2_VERSION := 2026.08-git ← 開發中字串,非發行版 make print-version # → 2026.05-586-gdf6f3ad43f ← tag + 提交數 + 短 hash(可重現) git log -1 --format='%H %ad' --date=short # → df6f3ad43f6dc4a8e76fa4b27a2c85a0b981de79 2026-07-27

請把 make print-version 的輸出記錄進測試報告,而不是記錄 2026.08-git。前者可以精準還原同一棵樹,後者在數個月內會對應到成百上千個不同的提交。

(e) 四項環境限制

限制 說明
不以 root 執行Buildroot 官方明確要求。以 root 建置會讓 target 檔案的擁有者與權限失去意義,並可能影響建置主機本身
路徑不可含空白Buildroot 不支援含空白的路徑,症狀會出現在建置中段而非一開始
磁碟空間實測值:本文組態完整建置後整棵樹約 17 GB(其中 output/ 16 GB、dl/ 659 MB)。建議預留 30 GB 以上
網路可達性建置過程會從上游抓取各套件的原始碼。受管網路請見下方 (f)

(f) 受管網路環境:先抓齊原始碼再離線建置

# 需先有 .config(即 STEP 2 之後)。抓齊全部上游 tarball 到 dl/ make source # 或改指向企業內部鏡像:於 .config 設定 # BR2_PRIMARY_SITE="https://<internal-mirror>/buildroot-dl" # BR2_PRIMARY_SITE_ONLY=y ← 只從內部鏡像取,不回退到上游 # dl/ 可在多台建置機之間共用,或以 BR2_DL_DIR 指向共用路徑
驗證
cd $HOME/buildroot make print-version # 預期:輸出一個明確的版本識別,不是空值 make --version | head -1 # 預期:GNU Make 3.81 以上(實測 4.3) id -u # 預期:非 0 pwd | grep -q ' ' && echo "路徑含空白,需搬移" || echo "路徑 OK" df -h . # 預期:可用空間 30 GB 以上 nproc # 決定後續 make -jN(實測環境 24 核) # 最關鍵的一項:目標 defconfig 存在 make list-defconfigs | grep -c qemu_x86_64_defconfig # 預期:1。若為 0,表示取得的樹不完整或版本過舊
STEP 2 / 8

建立基準組態與備份

以 Buildroot 官方的 QEMU x86_64 defconfig 為起點,並在改動前保留原始 .config,讓整個變更集可回溯、可還原。

操作
cd $HOME/buildroot make qemu_x86_64_defconfig # 首次建立時 cp -n .config .config.bak.orig # 保留原始組態,-n 避免覆蓋既有備份
驗證
grep -E '^(BR2_DEFCONFIG|BR2_ARCH|BR2_INIT_BUSYBOX|BR2_TARGET_ROOTFS_EXT2)=' .config ls -l .config.bak.orig # 預期:架構為 x86_64、init 為 BusyBox、備份檔存在
STEP 3 / 8

建立 board/veos 目錄並置入專案檔案

所有 VEOS 專用內容集中於一個 board 目錄,不散落在 Buildroot 樹的其他位置,讓上游更新與專案內容彼此隔離。

操作
mkdir -p $HOME/buildroot/board/veos/rootfs-overlay/etc/init.d mkdir -p $HOME/buildroot/board/veos/rootfs-overlay/etc/network # 完成後的目錄結構 board/veos/ ├── veos-sut.c # 受測應用原始碼 ├── post-build.sh # 以 target toolchain 編譯受測應用 ├── linux-veos.fragment # kernel config 片段(僅 icount 有效) └── rootfs-overlay/etc/ ├── inittab # 早期掛載 devtmpfs + 移除 getty ├── veos-sut.conf # 受測應用參數 ├── network/interfaces # 靜態 IP └── init.d/S90veos-sut # 開機自動啟動受測應用
驗證
# 權限:腳本必須可執行 chmod 755 board/veos/post-build.sh board/veos/rootfs-overlay/etc/init.d/S90veos-sut chmod 644 board/veos/rootfs-overlay/etc/inittab \ board/veos/rootfs-overlay/etc/veos-sut.conf \ board/veos/rootfs-overlay/etc/network/interfaces # 換行字元:從 Windows 複製過來的檔案務必是 LF file board/veos/post-build.sh board/veos/rootfs-overlay/etc/init.d/S90veos-sut # 預期:不出現 "with CRLF line terminators" # 若出現:sed -i 's/\r$//' <檔案>

CRLF 是本步驟最常見的失敗原因,症狀為建置時出現 bad interpreter: /bin/sh^M。它不會在複製階段報錯,只會在建置或開機時才顯現。

STEP 4 / 8

四個 overlay 檔案的內容與理由

這四個檔案是鏡像從「可開機的 Linux」變成「V-ECU」的關鍵。每一項都對應一個實測到的具體失敗。

(a) etc/inittab——最關鍵的一個檔案

做兩件事:早期掛載 devtmpfs 與 移除 getty。

::sysinit:/bin/mount -t proc proc /proc ::sysinit:/bin/mount -o remount,rw / ::sysinit:/bin/mount -t devtmpfs devtmpfs /dev ← 新增,且必須在此位置 ::sysinit:/bin/mkdir -p /dev/pts /dev/shm ::sysinit:/bin/mount -a ::sysinit:/bin/mkdir -p /run/lock/subsys ::sysinit:/sbin/swapon -a null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr ::sysinit:/bin/hostname -F /etc/hostname ::sysinit:/etc/init.d/rcS # 原本的兩行 getty 已移除:console 屬於受測應用 ::shutdown:/etc/init.d/rcK ::shutdown:/sbin/swapoff -a ::shutdown:/bin/umount -a -r
  • 順序有意義:devtmpfs 必須在 mkdir -p /dev/pts /dev/shm 之前,否則那兩個目錄會被隨後掛上的 devtmpfs 遮蔽。在 kernel 已掛載過的情況下重複掛載是無害的(同一個 superblock)。
  • 為何移除 getty:在 dsQEMU 下 /dev/console 不是操作員終端,而是 guest 唯一的 log 通道(VEOS 將其擷取為 <Name>.qemu.stdout.log)。getty 會呼叫 vhangup() 取得該 tty 所有權,使其他行程的 console 檔案描述子失效——受測應用會靜默地停止輸出。這是最容易被誤判為「應用沒啟動」的失敗模式:判斷方法是觀察對端節點的接收計數是否仍在增加。
  • 為何用 overlay 而非 BR2_TARGET_GENERIC_GETTY=n:後者是在 target-finalize 階段以 sed 加入 getty 行,關閉它並不會移除 output/target/etc/inittab 中已存在的那一行,需重建 skeleton 或 make clean。overlay 在 rootfs 產生階段整檔覆蓋,只需 make 即生效。

(b) etc/network/interfaces——靜態位址

auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.60.10 netmask 255.255.255.0

必須靜態的原因:VEOS 的 Ethernet cluster 內沒有 DHCP server。保留預設設定時 S40network 會執行 udhcpc 廣播並等到逾時:

Starting network: udhcpc: started, v1.38.0 udhcpc: broadcasting discover udhcpc: no lease, forking to background

在 icount 模式下,這段等待是真實的實時成本,每次開機都要支付;更根本的是沒有位址就無法通訊。另需注意:Buildroot 的 kernel 預設沒有 CONFIG_IP_PNP,因此無法用 kernel 命令列 ip=... 就地指定位址——這正是 STEP 5 加入 config fragment 的動機。

(c) etc/veos-sut.conf——受測應用參數

PEER_IP=192.168.60.20 PEER_PORT=30490 LOCAL_PORT=30490 PERIOD_MS=10 REPORT_EVERY=100

將行為參數外置為設定檔,使「改對端位址」或「改週期」不需要重新編譯應用;命令列旗標可覆蓋檔案值。

(d) etc/init.d/S90veos-sut——開機自動啟動

編號 90 的理由:必須排在 S40network 之後,確保 eth0 已取得位址。實測鏡像內的執行順序為 S01seedrng → S01syslogd → S02klogd → S02sysctl → S11modules → S40network → S50crond → S50dropbear → S90veos-sut。

# 由 kernel 命令列決定行為,切換不需重建鏡像 for arg in $(cat /proc/cmdline 2>/dev/null); do case "$arg" in veos.gdb=1) USE_GDBSERVER=1 ;; veos.gdbport=*) GDB_PORT="${arg#veos.gdbport=}" ;; esac done case "$1" in start) # sysfs 是 veos-start 的前提 mountpoint -q /sys 2>/dev/null || mount -t sysfs sysfs /sys 2>/dev/null printf 'Starting veos-sut: ' # 刻意把 stdout 導向 console:console 是 guest 唯一的輸出通道, # VEOS 會擷取它。start-stop-daemon -b 會把它丟進 /dev/null。 if [ "$USE_GDBSERVER" = "1" ] && [ -x /usr/bin/gdbserver ]; then /usr/bin/gdbserver ":$GDB_PORT" "$DAEMON" > /dev/console 2>&1 & else "$DAEMON" > /dev/console 2>&1 & fi echo $! > "$PIDFILE" # idle-detect 搭配 veos.clock_noinit 時,應用就緒後才放行模擬時鐘; # veos-start 不存在時整段跳過,因此在 icount 下無害。 if command -v veos-start >/dev/null 2>&1; then veos-start fi ;; esac

不要使用 start-stop-daemon -b。 -b(background)會把子行程的 stdout 導向 /dev/null,造成與 getty 相同的症狀——應用確實在執行,但輸出永遠不會出現在 log 裡。

(e) post-build.sh——單趟建置編出受測應用

以 Buildroot 剛建立的 target toolchain 編譯受測應用並安裝至 /opt/sut/。採用 post-build hook 而非「預先編好放進 overlay」的原因是:make clean 會連 toolchain 一併刪除,預編路線需要兩趟建置;post-build hook 在 rootfs 組裝前執行,此時 toolchain 已存在,因此單趟建置即可完成。

#!/bin/sh set -e TARGET_DIR="$1" # Buildroot 以 $1 傳入 TARGET_DIR SRC_DIR="$(dirname "$0")" # 不硬寫 x86_64-buildroot-linux-gnu-gcc,換架構時不需修改本腳本 CC="" for candidate in "${HOST_DIR}"/bin/*-linux-gnu-gcc "${HOST_DIR}"/bin/*-linux-gcc; do if [ -x "${candidate}" ]; then CC="${candidate}"; break; fi done [ -n "${CC}" ] || { echo "no target gcc under ${HOST_DIR}/bin" >&2; exit 1; } mkdir -p "${TARGET_DIR}/opt/sut" "${CC}" -g -O2 -Wall -Wextra -std=c99 \ -o "${TARGET_DIR}/opt/sut/veos-sut" "${SRC_DIR}/veos-sut.c" echo "veos post-build: installed ${TARGET_DIR}/opt/sut/veos-sut"

-g 是刻意保留的:遠端 GDB 除錯時,符號解析由 GDB host 端所保存的同一份 binary 提供。

STEP 5 / 8

kernel config fragment(僅 icount 模式有效)

此片段合併於 Buildroot 的 kernel defconfig 之上。內容刻意保持最小——因為在 idle-detect 模式下這顆 kernel 根本不會被載入。

操作
# board/veos/linux-veos.fragment # 讓位址可由 JSON 的 kernel 命令列指定,例如 # "-append", "... ip=192.168.60.10::192.168.60.20:255.255.255.0::eth0:off" # 改 guest 位址因此不再需要重建 rootfs。 CONFIG_IP_PNP=y # 僅靜態指定,不啟用 DHCP/BOOTP/RARP:cluster 內沒有 DHCP server, # 探測只會拖長開機時間。 # CONFIG_IP_PNP_DHCP is not set # CONFIG_IP_PNP_BOOTP is not set # CONFIG_IP_PNP_RARP is not set
驗證
grep -E 'CONFIG_IP_PNP' output/build/linux-*/.config # 預期:CONFIG_IP_PNP=y,且三個子選項為 not set

修改 fragment 之後必須執行 make linux-rebuild。變更 BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES 不會讓 Buildroot 認為 kernel 已過期,單純 make 會沿用舊 kernel 並回報成功。

STEP 6 / 8

套用 .config

下表為完整變更清單。使用 utils/config 而非 menuconfig,使整個組態可腳本化、可重現、可納入版本控制。

符號 值 理由
BR2_TOOLCHAIN_BUILDROOT_CXXy前提條件:gdb 的 Config.in 要求 BR2_INSTALL_LIBSTDCPP,而它只由 C++ toolchain 提供
BR2_PACKAGE_GDBygdb 套件(會 select gdbserver)
BR2_PACKAGE_GDB_SERVERyguest 端 gdbserver,遠端除錯用
BR2_PACKAGE_IPROUTE2y靜態位址設定;行為較 BusyBox 內建 ip 一致
BR2_PACKAGE_ETHTOOLy虛擬網卡診斷
BR2_PACKAGE_TCPDUMPyguest 端封包擷取
BR2_PACKAGE_STRACEy追蹤受測應用的 syscall
BR2_PACKAGE_SOCATysocket 轉接
BR2_PACKAGE_DROPBEARy救援用 shell(經 TAP 或 slirp 進入)
BR2_TARGET_ROOTFS_EXT2_SIZE"512M"原 60 MB 容不下新增套件
BR2_ROOTFS_OVERLAYboard/veos/rootfs-overlay置入 VEOS 專用檔案
BR2_ROOTFS_POST_BUILD_SCRIPT兩個路徑附加於官方 hook 之後,編譯受測應用
BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILESboard/veos/linux-veos.fragment套用 STEP 5 的 kernel 片段
操作
cd $HOME/buildroot C=./utils/config $C --file .config --enable BR2_TOOLCHAIN_BUILDROOT_CXX $C --file .config --enable BR2_PACKAGE_GDB $C --file .config --enable BR2_PACKAGE_GDB_SERVER $C --file .config --enable BR2_PACKAGE_IPROUTE2 $C --file .config --enable BR2_PACKAGE_ETHTOOL $C --file .config --enable BR2_PACKAGE_TCPDUMP $C --file .config --enable BR2_PACKAGE_STRACE $C --file .config --enable BR2_PACKAGE_SOCAT $C --file .config --enable BR2_PACKAGE_DROPBEAR $C --file .config --set-str BR2_TARGET_ROOTFS_EXT2_SIZE "512M" $C --file .config --set-str BR2_ROOTFS_OVERLAY "board/veos/rootfs-overlay" $C --file .config --set-str BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES \ "board/veos/linux-veos.fragment" # 注意:這是「附加」而非「取代」——原值是官方的 qemu 設定腳本, # 若直接覆蓋掉,Buildroot 對 qemu target 的設定動作就不會執行。 $C --file .config --set-str BR2_ROOTFS_POST_BUILD_SCRIPT \ "board/qemu/x86_64/post-build.sh board/veos/post-build.sh" make olddefconfig # 解析相依性,不會互動詢問
驗證
grep -E '^(BR2_TOOLCHAIN_BUILDROOT_CXX|BR2_INSTALL_LIBSTDCPP|BR2_PACKAGE_GDB|BR2_PACKAGE_GDB_SERVER|BR2_ROOTFS_OVERLAY|BR2_ROOTFS_POST_BUILD_SCRIPT|BR2_TARGET_ROOTFS_EXT2_SIZE|BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES)=' .config # 重點確認兩件事: # 1. BR2_INSTALL_LIBSTDCPP=y 已被自動帶入(否則 gdb 不會被建置) # 2. BR2_ROOTFS_POST_BUILD_SCRIPT 內含「兩個」以空白分隔的路徑

若偏好互動式設定,對應的 make menuconfig 路徑為:Toolchain → Enable C++ support、Target packages → Debugging, profiling and benchmark → gdb → gdbserver、Target packages → Networking applications → iproute2 / ethtool / tcpdump / socat / dropbear、Filesystem images → ext2/3/4 root filesystem → exact size、System configuration → Root filesystem overlay directories、System configuration → Custom scripts to run before creating filesystem images。

STEP 7 / 8

執行建置

首次建置必須完整重建,因為 Buildroot 無法增量重新配置 toolchain;後續僅改動 overlay 或應用原始碼時,增量建置即可。

操作
# 首次(或曾變更 BR2_TOOLCHAIN_BUILDROOT_CXX):完整重建,約 30 分鐘 make clean && make 2>&1 | tee $HOME/veos-build-logs/buildroot.log # 後續僅改 overlay / 應用原始碼:增量建置,約 1–2 分鐘 make # 僅改 kernel fragment: make linux-rebuild && make
驗證
grep 'veos post-build' $HOME/veos-build-logs/buildroot.log # 預期:veos post-build: installed .../output/target/opt/sut/veos-sut ls -la output/images/ # 預期:bzImage 約 6.6 MB、rootfs.ext2 = 536,870,912 B(512 MB)

變更 C++ toolchain 後只執行 make 會產生不一致的 output。反之,在 toolchain 未變動時執行 make clean 只是白花時間重建未變更的 toolchain——請依實際變更範圍選擇。

STEP 8 / 8

鏡像內容驗證(不需開機)

這是整個 Build Server 階段最有價值的一個步驟:用 debugfs 直接檢查 ext2 映像的內容,在進入模擬之前就確認每一項配置真的進到鏡像裡了。少了這一步,配置錯誤只會在開機階段以間接症狀出現。

操作+驗證
IMG=output/images/rootfs.ext2 # 1. 受測應用是否安裝 debugfs -R 'ls -l /opt/sut' $IMG # 預期:veos-sut 存在(約 30 KB,模式 100755) # 2. 靜態位址是否生效 debugfs -R 'cat /etc/network/interfaces' $IMG # 預期:iface eth0 inet static / address 192.168.60.10 # 3. getty 是否已移除(這一項最容易被忽略) debugfs -R 'cat /etc/inittab' $IMG | grep -c getty # 預期:0 # 4. devtmpfs 是否在 inittab 內且位置正確 debugfs -R 'cat /etc/inittab' $IMG | grep -n -E 'devtmpfs|/dev/pts' # 預期:devtmpfs 那一行的行號小於 /dev/pts 那一行 # 5. 啟動腳本是否存在且可執行 debugfs -R 'ls -l /etc/init.d' $IMG | grep veos-sut # 預期:S90veos-sut 存在,模式 100755 # 6. 工具是否齊備 for f in usr/bin/gdbserver usr/bin/socat usr/bin/tcpdump \ sbin/ip usr/bin/strace usr/sbin/ethtool; do printf '%-24s ' "$f" debugfs -R "stat /$f" $IMG 2>/dev/null | grep -q 'Inode:' \ && echo present || echo MISSING done # 預期:六項全部 present

4. 鏡像產出後:模擬環境端的配置

鏡像本身完成後,還需要在模擬主機上完成四件事才能執行:部署與基準保存、撰寫 JSON 連接配置、匯入產生 OSA、載入並執行。

STEP A

部署鏡像並保留乾淨母版

guest 以 rw 掛載 rootfs,每執行一次就會寫入(log、SSH host key、隨機種子)。若不還原,每次測試的起點都不同,問題將難以重現。

操作
# 模擬主機上建立工作目錄(全部在使用者家目錄下,不需 sudo) mkdir -p ~/dsqemu/{buildroot,configs,osa,log} # 從 Build Server 取得兩個檔案 scp <build-server>:~/buildroot/output/images/bzImage ~/dsqemu/buildroot/ scp <build-server>:~/buildroot/output/images/rootfs.ext2 ~/dsqemu/buildroot/ # 保留乾淨母版 cp -f ~/dsqemu/buildroot/rootfs.ext2 ~/dsqemu/buildroot/rootfs.ext2.golden
驗證
# 在兩端各執行一次並比對雜湊值——傳輸階段的靜默損毀只有這樣才抓得到 sha256sum ~/dsqemu/buildroot/bzImage ~/dsqemu/buildroot/rootfs.ext2 # 預期:與 Build Server 端的輸出逐字相符

若鏡像尺寸較大,也可改用 qcow2 加 backing file,使每輪只需丟棄薄薄一層差異檔:

QIMG=/opt/dspace/veos2026a/simulator/extensions/qemu/bin/veos-qemu-img $QIMG convert -f raw -O qcow2 rootfs.ext2 rootfs.qcow2 $QIMG create -f qcow2 -F qcow2 -b rootfs.qcow2 run.qcow2
STEP B

撰寫 icount 模式的 JSON 配置

dsQEMU 的 V-ECU 由一份 Type 為 DsVeosQemu 的 JSON 描述,經 veos model import 轉為 OSA。以下為可直接使用的完整範例。

操作 — configs/br-x86_64-icount.json
{ "$schema": "file:///opt/dspace/veos2026a/tools/schemas/DsVeosQemu.schema.json", "Type": "DsVeosQemu", "Name": "BuildrootX8664-icount", "StepSize": 0.001, "MetaData": { "Author": "JOTACTIC", "Description": "Buildroot as POSIX-based V-ECU, icount mode. No KVM required." }, "Network": { "ETH": { "eth0": { "ClusterName": "EthBackbone", "MacAddress": "9a:a2:36:d2:90:10", "BitsPerSecond": 1000000000, "Qemu": { "OptionKind": "nic", "Arguments": "model=virtio-net-pci,id=vnic0" } } } }, "Veos": { "SynchronizationMode": "icount", "RemotePort": 0 }, "Qemu": { "Architecture": "x86_64", "AllowOverridingDefaultQemuArgs": false, "Arguments": [ "-name BuildrootX8664-icount", "-M pc", "-m 512", "-smp 1", "-kernel ${Resource:bzImage}", "-drive if=virtio,file=${Resource:rootfs.ext2},format=raw", "-append", "root=/dev/vda rootwait rw console=ttyS0 tsc=reliable no_timer_check panic=-1" ], "RedirectStdout": "both", "RedirectStderr": "both" }, "Resources": [ "/home/<user>/dsqemu/buildroot/bzImage", "/home/<user>/dsqemu/buildroot/rootfs.ext2" ] }
欄位 說明與注意事項
NameVPU 名稱,不可含空白。同時決定 log 檔名 <Name>.qemu.stdout.log
StepSize同步時間步(秒)。建議與 vECU 的 base step 及 veos sim config -e 三者一致,否則會出現封包丟棄或抖動
ClusterName要接入的 VEOS Ethernet cluster 名稱。大小寫敏感;多個 JSON 使用相同名稱時,匯入階段會自動建立 cluster 與 channel 並完成接線
MacAddress預設值為全零,務必自訂且每節點唯一,否則 cluster 上會位址衝突
Qemu.Arguments(ETH)一律寫 model=virtio-net-pci。veos-linux kernel 不含 e1000 驅動,用預設型號在 idle-detect 下 guest 會看不到網卡
Resources絕對路徑清單,匯入時嵌入 OSA,以 ${Resource:<basename>} 引用。不支援相對路徑;單檔超過 1 GB 不建議嵌入
RedirectStdout / Stderrlog / file / both。建議 both,使 console 同時進入 VEOS log 與獨立檔案

OSA 會把鏡像嵌入。重新部署鏡像之後若未重建 OSA,執行的仍是舊 guest,而且不會有任何警告。凡是更新鏡像,就要重新 veos model import。

驗證
# JSON 語法與 schema:schema 檔隨 VEOS 安裝,可直接引用 python3 -c "import json;json.load(open('configs/br-x86_64-icount.json'));print('JSON OK')" # 建議另外以專案規則自我檢查(可自動化): # · Name 不含空白 # · ETH 參數含 model=virtio-net-pci # · MacAddress 不是預設全零值 # · idle-detect 配置未自訂 -kernel / -cpu / -smp / -enable-kvm / -rtc / -veos
STEP C

撰寫 idle-detect 模式的 JSON 配置

與 icount 版本相比只有四處差異,但每一處都是必要的。

差異 原因
移除 -kernel 與 -smpVEOS 自行指定 veos-linux kernel 並強制 -smp 1。自訂這些參數會被拒絕
加入 -serial stdio該模式 VEOS 會加 -nodefaults,取消預設 serial 裝置。不加則 console 全黑,模擬看似正常但一行 guest 輸出都沒有
加入 virtio-rng 裝置veos-linux 不含 CONFIG_RANDOM_TRUST_CPU,決定性模擬環境也沒有中斷抖動可供收集熵,CRNG 將永不初始化。實測 random.trust_cpu=on 無效
Resources 只留 rootfsguest kernel 不再被使用,無須嵌入
操作 — configs/br-x86_64-idle-detect.json(節錄差異段)
"Veos": { "SynchronizationMode": "idle-detect", "RemotePort": 0 }, "Qemu": { "Architecture": "x86_64", "AllowOverridingDefaultQemuArgs": false, "Arguments": [ "-name BuildrootX8664-idledetect", "-M pc", "-m 512", "-drive if=virtio,file=${Resource:rootfs.ext2},format=raw", "-object rng-random,filename=/dev/urandom,id=rng0", "-device virtio-rng-pci,rng=rng0", "-serial stdio", "-append", "root=/dev/vda rootwait rw console=ttyS0 random.trust_cpu=on panic=-1" ], "RedirectStdout": "both", "RedirectStderr": "both" }, "Resources": [ "/home/<user>/dsqemu/buildroot/rootfs.ext2" ]

VEOS 自動加入與禁止覆寫的參數

類別 icount idle-detect
未指定時自動加入-nographic、網卡選項同左,另加 -no-reboot、-nodefaults
VEOS 控制(需明確允許才可覆寫)-icount、-rtc clock=vm、-accel tcg,thread=single-rtc base=epoch,clock=vm、-kernel <veos-linux>、-cpu host
絕對禁止自訂-veos-veos、-S、-enable-kvm、-smp 1
VEOS 追加的 kernel 參數—hpet=disable tsc=reliable nohz=off veos.clock_tick_ns=<StepSize>(勿重複設定)
驗證
# 前置條件:idle-detect 需要 KVM。三項缺一不可。 grep -c -E ' vmx| svm' /proc/cpuinfo # 預期 > 0(虛擬機需啟用巢狀虛擬化) ls -l /dev/kvm # 預期:裝置存在 python3 -c "open('/dev/kvm','rb').close();print('openable')" # 預期:openable。若 Permission denied,執行 VEOS 隨附的權限設定腳本 # sudo /opt/dspace/veos2026a/simulator/extensions/qemu/util/configure.sh <user> # 之後必須「登出再登入」,重開終端機不足夠。

兩種 KVM 錯誤訊息要分清楚:
Could not access KVM kernel module: No such file or directory → /dev/kvm 根本不存在,屬於虛擬機/BIOS 設定問題,且變更後需完整關機再開機(guest 內 reboot 無效)。
... Permission denied → 裝置存在但無權限,屬於權限設定問題。

STEP D

匯入產生 OSA 並確認拓撲

操作
source /opt/dspace/veos2026a/tools/shell/veos-env.sh # 提示字元變為 (dspace-veos) cd ~/dsqemu # 清場(每輪測試前) veos sim down < /dev/null cp -f buildroot/rootfs.ext2.golden buildroot/rootfs.ext2 # 匯入第一個節點(-n 建立新 OSA) veos model import osa/test.osa -n \ -p configs/br-x86_64-icount.json \ -o osa/test.osa -v Info < /dev/null # 需要雙節點聯測時,再匯入第二個(不加 -n,併入同一 OSA) veos model import osa/test.osa \ -p configs/yocto-x86_64-icount.json -o osa/test.osa < /dev/null

< /dev/null 不可省略。VEOS Model Console 會檢查 stdin 是否帶有修改輸入,透過腳本或 SSH 非互動呼叫時,省略它會讓指令一直等待到連線逾時。互動式終端機下不受影響。

驗證
veos model get osa/test.osa < /dev/null | jq ' { VPUs: [.Vpus[].Name], Clusters: [.BusClusters[] | {name:.Name, channels:[.Channels[].Name]}], Connectors: [.Vpus[] | {vpu:.Name, conns:[.BusConnectors[] | {name:.Name, ch:.ChannelReference}]}] }'

通過條件:所有 VPU 都在、存在對應的 cluster、且每個 eth0 connector 的 ChannelReference 不是 null。實測正確的樣子:

Clusters: [('EthBackbone', ['EthBackbone'])] Connectors: BuildrootX8664-icount/eth0 -> /BusClusters[EthBackbone]/Channels[EthBackbone] YoctoQemux8664-icount/eth0 -> /BusClusters[EthBackbone]/Channels[EthBackbone]

只要各 JSON 的 ClusterName 相同,不需要手動執行 model add 或 model connect——匯入階段會自動建立 cluster、channel 並完成接線。僅在加入非 JSON 來源的參與者(例如 SystemDesk 產出的 Classic vECU)時,才需要 veos model connect --autoconnect-communication-controllers。

STEP E

載入、執行與觀察

操作
veos sim config --enable-sim-log --enable-bus-log -e 0.001 < /dev/null veos sim load osa/test.osa < /dev/null veos sim start 90s < /dev/null # 觀察 veos sim info < /dev/null | grep -E 'State|SimulationTime|ElapsedTime' veos sim show-log # 列出可用 log veos sim show-log BuildrootX8664-icount.qemu.stdout.log # 收場 veos sim pause < /dev/null veos sim down < /dev/null
載入成功的關鍵輸出
INFO License was acquired: VEOS - Base, ... INFO License was acquired: VEOS - AUTOSAR Adaptive Platform, ... INFO Configuration: Synchronization mode 'icount' INFO Run QEMU executable: .../veos-qemu-system-x86_64 -icount "shift=1,..." ... INFO The following log file was created: .../BuildrootX8664-icount.qemu.stdout.log INFO Simulation loaded.

請保留 Run QEMU executable: 那一整行。它是整個流程中最有價值的診斷資訊——顯示 VEOS 最終組出的完整命令列(您的參數加上 VEOS 追加的參數)。網卡型號、同步模式、kernel 路徑、追加的 kernel 參數,全部可從這一行確認。

5. 驗證判準

驗證要成立,判準必須事前訂好、可觀測、且能區分「看起來過了」與「真的過了」。以下判準均已在實機環境驗證通過。

5.1 icount 模式:開機與應用執行

# 判準 通過條件 失敗時檢查
1.1載入成功輸出含 Simulation loaded.ERROR 行、.qemu.stderr.log
1.2同步模式正確含 Synchronization mode 'icount'JSON 的 Veos.SynchronizationMode
1.3網卡型號正確Run QEMU 行含 model=virtio-net-pciJSON 的 ETH Qemu.Arguments
1.4rootfs 掛載成功console 含 VFS: Mounted root ... on device-drive 參數、鏡像完整性
1.5到達 userspaceRun /sbin/init as init process 之後仍有輸出見 §6 失敗模式
1.6網路已配置console 不含 udhcpc: no lease/etc/network/interfaces
2.1應用啟動console 含應用的啟動行(監聽埠、對端、週期)S90veos-sut、/opt/sut/
2.2週期正確guest 時間 ÷ 傳送計數 = 設定週期veos-sut.conf 的 PERIOD_MS
2.3時鐘由 VEOS 驅動連續兩行報告的時間差 = REPORT_EVERY × PERIOD_MS同步模式、StepSize
2.4加速比合理SimulationTime / ElapsedTime ≈ 0.9–1.0主機負載、guest 內忙迴圈
Buildroot 通過時的 console 樣貌
EXT4-fs (vda): mounting ext2 file system using the ext4 subsystem VFS: Mounted root (ext2 filesystem) on device 254:0. Run /sbin/init as init process Starting syslogd: OK Starting klogd: OK Starting network: OK Starting dropbear sshd: OK Starting veos-sut: OK veos-sut: start listen=:30490 peer=192.168.60.20:30490 period=10ms veos-sut: t=73001ms tx=7300 rx=6014 sensor=2049 veos-sut: t=74001ms tx=7400 rx=6114 sensor=4092 veos-sut: t=75001ms tx=7500 rx=6214 sensor=2045

EXT4-fs (vda): mounting ext2 file system using the ext4 subsystem 這一行本身就是一項驗證:它證實了「Buildroot 的 ext2 映像由 ext4 驅動掛載」這條路徑成立(EXT4_USE_FOR_EXT2),不需要另外提供 ext2 驅動。

5.2 idle-detect 模式:額外的五項判準

# 判準 通過條件
5.1hypervisor 參數已加入Run QEMU 行含 -S -enable-kvm -smp 1 -cpu host -kernel <veos-linux.bzImage>
5.2真的使用 VEOS kernelconsole 含 Linux version 6.6.119-...-veos26.1.0
5.3veos-clock 生效console 含 Entering VEOS simulator mode,且 veos-qemu 印出 Found idle breakpoint
5.4/dev 已填充console 不含 can't open /dev/null(此即 STEP 4 (a) 的直接驗證)
5.5加速比接近 1.000完全時間同步的特徵。實測 SimulationTime 90.4 / ElapsedTime 90.401
5.6雙節點 lockstep兩個 guest 報告的時間完全相同,且雙向計數相等(零封包遺失)

判準 5.6 是兩種模式最本質的差別。icount 下兩個 guest 各自推進自己的時間;idle-detect 下它們共用 VEOS 的模擬時鐘。這一項不成立,就不是真正的完全同步——即使其他判準全部通過。

### BuildrootX8664-idledetect veos-sut: t=83000ms tx=8300 rx=8300 sensor=2047 ### YoctoQemux8664-idledetect veos-sut: t=83000ms tx=8300 rx=8300 sensor=2047

6. 失敗模式與判讀

dsQEMU 環境中多數失敗不會產生錯誤訊息,只會表現為「某個數字不對」或「某段輸出不見了」。下表為實測整理的對照。

症狀 根因 處理
can't open /dev/null ×N,應用無輸出devtmpfs 未掛載(idle-detect 的 chroot 路徑)inittab 第一個 sysinit 動作掛 devtmpfs
應用無輸出,但對端接收計數持續增加console fd 被搶走:getty 的 vhangup(),或 start-stop-daemon -b 把 stdout 導向 /dev/null移除 getty;啟動腳本改為 > /dev/console 2>&1 &
console 完全空白,但 Found idle breakpoint 已出現idle-detect 的 -nodefaults 取消了預設 serial 裝置JSON 加 -serial stdio
卡在 Initializing random number generator...無熵源,CRNG 永不初始化JSON 加 virtio-rng 裝置(或移除所有熵消費者)
開機時出現 udhcpc: no leasecluster 內沒有 DHCP serveroverlay 設定靜態位址
guest 看不到網卡使用了預設 e1000,veos-linux 無此驅動JSON 寫 model=virtio-net-pci,並從 Run QEMU 行確認
接收計數恆為 0cluster 未接通、MAC 重複、對端位址不互指,或對端未開機依序檢查 ChannelReference、ClusterName 大小寫、MAC 唯一性、對端 console
收送計數差距持續擴大對端當機或封包被丟棄看對端 console 最後幾行;匯出 bus log 分析
收送計數差距固定不變不是故障:兩節點開機時間差造成的偏移差值 ÷ 每秒封包數 應約等於開機時間差
veos model 指令沒有回應Model Console 在等 stdin 的修改輸入加 < /dev/null
改了鏡像但行為沒變OSA 內嵌的是舊鏡像重新 veos model import
每次執行結果不同rootfs 未還原 golden;或 rng-random 引入主機隨機性每輪還原母版;需位元級重現時改用內建 rng 或移除熵消費者
建置時 bad interpreter: /bin/sh^M從 Windows 複製的腳本是 CRLFsed -i 's/\r$//' <檔案>

6.1 先判斷是「慢」還是「死結」

開機停住時,第一件事不是看 console,而是確認模擬時間有沒有在前進:

veos sim info < /dev/null | grep -E 'SimulationTime|ElapsedTime' # 間隔 60 秒再執行一次 # · SimulationTime 有增加 → 模擬在跑,是 guest 內部卡住 # · kernel printk 時間戳不動 → 真的死結

實測經驗值可作為量級參考:一個真實的 guest 內死結,曾出現「300 秒實時只推進 2.55 秒 guest 時間」的比例。這與「單純比較慢」的量級差距非常明顯,據此即可分流後續診斷方向。

7. 回歸測試與工程化建議

01

組態即程式碼

以 utils/config 而非互動式 menuconfig 套用設定,讓整份 .config 變更可腳本化、可審閱、可納入版本控制。overlay、應用原始碼、JSON 配置一併版控。

02

先驗鏡像,再進模擬

以 debugfs 在開機前確認鏡像內容(STEP 8)。這能把「配置沒進鏡像」與「模擬環境配置錯誤」兩類問題徹底分離,省下大量診斷時間。

03

用 JSON 換行為

把 gdbserver 開關、應用選擇、位址等做成讀取 kernel 命令列的參數。切換測試情境只需改 JSON,不需重建鏡像——單次迭代由 30 分鐘降到數秒。

每次變更鏡像設定或 JSON 之後,建議至少執行以下三項回歸:

  • 配置檔驗證以原廠 schema 檢查所有 JSON,並加上專案自訂規則(名稱不含空白、網卡型號、MAC 非預設值、idle-detect 未自訂受管參數)。
  • 雙節點 icount 聯測兩個 guest 掛入同一 Ethernet cluster,確認雙向通訊與封包計數關係成立——這一項同時涵蓋開機、應用、網路與拓撲。
  • rootfs × veos-linux kernel 相容性探測在 icount 模式下以 veos-linux kernel 開機自家 rootfs。這是一個很有價值的技巧:由於 icount 允許指定任意 kernel,該組合測到的正是 idle-detect 的鏡像條件,因此能在不具備 KVM 的環境下,先把「鏡像相容性」與「主機能力」兩件事分離。

結語

把 Buildroot 鏡像帶進 VEOS QEMU Coupling,工作量並不在於「改很多設定」,而在於理解每個設定對應哪一個具體失敗。本文的配置清單並不長——一份 .config、一個 kernel fragment、四個 overlay 檔案、一支 post-build 腳本、兩份 JSON——但每一項都源自一個實測到的症狀,而多數症狀不會以錯誤訊息的形式出現。

這也是為什麼本文堅持每個步驟都附上驗證方法:在虛擬 ECU 的整合環境裡,「沒有報錯」從來不等於「配置正確」。建立一套可重複執行、判準事前訂好的驗證流程,是讓 SIL 測試結果可信的前提。

需要導入協助?

捷策動能提供 dSPACE VEOS/SystemDesk 虛擬驗證平台的導入規劃、環境建置、鏡像客製與 SIL 測試流程設計服務,涵蓋 Buildroot/Yocto 客製 Linux、Classic AUTOSAR vECU、Restbus 模擬與 CI 整合。歡迎與我們的技術團隊討論您的專案需求。

本文技術內容基於 dSPACE VEOS 2026-A 實機環境驗證,所引用的行為與數值均為實測結果。相關原廠規範請參閱 dSPACE VEOS Manual「Basics on VEOS QEMU Coupling」與「How to Debug a POSIX-Based V-ECU Remotely with GDB」等章節。文中路徑、位址與名稱請依實際環境調整。