external_bootup

DUT 外部启动流程文档

概述

本文档基于 tcpe3_external_bootup.txt 日志文件和相关代码分析,详细梳理了 DUT(Device Under Test)从外部 Flash 冷启动到 Radio 应用完全就绪的完整流程。

核心理解:整个外部启动流程中,电源控制的对象始终是 DUT(无线电设备),而不是接口板。接口板(NIB/TCPE3)是独立供电、始终在线的设备,它只负责转发命令和控制 Flash 模式切换。


用户操作步骤总览

整个外部启动流程从用户视角可以分为以下几个大步骤:

步骤1: DUT断电 + 接口板切换到外部Flash模式 + DUT上电
步骤2: DUT从外部Flash自动启动(BootROM → Linux,无需干预)
步骤3: 等待DUT启动完成,登录DUT
步骤4: 配置DUT网络,建立SSH连接
步骤5: 接口板切换到内部Flash模式(DUT不断电,热切换)
步骤6: DUT上执行 flash-hotswap internal + prepare_onboard_flash
步骤7: 启动Radio应用(mamastart + waitradiohal)

关键区别:步骤1 是"冷切换"(需要断DUT电),步骤5 是"热切换"(DUT保持运行)。


物理连接拓扑

┌──────────┐     GPIB/TCPIP      ┌──────────────┐     DC 56V      ┌─────────┐
│  测试PC  │ ──────────────────→ │  电源控制器   │ ──────────────→ │   DUT   │
│          │                     │  (GPIB addr:5)│                 │(hawkowl)│
│          │                     └──────────────┘                 │         │
│          │                                                      │         │
│          │     Telnet:3000     ┌──────────────┐   Flash总线     │         │
│          │ ──────────────────→ │  接口板       │ ←─────────────→ │         │
│          │     (控制端口)       │ (TCPE3/NIB)  │   (ext/int)    │         │
│          │                     │  独立供电     │                 │         │
│          │     Telnet:3001     │  始终在线     │   串口(UART)    │         │
│          │ ──────────────────→ │              │ ←─────────────→ │         │
│          │     (串口转发)       │              │                 │         │
│          │                     └──────────────┘                 │         │
│          │                                                      │         │
│          │     SSH:22          (配好IP后直连)                    │         │
│          │ ─────────────────────────────────────────────────────→│         │
│          │     (文件传输)                                        │         │
└──────────┘                                                      └─────────┘

连接说明:

  • 电源控制器 → DUT:直接给 DUT 供电(56V DC),控制 DUT 上电/断电
  • 接口板(独立供电,始终在线)
    • 控制 DUT 从外部还是内部 Flash 启动(Flash 总线切换)
    • 将 DUT 的物理串口通过 Telnet 端口暴露给测试 PC
  • 测试PC → DUT(SSH):DUT 配好 IP 后直连,用于 SCP 文件传输

详细流程分析

步骤1:DUT 断电 + 切换到外部 Flash 模式 + DUT 上电

用户操作:通过电源控制器断开 DUT 电源 → 通过接口板切换 Flash 模式为外部 → 给 DUT 上电。

对应代码: tcpe3.py - tcpe_switch_2_external_flash()

def tcpe_switch_2_external_flash(power_supply_com, interface_board_connection, logger):
    power_supply_com.power_off()                              # 1. 断开DUT电源
    sleep(2)
    # must power off first, then switch, power on at last
    tcpe_swtich_to_external_flash(logger, interface_board_connection)  # 2. 接口板切换到外部Flash
    power_supply_com.power_on()                               # 3. 给DUT上电

NIB 接口板版本:

def nib_switch_2_external_flash(power_supply_com, nib_telnet_connection, nib_ssh_connection, logger):
    power_supply_com.power_off()                              # 1. 断开DUT电源
    sleep(0.1)
    nib_swtich_to_external_flash(nib_telnet_connection, nib_ssh_connection, logger)  # 2. NIB切换到外部Flash
    power_supply_com.power_on()                               # 3. 给DUT上电

日志记录(DUT 上电部分):

[w5 SOUR:curr 20]       ← 设置DUT电流限制 20A
[w5 SOUR:VOLT 56]       ← 设置DUT电压 56V
[w5 OUTP:STAT 1]        ← 打开电源输出(DUT上电)
[w5 meas:curr?]         ← 测量DUT实际电流
[r5 5.36840E-1]         ← DUT实际电流 0.537A(DUT开始工作)

电源控制对象说明:

  • [w5 ...] 中的 5 是 GPIB 地址,对应产品配置中 'addr':'5'
  • 这个电源直接给 DUT 供电(56V DC)
  • 接口板是独立供电的,不受此电源控制

为什么必须先断电再切换?
代码注释:must power off first, then switch, power on at last, or external flash will be written some other content。如果不断电就切换 Flash 模式,可能会导致外部 Flash 被写入错误内容。

接口板发送的命令:

  • TCPE3: bm ext(boot mode external)
  • NIB: 通过 Telnet/SSH 发送切换命令

步骤2:DUT 自动启动过程(无需用户干预)

DUT 上电后,以下过程自动发生,用户只需等待(约32秒到达 login):

2a. BootROM 启动

H1.0
INFO: BootROM P/N: CAH1611693/7 A
INFO: bootmode: 00000001          ← 外部启动模式(由接口板的 bm ext 决定)
INFO: LCS: 00000005

2b. Flash 容器加载 + 安全验证

INFO: Flash container loaded from: 000C0000, type: 00000001
INFO: Jump to BL2/FSBL P/N: CXC0000000/0

2c. FSBL 启动 + 系统初始化

=== FSBL Start ===
[Runtime 24215 us]
System Watchdog configured
Global Watchdog configured

2d. Slot 选择

SW Selection: Current slot is 1
SW Selection: Continue the boot on this slot (1)
Try booting from slot 1

2e. PBOOT/XCS-CONFIG 验证

PBOOT (rif): CXP2010367/1 R1A844
XCS-CONFIG (sct): EXT2014511/5 R1A393
HBK verified ok / Signature verified / Hash verified Ok
hw profile: hawkowl_x2_air3286

2f. DDR 内存初始化

DDR PLL set to 2800MHz, data rate 5600MHz
Controller initialized / PI initialized / Quick check done

2g. SPL + U-Boot 启动

U-Boot SPL 2021.10-00034-g321698ce817
CHOSEN: boot-target: spi-nor0.1
CHOSEN: boot-mode: ramdisk

2h. Linux 内核启动 + 系统服务

[   31.995498] setup-debug-serdes.sh: Loading firmware and initializing SGMII SerDes
[   32.134948] Toggle eth0 interface down/up after SGMII SerDes initialization

步骤3:等待 DUT 启动完成,登录

用户操作:等待 DUT 出现 login 提示符,然后登录 root。

日志记录:

Wind River Linux LTS 24.33 Update 13 hawkowl ttyS0

hawkowl login: root
Welcome to Factory Application for hawkowl
root@hawkowl:~# 

对应代码: common/connections.py - TelnetConnection

# 创建 DUT 串口连接时,自动等待 login 提示符并登录
DUT_connection = TelnetConnection(serial_logger, 
    config.interface_board.ip_addr,          # 接口板IP(不是DUT IP)
    config.interface_board.serial_telnet_port, # 串口转发端口 3001
    f'{config.product.family} login')         # 等待 "hawkowl login"

TelnetConnection 的 initialize_connection() 方法会自动等待 login 并发送 "root":

def initialize_connection(self):
    for line in self.write("", wait_for=self.expect_str, timeout=180):
        if "login" in line:
            self.write("root")
            break

说明

  • Telnet 连接的目标是接口板(不是 DUT),接口板把 DUT 的物理串口转发为 Telnet 端口
  • TCPE3: 连接 192.168.2.71:3001(DUT 串口转发端口)
  • NIB: 连接 192.168.2.51:3003(DUT 串口转发端口)

步骤4:配置 DUT 网络,建立 SSH 连接

用户操作:给 DUT 配置 IP 地址,建立 SSH 连接用于后续文件传输。

日志记录:

root@hawkowl:~# ifconfig eth0 192.168.1.20

对应代码: common/radio.py - Radio.__bootstrap_ssh()

def __bootstrap_ssh(self):
    # 1. 通过串口检查 DUT 网络
    outp = self.serial_connection.write("ifconfig eth0")
    
    # 2. 如果没有 IP,通过串口配置
    if self.ip not in outp:
        if not self.external_boot or self.product.family != 'krypton':
            self.serial_connection.write(f"ifconfig eth0 {self.ip}")
        self.serial_connection.write("ping -c1 192.168.1.1")
    
    # 3. 建立 SSH 连接(用于后续文件传输)
    target_ip = self.interface_board.ip_addr if self.interface_board.type == InterfaceBoardType.NIB else self.ip
    client.connect(target_ip, port=22, username='root', password="", timeout=30)

为什么需要 SSH?

  • 串口只能发命令,不能传文件
  • SSH/SCP 用于传输 DC 文件等大文件到 DUT
  • 这是 Radio 类构造函数中 external_boot=True 时执行的最后一步,之后 return

步骤5:接口板切换到内部 Flash 模式(热切换)

用户操作:通过接口板将 Flash 模式从外部切换到内部。此时 DUT 不断电,保持运行状态。

对应代码: tcpe3.py - tcpe_swtich_to_internal_flash()

def tcpe_swtich_to_internal_flash(logger, tcpe_telent_connection):
    logger.info("switch to internal flash")
    tcpe_telent_connection.write("bm int", "COMMAND_OK")  # 接口板切换到内部Flash

关键区别:

步骤1(冷切换) 步骤5(热切换)
DUT 电源 需要断电再上电 保持运行,不断电
目的 让 DUT 从外部 Flash 启动 让 DUT 能访问内部 Flash
原因 DUT 启动时需要从外部 Flash 读取固件 DUT 已启动完成,Linux 在内存中运行,不再依赖外部 Flash

步骤6:DUT 上执行 Flash 热切换和板载 Flash 准备

用户操作:在 DUT 上执行一系列命令,让 DUT 识别并准备内部 Flash。

对应代码: tcpe3.py - tcpe_switch_2_internal_flash()

def tcpe_switch_2_internal_flash(interface_board_connection, DUT_connection, radio, loading_file_path):
    # 1. 获取当前硬件信息(切换前)
    DUT_connection.write("parget --flat /sys/hw/")
    DUT_connection.write("cat /proc/mtd")
    DUT_connection.write("getenv | grep mtd")
    
    # 2. 接口板切换到内部Flash(步骤5)
    tcpe_swtich_to_internal_flash(radio.logger, interface_board_connection)
    
    # 3. 通过SSH传输 DC 文件到 DUT
    dc_file_name = get_dc_files(f"{loading_file_path}/pis")
    radio.transfer_file(dc_file_name, f"/tmp/{os.path.basename(dc_file_name)}")
    
    # 4. DUT 上执行 flash-hotswap(让Linux识别内部Flash)
    DUT_connection.write("flash-hotswap internal")
    
    # 5. 准备板载Flash(根据产品型号创建分区)
    DUT_connection.write(f'prepare_onboard_flash "{radio.product.production_number}" "{radio.product.r_state}"')
    
    # 6. 加载MTD环境变量
    DUT_connection.write("cat /proc/mtd")
    DUT_connection.write("source /run/env/mtdenv")
    DUT_connection.reconnect()
    DUT_connection.write("getenv | grep mtd")
    
    # 7. hawkowl 产品:写入数据库到Flash
    if radio.get_family_name() == 'hawkowl':
        DUT_connection.write("dbtool -w -t radiodb")
        DUT_connection.write("dbtool -w -t ppar")

日志记录:

root@hawkowl:~# flash-hotswap internal
[  273.916682] spi-nor spi100.1: unrecognized JEDEC id bytes: 00 00 00 00 00 00

root@hawkowl:~# prepare_onboard_flash "KRD 901 316/2" "R1A"
KRD 901 316/2 R1A matches XCS_CONFIG_CXC2014511_5_P1C41
Creating FLASH partitions according to XCS-config...

root@hawkowl:~# dbtool -w -t radiodb
Radio DB size 283020
SHA256 of radiodb bc086d3c40618db12c0db19965fb1ca7a6faa8de2132bd8d626bd1cb77f53428

root@hawkowl:~# dbtool -w -t ppar
PPAR size 1624
SHA256 of ppar 5747481321ef9fa5783d35fb107a2a9491d15dd597b035afc72cb4bddae026b9

各命令作用:

命令 作用
flash-hotswap internal 让 Linux 内核重新扫描并识别内部 Flash 设备
prepare_onboard_flash "KRD 901 316/2" "R1A" 根据产品型号和 XCS-Config 创建 Flash 分区表
source /run/env/mtdenv 加载 MTD 环境变量
dbtool -w -t radiodb 写入 Radio 数据库到 Flash
dbtool -w -t ppar 写入 PPAR(产品参数)到 Flash

步骤7:启动 Radio 应用

用户操作:执行 mamastart 启动 Radio 应用,等待 HAL is ready

日志记录:

root@hawkowl:~# parget --flat /sys/hw/
/sys/hw/pid str "KRD 901 316/2"
/sys/hw/profile str "hawkowl_x2_air3286"
/sys/hw/serial str "EA8D726158"
...

root@hawkowl:~# mamastart
...
[ 1159.052307] radioapp.elf[14429]: Etsw Grpc Server listening on 0.0.0.0:50053
HAL is ready

AIR3286 SW started
Starting O&M interfaces

对应代码: common/radio.py - Radio.startup_mama()

def startup_mama(self):
    self.serial_connection.write("refresh_pid")              # 刷新产品ID
    self.serial_connection.write("db list /freqClassUsage")  # 查询频率配置
    self.serial_connection.write("cid")                      # 查询组件ID
    self.serial_connection.write("parget --flat /sys/hw/")   # 获取硬件信息
    self.serial_connection.write("mamastart")                # 启动Radio应用
    self.serial_connection.write("waitradiohal", wait_for='HAL is ready')  # 等待HAL就绪

启动成功标志:串口输出中出现 HAL is ready


完整流程时序图

时间轴
│
│  ┌─────────────────────────────────────────────────────────────┐
│  │ 步骤1: DUT断电 + 接口板切换外部Flash + DUT上电              │
│  │                                                             │
│  │  电源控制器 ──→ DUT断电 (OUTP:STAT 0)                       │
│  │  测试PC ──→ 接口板 ──→ "bm ext" (切换到外部Flash)           │
│  │  电源控制器 ──→ DUT上电 (SOUR:VOLT 56, OUTP:STAT 1)        │
│  └─────────────────────────────────────────────────────────────┘
│
│  ┌─────────────────────────────────────────────────────────────┐
│  │ 步骤2: DUT自动启动(无需干预,约32秒)                       │
│  │                                                             │
│  │  BootROM → FSBL → DDR初始化 → SPL → U-Boot → Linux         │
│  └─────────────────────────────────────────────────────────────┘
│
│  ┌─────────────────────────────────────────────────────────────┐
│  │ 步骤3: 登录DUT                                              │
│  │                                                             │
│  │  等待 "hawkowl login:" → 输入 "root"                        │
│  └─────────────────────────────────────────────────────────────┘
│
│  ┌─────────────────────────────────────────────────────────────┐
│  │ 步骤4: 配置网络 + 建立SSH                                   │
│  │                                                             │
│  │  串口 → "ifconfig eth0 192.168.1.20"                        │
│  │  测试PC → SSH连接DUT (用于后续文件传输)                      │
│  └─────────────────────────────────────────────────────────────┘
│
│  ┌─────────────────────────────────────────────────────────────┐
│  │ 步骤5: 接口板切换内部Flash(热切换,DUT不断电)              │
│  │                                                             │
│  │  测试PC ──→ 接口板 ──→ "bm int" (切换到内部Flash)           │
│  └─────────────────────────────────────────────────────────────┘
│
│  ┌─────────────────────────────────────────────────────────────┐
│  │ 步骤6: DUT上准备内部Flash                                   │
│  │                                                             │
│  │  SCP传输DC文件 → flash-hotswap internal →                   │
│  │  prepare_onboard_flash → dbtool写入数据库                   │
│  └─────────────────────────────────────────────────────────────┘
│
│  ┌─────────────────────────────────────────────────────────────┐
│  │ 步骤7: 启动Radio应用                                        │
│  │                                                             │
│  │  mamastart → waitradiohal → "HAL is ready" ✓               │
│  └─────────────────────────────────────────────────────────────┘
│
▼  DUT完全就绪,可以执行RF校准测试

为什么需要外部启动?

外部启动的场景是:DUT 的内部 Flash 是空的或损坏的

正常情况下,DUT 从内部 Flash 启动。但在以下场景需要外部启动:

  1. 全新设备:内部 Flash 没有任何固件
  2. Flash 损坏:内部 Flash 数据损坏,无法正常启动
  3. 重新烧录:需要重新初始化内部 Flash 的分区和数据

外部启动的本质是:

  1. 先从外部 Flash 启动一个最小 Linux 系统(外部 Flash 上有预烧好的固件)
  2. 在这个系统中,通过 flash-hotswap + prepare_onboard_flash 初始化内部 Flash
  3. 然后在内部 Flash 上创建分区、写入数据库
  4. 最后启动 Radio 应用

类比:就像用 U 盘启动电脑来修复硬盘一样。外部 Flash 就是"U 盘",内部 Flash 就是"硬盘"。


关键发现和问题分析

1. 电源控制对象

  • 电源控制的是 DUT,不是接口板
  • 接口板独立供电,始终在线
  • 日志中 [w5 ...] 的 GPIB 地址 5 对应 DUT 电源

2. 两次上电

  • 第一次上电(步骤1):DUT 从外部 Flash 启动
  • 无第二次断电/上电:步骤5 的 Flash 切换是热切换,DUT 保持运行

3. 产品识别问题

  • 日志显示第一次使用 KRC 901 316/2 失败,第二次使用 KRD 901 316/2 成功
  • 产品配置中应使用正确的产品 ID

4. JEDEC ID 识别问题

  • flash-hotswap internal 时出现 "unrecognized JEDEC id bytes: 00 00 00 00 00 00"
  • 可能是内部 Flash 设备未正确识别或需要多次尝试

5. 启动时间分析

阶段 时间 说明
DUT 上电到 Linux 登录 ~32秒 自动过程
Flash 切换和准备 ~10分钟 包含文件传输和分区创建
Radio 应用启动 ~3分钟 mamastart 到 HAL ready
总时间 ~15-20分钟 完整外部启动流程

附录

相关代码文件

  1. external_boot_faap_via_tcpe3.py - TCPE3 接口板外部启动主程序
  2. bootup_dut_from_external_nib.py - NIB 接口板外部启动主程序
  3. common/radio.py - Radio 类,管理 DUT 启动
  4. common/tcpe3.py - TCPE3 接口板控制
  5. common/nib.py - NIB 接口板通信
  6. common/power_supply.py - 电源控制(控制 DUT 电源)
  7. common/connections.py - 连接管理

产品配置示例(AIR3286)

'AIR3286_B25B66': {
    'product': {'family': 'hawkowl', 'production_number': 'KRD 901 316/2', 'r_state': 'R1A'},
    'interface_board': {'InterfaceBoardType': InterfaceBoardType.TCPE3, 'position': 1},
    'power_supply': {'type': PowerSupplyType.DC, 'com_mode': ComMode.GPIB, 'addr': '5', 'vol': 56, 'current': 10},
}

文档版本: 2.0
创建日期: 2026年5月7日
基于日志: tcpe3_external_bootup.txt

posted @ 2026-05-07 14:47  mo686  阅读(1)  评论(0)    收藏  举报