NVMe-eMMC Bridge 芯片固件开发(二):Bootloader 启动流程

1. Bootloader 概述

Bootloader 是芯片上电后执行的第一段代码,负责完成以下核心任务:

  1. CPU 寄存器初始化:设置 PSR、TBR、WIM、堆栈指针
  2. Cache 使能:开启指令和数据 Cache
  3. 固件加载:从 SPI Flash 或 eMMC Boot 分区加载 Firmware
  4. 安全验证:Parity Check 和 Checksum 校验
  5. 跳转执行:跳转到 RAM 中的 Firmware 入口

1.1 启动模式

BayBridge 支持多种启动模式:

模式 Bootloader 位置 Firmware 位置 典型场景
ROM + SPI Flash 芯片 ROM 固化 SPI Flash 省成本方案
SPI Flash Only SPI Flash Base SPI Flash Middle 外挂 Flash
eMMC Boot ROM 或 SPI eMMC Boot Partition 无外挂 Flash
FW Download ROM 或 SPI 主机下载 工厂烧录/救砖

2. 汇编启动代码分析

2.1 boot.S 入口代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/* boot.S - SPARC LEON2 启动汇编 */

#include "cpu.h"
#include "config.h"

.seg "text"
PUBLIC(start)
.global start

SYM(start):
start:
/* 硬件复位入口 */
PUBLIC(hard_reset)
SYM(hard_reset):

common_init:
/* 1. 初始化 PSR (Processor State Register) */
rd %psr, %g1
or %g1, 0xFA0, %g1 ! supervisor mode, disable interrupt, enable trap
wr %g0, %g1, %psr
nop ! PSR 写入需要 3 个延迟槽
nop
nop

/* 2. 初始化 TBR (Trap Base Register) */
set 0x40000000, %g1 ! Trap 表放在 RAM 起始位置
mov %g1, %tbr

/* 3. 初始化 WIM (Window Invalid Mask) */
set WIM_INIT, %g1 ! WIM_INIT = 2
mov %g1, %wim
nop
nop
nop

/* 4. 初始化堆栈指针 */
set STACK_BASE, %g3 ! STACK_BASE = 0x40000000 + 160KB
sub %g3, 1024, %g3 ! 预留 1KB 存储 FW Header 参数
sub %g3, 128, %g3 ! 预留输入参数空间
mov %g3, %fp ! 设置 Frame Pointer
sub %g3, 96, %sp ! 设置 Stack Pointer (SPARC ABI 要求)
andn %sp, 0x0f, %sp ! 16 字节对齐

/* 5. 使能 Cache */
set 0x80000014, %l1 ! Cache Control Register 地址
ld [%l1], %l2
set 0x81000F, %l3 ! 使能 I-Cache 和 D-Cache
or %l3, %l2, %l2
st %l2, [%l1]
flush ! 刷新 Pipeline
nop
nop
nop

/* 6. 跳转到 C 代码 */
set SYM(boot_route), %g2
jmp %g2
nop

2.2 关键寄存器说明

寄存器 作用 初始值
PSR 处理器状态 0xFA0 (S=1, ET=1, PIL=15)
TBR Trap 基地址 0x40000000
WIM 窗口无效掩码 0x02
%fp Frame Pointer RAM_TOP - 1KB - 128
%sp Stack Pointer %fp - 96

2.3 堆栈布局

1
2
3
4
5
6
7
8
9
10
11
12
13
0x40028000 (160KB RAM Top)
|
+------------------------+ <- RAM_TOP
| FW Header (1KB) | <- 存储从 SPI Flash 拷贝的参数
+------------------------+ <- 0x40027C00
| Input Args (128B) | <- 函数调用参数区
+------------------------+ <- 0x40027B80 (%fp)
| Local Vars (96B) | <- 当前栈帧
+------------------------+ <- %sp (16字节对齐)
| |
| Stack Growth ↓ |
| |
+------------------------+

3. 启动路由逻辑 (boot_route.c)

3.1 启动流程状态机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
void boot_route()
{
card_info_struct card_info;
u32 dump_info_buf[20] = {0};
u32 fw_head_base = 0;
u32 fw_body_base;
fw_sect_t fw_sect;
spi_cfg_t spi_cfg;
u32 rst_slot;

// Step 1: 设置默认 PLL 和 AHB 时钟
// PLL 200MHz, AHB 100MHz (ASIC)
set_pll_ahb_clock();

// Step 2: 初始化 UART (调试模式)
#ifdef BOOTLOADER_DEBUG
InitUARTPrint();
#endif

// Step 3: 检查强制下载标志
if (get_fw_download_flag() == 0x2) {
goto fw_download_entry; // 进入固件下载模式
}

// Step 4: 检查 SPI Flash 是否存在
if (!check_spi_exist()) {
goto emmc_boot_entry; // 无 SPI Flash,尝试 eMMC Boot
}

// Step 5: 检查复位时激活的 Slot
rst_slot = get_reset_activate_slot();
if (rst_slot != 0 && rst_slot != 3) {
goto emmc_boot_entry; // 目标不是 SPI,尝试 eMMC
}

// === SPI Boot Flow ===

// Step 6: 验证 SPI Flash 中的固件有效性
int valid = firmware_valid_check(SPI_FLASH_BASE, BOOT_MAX_SIZE);
if (valid == 0) {
// Firmware 在 SPI Flash 起始位置 (Bootloader 在 ROM)
fw_head_base = SPI_FLASH_BASE;
} else if (valid == 1) {
// Firmware 在 SPI Flash 中部 (Bootloader 也在 SPI Flash)
fw_head_base = SPI_FLASH_BASE + BOOT_MAX_SIZE; // 跳过 16KB Bootloader
} else {
goto emmc_boot_entry; // SPI 固件无效,尝试 eMMC
}

// Step 7: 解析 Firmware Header,获取段信息
fw_body_base = fw_head_base + FW_HEAD_SIZE; // FW_HEAD_SIZE = 1KB
firmware_head_parse(fw_head_base, &fw_sect, &spi_cfg);

// Step 8: 拷贝 Firmware 到 RAM
firmware_copy(fw_body_base, &fw_sect, &spi_cfg);

// Step 9: 设置当前激活 Slot 为 SPI
set_current_activate_slot_spi();

// Step 10: 跳转到 Firmware
boot_exit(); // 永不返回

emmc_boot_entry:
// eMMC Boot 流程...
clear_current_activate_slot_spi();
read_boot_part(&fw_sect, ...);

fw_download_entry:
// 固件下载模式
bootload_firmware_downloader(&card_info, dump_info_buf);
}

3.2 固件有效性校验

采用奇偶校验 (Parity Check) 验证固件 Header:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int parity_check(int parity_mode, u32 dword_addr)
{
u8 byte[4];
int check_result;
u32 *p_dword = (u32 *)dword_addr;
u8 *p_byte = (u8 *)dword_addr;

byte[0] = *p_byte;
byte[1] = *(p_byte + 1);
byte[2] = *(p_byte + 2);
byte[3] = *(p_byte + 3);

// 检查全 0 或全 1(无效固件)
if (*p_dword == 0xffffffff || *p_dword == 0) {
return -1;
}

// 计算奇偶校验
check_result = byte[0] ^ byte[1] ^ byte[2] ^ byte[3];

if (parity_mode == 1 && check_result == 0xff) // 奇校验通过
return 0;
else if (parity_mode == 0 && check_result == 0) // 偶校验通过
return 0;
else
return -1;
}

3.3 Firmware Header 结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
* Firmware Header Layout (1KB = 2 Blocks)
*
* Block 1 (512 Bytes):
* DWORD[0]: Parity Byte + Firmware Size (24-bit)
* DWORD[1]: .text Section Address
* DWORD[2]: .text Section Size
* DWORD[3]: .data Section Address
* DWORD[4]: .data Section Size
* DWORD[5]: .bss Section Address
* DWORD[6]: .bss Section Size
* DWORD[8-19]: SPI Flash Configuration
* DWORD[31]: Firmware Checksum
* DWORD[32-127]: Customer Parameters (VID/PID, Serial Number, etc.)
*
* Block 2 (512 Bytes):
* Reserved / Additional Parameters
*/

typedef struct {
u32 text_addr;
u32 text_size;
u32 data_addr;
u32 data_size;
u32 bss_addr;
u32 bss_size;
} fw_sect_t;

typedef struct {
u32 spi_frq_div; // SPI 时钟分频
u32 spi_timing_dly; // SPI 相位配置
} spi_cfg_t;

4. 固件拷贝流程

4.1 firmware_copy 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static void firmware_copy(u32 base, fw_sect_p fw_sect, spi_cfg_p spi_cfg)
{
u32 *copy_start, *copy_end, *bss_start, *bss_end;
u32 *dest, *src;
u32 copy_offset;

// Step 1: 配置 SPI Flash 访问参数
if (SPI_WR_CFG == 0) {
SPI_FRQ_DIV = spi_cfg->spi_frq_div;
SPI_CTRL &= ~PHASE_ADJUST_FIELD_MASK;
SPI_CTRL |= spi_cfg->spi_timing_dly;
SPI_WR_CFG = 1; // 生效配置(100 CPU cycles 后自动清零)
}

// Step 2: 拷贝 Firmware Header 到 RAM Top 1KB
dest = (u32 *)(BAYBRIDGE_WORKRAM_BASE + BB_RAM_SIZE - FW_HEAD_SIZE);
src = (u32 *)(base - FW_HEAD_SIZE);
copy_end = (u32 *)(BAYBRIDGE_WORKRAM_BASE + BB_RAM_SIZE);
while (dest < copy_end) {
*dest++ = *src++;
}

// Step 3: 设置设备 VID/PID 寄存器
dev_vend_id_register();

// Step 4: 判断是否为 Fastboot(仅拷贝 .data 段)
if (is_reset_fastboot()) {
copy_start = (u32 *)fw_sect->data_addr;
copy_offset = fw_sect->data_addr - fw_sect->text_addr;
} else {
// 正常启动:拷贝 .text + .data
copy_start = (u32 *)fw_sect->text_addr;
copy_offset = 0;
}
copy_end = (u32 *)(fw_sect->data_addr + fw_sect->data_size);

// Step 5: 拷贝 .text 和 .data 段
dest = copy_start;
src = (u32 *)(base + copy_offset);
while (dest < copy_end) {
*dest++ = *src++;
}

// Step 6: 清零 .bss 段
bss_start = (u32 *)fw_sect->bss_addr;
bss_end = (u32 *)(fw_sect->bss_addr + fw_sect->bss_size);
dest = bss_start;
while (dest < bss_end) {
*dest++ = 0;
}

// Step 7: 恢复 SPI 频率为安全值
if (SPI_WR_CFG == 0) {
SPI_FRQ_DIV = 8; // DIV4,保证 SPI Write 稳定
SPI_WR_CFG = 1;
}
}

4.2 Fastboot 机制

Fastboot 是一种快速启动优化:

  • 触发条件:热复位(非断电重启)且 Fastboot 标志有效
  • 优化效果:跳过 .text 段拷贝,直接复用 RAM 中的代码
  • 限制:RAM 内容必须保持不变
1
2
3
4
5
6
7
8
9
static u32 is_reset_fastboot()
{
// Slot 复位时禁止 Fastboot
if (get_reset_activate_slot())
return 0;

// 检查 Fastboot 指示标志
return ((BAYBRIDGE_RESET_FAST_BOOT_INDICATOR >> 4) == 0x5a);
}

5. 跳转到 Firmware

5.1 boot_exit 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void boot_exit()
{
Key_Info_Print_BL("boot_exit...\n");

/* 跳转到 RAM 入口执行 Firmware
* RAM 起始位置是 Trap Table,不是代码
* 需要通过 Trap Table 中的二次跳转到达真正的入口
*/
asm(
"set 0x40000000, %g2\n"
"jmp %g2\n"
"nop"
);
}

5.2 Trap Table 结构

SPARC 架构的 Trap(相当于 ARM/MIPS 的异常)机制是整个系统中断和异常处理的核心。当硬件发生中断、异常或执行特定指令时,CPU 会自动跳转到 Trap Table 中对应的处理函数。

5.2.1 SPARC Trap 机制概述

SPARC V8 架构的 Trap 处理流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
┌─────────────────────────────────────────────────────────────────┐
│ 硬件事件发生 │
│ (中断/异常/软件TRAP指令) │
└─────────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 1. CPU 自动保存上下文 │
│ - PC → l1 (Trap发生时的程序计数器) │
│ - NPC → l2 (下一条指令地址) │
│ - PSR → l0 (处理器状态寄存器,包含CWP/ET/PIL等) │
│ - Trap Type → TBR(Trap Base Register) │
└─────────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 2. 计算跳转地址 │
│ - TBR[31:8] | (tt << 4) = 跳转地址 │
│ - tt 是 Trap 类型编号 (0x00-0xFF) │
│ - 每个 Trap 向量占用 16 字节 (4条指令) │
└─────────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 3. 跳转到 Trap Table 中的对应处理函数 │
│ - Trap Table 基地址由 TBR 寄存器设置 │
│ - 固件将 Table 放置在 RAM 起始位置 0x40000000 │
└─────────────────────────────────────────────────────────────────┘

5.2.2 Trap Table 详细内容

Trap Table 位于 baybridge/src/system/trap/trap_table.S,定义了 256 个 Trap 向量(Trap 0x00 - 0xFF):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/* trap_table.S - SPARC LEON2 Trap 表定义 */

PUBLIC(trap_table)
SYM(trap_table):

RTRAP( 0, SYM(InitSystem) ); ! 00 ROM reset trap code
TRAP( 1, SYM(instruction_access_exception_handler) ); ! 01 instruction access
TRAP( 2, SYM(illegal_instruction_handler) ); ! 02 illegal instruction
TRAP( 3, SYM(privileged_instruction_handler) ); ! 03 privileged instruction
TRAP( 4, SYM(fp_disabled_handler) ); ! 04 fp disabled
TRAP( 5, SYM(window_overflow_trap_handler) ); ! 05 window overflow
TRAP( 6, SYM(window_underflow_trap_handler) ); ! 06 window underflow
TRAP( 7, SYM(mem_address_not_aligned_handler) ); ! 07 memory address not aligned
TRAP( 8, SYM(fp_exception_handler) ); ! 08 fp exception
TRAP( 9, SYM(data_access_exception_handler) ); ! 09 data access exception
TRAP( 0xA, SYM(tag_overflow_handler) ); ! 0A tag overflow

/* 0x11 - 0x1F: 外部中断 (IRQ1-IRQ15) */
TRAP( 0x11, SYM(IRQTrap)); ! 0x11 invalid H/W external interrupt
TRAP( 0x12, SYM(IRQTrap)); ! 0x12
TRAP( 0x13, SYM(IRQTrap)); ! 0x13
TRAP( 0x14, SYM(IRQTrap)); ! 0x14
TRAP( 0x15, SYM(IRQTrap)); ! 0x15
TRAP( 0x16, SYM(IRQTrap)); ! 0x16
TRAP( 0x17, SYM(IRQTrap)); ! 0x17
TRAP( 0x18, SYM(IRQTrap)); ! 0x18
TRAP( 0x19, SYM(IRQTrap)); ! 0x19
TRAP( 0x1a, SYM(IRQTrap)); ! 0x1a
TRAP( 0x1b, SYM(IRQTrap)); ! 0x1b
TRAP( 0x1c, SYM(IRQTrap)); ! 0x1c
TRAP( 0x1d, SYM(IRQTrap)); ! 0x1d
TRAP( 0x1e, SYM(IRQTrap)); ! 0x1e
TRAP( 0x1f, SYM(IRQTrap)); ! 0x1f

/* 硬件错误相关 */
TRAP(0x20, SYM(r_register_access_error_handler) ); ! 20
TRAP(0x21, SYM(instruction_access_error_handler) );
TRAP(0x24, SYM(cp_disabled_handler) ); ! 24 cp_disabled
TRAP(0x25, SYM(unimplemented_FLUSH_handler) );
TRAP(0x28, SYM(cp_exception_handler) ); ! 28 cp_exception
TRAP(0x29, SYM(data_access_error_handler) ); ! 29
TRAP(0x2A, SYM(divide_by_zero_handler) ); ! 2A divid_by_zero
TRAP(0x2B, SYM(data_store_error_handler) ); ! 2B data_store
TRAP(0x2C, SYM(data_access_MMU_miss_handler) ); ! 2C
TRAP(0x3C, SYM(instruction_access_MMU_miss_handler) );

/* 软件 TRAP */
TRAP(0x80, SYM(AHB_cp_excpt_handler)); ! 80 软件Trap
TRAP(0x82, SYM(divide_by_zero_handler)); ! 82 divide_by_zero

5.2.3 宏定义说明

Trap Table 使用两个核心宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* TRAP 宏 - 用于普通 Trap 入口 */
#define TRAP(_vector, _handler) \
mov %psr, %l0 ; \ /* 保存 PSR 到 l0 */
sethi %hi(_handler), %l4 ; \ /* 加载目标地址高20位 */
jmp %l4+%lo(_handler); \ /* 跳转到处理函数 */
mov _vector, %l3 /* 将 trap 类型保存到 l3 */

/* RTRAP 宏 - 用于 Reset Trap (特权级为0) */
#define RTRAP(_vector, _handler) \
mov %g0, %l0 ; \ /* PSR=0 (特权级0) */
sethi %hi(_handler), %l4 ; \
jmp %l4+%lo(_handler); \
mov _vector, %l3

两者的区别:

  • TRAP 用于普通中断/异常处理,保存当前 PSR
  • RTRAP 用于 Reset 入口,此时 PSR 还未初始化(设为0)

5.2.4 Trap 向量分类

Trap 编号 类型 说明
0x00 Reset 系统复位,跳转到 InitSystem
0x01-0x0A 异常 指令异常、非法指令、特权指令等
0x11-0x1F 中断 外部硬件中断 IRQ1-IRQ15
0x20-0x3F 错误 硬件错误、MMU 未命中、除零等
0x80-0xFF 软件 软件 TRAP 指令触发

5.3 外部中断处理流程 (IRQ Trap)

当外部外设(如 Timer、UART、eMMC、NVMe)产生中断时,CPU 通过 Trap 0x11-0x1F 进入 IRQTrap 处理函数。

5.3.1 IRQTrap 处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* trap_isr.S - IRQ 中断处理入口 */

IRQTrap:
/* 1. 禁用中断,设置 PIL=15 (最高优先级) */
rd %psr, %l0 ! 读取当前 PSR
or %l0, 0x0F00, %l7 ! PIL=15,屏蔽所有中断
wr %g0, %l7, %psr ! 写入 PSR 禁用中断
nop; nop; nop ! 3个延迟槽

/* 2. 检查并处理寄存器窗口溢出 */
mov %wim, %l4
and %l7, 0x1f, %l7 ! 提取 CWP
mov 1, %l5
sll %l5, %l7, %l5 ! 计算窗口位
cmp %l5, %l4
bne CPU_window_fine ! 窗口有效,跳过
nop

/* 3. 窗口溢出处理:保存当前窗口到栈 */
! 计算新 WIM,切换到新窗口
save ! SAVE_NEXT_WINDOW
mov %g1, %wim
std %l0, [%sp + 0] ! 保存局部寄存器
... (保存所有 L/I 寄存器)
restore ! 恢复

CPU_window_fine:
/* 4. 构建中断栈帧 */
sub %fp, 256, %sp ! ISR 栈帧 256 字节

/* 5. 保存全局寄存器 */
std %g0, [%sp + 0x80]
std %g2, [%sp + 0x88]
std %g4, [%sp + 0x90]
std %g6, [%sp + 0x98]

/* 6. 获取中断号 */
mov %tbr, %l3
and %l3, 0x0FF0, %l3 ! 提取 tt 字段
srl %l3, 4, %l3 ! 右移4位得到 trap 号
sub %l3, 16, %o0 ! 中断号 = tt - 16 (IRQ1=0x11-16=1)

/* 7. 清除中断源 */
mov 1, %l3
sll %l3, %o0, %l3 ! 生成位掩码
set INTR_CLEAR_ADDR, %l4
st %l3, [%l4] ! 写入中断清除寄存器

/* 8. 重新启用 Trap (但保持 PIL 较高) */
rd %psr, %l3
or %l3, 0x0020, %l3 ! ET = 1,启用 trap
wr %g0, %l3, %psr
nop; nop; nop

/* 9. 调用 C 语言中断分发函数 */
call CPUDispatchIRQ
nop

/* 10. 恢复全局寄存器 */
ldd [%sp + 0x80], %g0
ldd [%sp + 0x88], %g2
ldd [%sp + 0x90], %g4
ldd [%sp + 0x98], %g6

/* 11. 恢复 PSR,退出中断 */
andn %l0, 0x0F20, %l0 ! ET=0, PIL=0
wr %g0, %l0, %psr
nop; nop; nop

/* 12. 返回中断点继续执行 */
jmpl %l1, %g0 ! 跳转到 PC
rett %l2 ! 返回 NPC

5.3.2 中断分发函数 CPUDispatchIRQ

trap_isr.S 调用 C 函数的 CPUDispatchIRQ 进行中断分发:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* irq_handler.c - 中断向量表和分发 */

static PFVI IRQVector[15] = {
invalid_irq, /* IRQ 0 - 未使用 */
invalid_irq, /* IRQ 1 */
invalid_irq, /* IRQ 2 */
// ... 初始化为无效处理函数
};

/* 中断初始化时,通过 interrupt_install() 注册具体外设的中断处理函数 */
void interrupt_install(int32 vec, void (*f)(int32 vec))
{
if (vec > 0 && vec < 16 && NULL != f)
{
IRQVector[vec] = (void (*)())f;
INTR_PRIORITY0_BIT(vec); /* 设置优先级 */
INTR_CLEAR_BIT(vec); /* 清除中断 */
}
}

/* 中断分发:根据中断号调用对应处理函数 */
void CPUDispatchIRQ(u32 InterruptNumber)
{
if (InterruptNumber > 0 && InterruptNumber < 16 && IRQVector[InterruptNumber])
{
/* 调用注册的中断处理函数 */
(*(IRQVector[InterruptNumber]))(InterruptNumber);
}
else
{
FATAL_ERR_BREAK; /* 未注册的中断 */
}
}

5.3.3 典型外设中断注册示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* 定时器中断注册示例 */
void timer_init(void)
{
// 注册 Timer0 中断处理函数
interrupt_install(TIMER0_IRQ, timer0_handler);

// 设置中断优先级
interrupt_priority0(TIMER0_IRQ);

// 启用中断
interrupt_unmask(TIMER0_IRQ);
}

void timer0_handler(int32 vec)
{
// 清除定时器中断
TIMER->INT_CLEAR = TIMER_INT_TIMEOUT;

// 更新系统 tick
g_system_tick++;

// 调度任务(RTOS)
#if defined(FREERTOS)
vTaskNotifyGiveFromISR(xTaskHandle, NULL);
#endif
}

5.4 异常处理流程

对于非中断类的异常(如非法指令、内存访问错误、除零等),Trap Table 跳转到对应的异常处理函数。这些处理函数最终都调用 unified_exception_manager 进行统一处理。

5.4.1 异常处理通用流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* exception.S - 通用异常处理示例 */

PUBLIC(instruction_access_exception_handler)
SYM(instruction_access_exception_handler):
sub %fp, 128, %sp ! 构建栈帧

/* 启用 Trap,禁用中断 */
mov %psr, %l0
or %l0, 0xF20, %l0 ! ET=1, PIL=15
mov %l0, %psr

/* 记录异常信息并调用统一处理 */
set 0x01, %o0 ! 异常类型
set 0x05, %o1 ! 异常子类型
call unified_exception_manager
nop

/* 恢复 PSR,返回原执行点 */
andn %l0, 0x0F20, %l0 ! ET=0, PIL=0
wr %g0, %l0, %psr

nop; nop; nop

jmpl %l1, %g0 ! 返回 PC
rett %l2 ! 返回 NPC

5.4.2 寄存器窗口溢出/下溢处理

SPARC 的寄存器窗口机制是其特色,但也引入了窗口溢出/下溢异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* 窗口溢出处理 - Trap 5 */
PUBLIC(window_overflow_trap_handler)
SYM(window_overflow_trap_handler):
/* 计算新 WIM: 右移1位 + 左移(16-1)位 = 循环右移 */
mov %wim, %l3
mov %g1, %l7 ! 保存 g1
srl %l3, 1, %g1 ! WIM >> 1
sll %l3, 15, %l4 ! WIM << 15 (16个窗口)
or %l4, %g1, %g1 ! 循环右移1位

save ! 切换到下一个窗口
mov %g1, %wim ! 写入新 WIM
nop; nop; nop

/* 保存当前窗口寄存器到栈 */
std %l0, [%sp + 0x00]
std %l2, [%sp + 0x08]
... (保存所有 L/I 寄存器)

restore ! 返回原窗口
mov %l7, %g1 ! 恢复 g1

/* 重新执行导致溢出的指令 */
jmp %l1
rett %l2

5.5 TBR (Trap Base Register) 设置

Trap Table 的基地址通过 TBR 寄存器设置。在系统初始化时,固件将 TBR 设置为 RAM 起始地址:

1
2
3
4
5
6
7
8
9
/* cpu.h - TBR 设置宏 */

#define t_sparc_set_tbr( _tbr ) \
do { \
asm volatile("sethi %hi(trap_table), %g1"); \
asm volatile("mov %lo(trap_table), %g2"); \
asm volatile("mov %g1 + %g2, %tbr"); \
nop(); nop(); nop(); \
} while ( 0 )

固件在 InitSystem() 初始化阶段设置 TBR:

1
2
3
4
5
6
7
void InitSystem(void)
{
// 设置 Trap Table 基地址
t_sparc_set_tbr(trap_table);

// ... 其他初始化
}

Trap 跳转地址计算:

1
2
3
4
5
6
跳转地址 = TBR[31:8] | (trap_type << 4)

例如:
- TBR = 0x40000000 (RAM 起始)
- 发生 IRQ1 中断 (trap_type = 0x11)
- 跳转地址 = 0x40000000 | (0x11 << 4) = 0x40000110

5.6 链接脚本中的 Trap Table 位置

链接脚本确保 Trap Table 位于固件的最前面(0x40000000):

1
2
3
4
5
6
7
8
9
10
11
12
/* fw_sys.lds */

_data_start = . ;
_common_text_start = . ;
. = ALIGN(0x4);

/* 强制 trap_table 排在最前面 */
src/system/trap/trap_table.ao(.text)

/* 其他系统代码 */
src/system/trap/*.ao(.text)
*(.text)

这样确保无论固件加载到 RAM 的任何位置,Trap Table 始终在 TBR 指向的基地址处。

5.7 完整中断处理流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
┌─────────────────────────────────────────────────────────────────────┐
│ 系统运行 │
└────────────────────────────┬────────────────────────────────────────┘

▼ 外部中断/异常发生
┌─────────────────────────────────────────────────────────────────────┐
│ 1. CPU 硬件自动处理 │
│ - 保存 PSR → l0 │
│ - 保存 PC → l1 │
│ - 保存 NPC → l2 │
│ - 提取 trap_type → TBR │
│ - 禁用中断 (ET=0, PIL=15) │
└────────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 2. 计算跳转地址 │
│ 地址 = TBR | (trap_type << 4) │
│ 例如: 0x40000000 | (0x11 << 4) = 0x40000110 (IRQ1) │
└────────────────────────────┬────────────────────────────────────────┘

▼ 跳转到 trap_table[0x11]
┌─────────────────────────────────────────────────────────────────────┐
│ 3. Trap Table 入口 (trap_table.S) │
│ TRAP(0x11, SYM(IRQTrap)) │
│ → jmp IRQTrap │
└────────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 4. IRQTrap 汇编处理 (trap_isr.S) │
│ - 检查/处理寄存器窗口溢出 │
│ - 构建 ISR 栈帧 │
│ - 保存寄存器 │
│ - 清除中断源 │
│ - 启用嵌套中断 (ET=1) │
│ - 调用 CPUDispatchIRQ() │
└────────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 5. CPUDispatchIRQ() (irq_handler.c) │
│ - 根据中断号查 IRQVector[] │
│ - 调用注册的 ISR: (*IRQVector[irq_num])(irq_num) │
│ 例如: timer0_handler(5) │
└────────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 6. 具体外设 ISR (如 timer.c) │
│ - 清除外设中断标志 │
│ - 处理中断业务逻辑 │
│ - 记录中断统计 │
└────────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│ 7. 返回 IRQTrap │
│ - 恢复寄存器 │
│ - 恢复 PSR (ET=0, PIL=0) │
│ - jmp l1; rett l2 → 返回中断发生点继续执行 │
└─────────────────────────────────────────────────────────────────────┘

5.3 InitSystem 入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// init_sys.c
void InitSystem(void)
{
// 1. 执行 Firmware Checksum 校验
// do_fw_checksum();

// 2. 初始化 BSP 模块
InitBSP();

// 3. 初始化中断控制器
interrupt_init();

// 4. 进入 NVMe 主流程
main();
}

6. 固件构建工具

6.1 bin_attach 工具

bin_attach 是一个多功能二进制处理工具,支持三种模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
* 模式 0: 生成 Firmware Header
* - 输入: fw_sys_raw.bin, fw_sys_size.txt, boot.cfg, customer.cfg
* - 输出: fw_head.bin (1KB)
*
* 模式 1: 扩展 Bootloader 到固定大小
* - 输入: boot_flash.bin
* - 输出: boot_flash_extended.bin (16KB)
*
* 模式 2: 合并两个二进制文件
* - 输入: head.bin, tail.bin
* - 输出: merged.bin
*/

// Parity 生成算法
static u32 parity_generate(u32 dword, u32 mode)
{
u8 byte[4];
byte[1] = (dword & 0x00ff0000) >> 16;
byte[2] = (dword & 0x0000ff00) >> 8;
byte[3] = (dword & 0x000000ff);

if (mode == 1) // 奇校验
byte[0] = ~(byte[1] ^ byte[2] ^ byte[3]);
else // 偶校验
byte[0] = (byte[1] ^ byte[2] ^ byte[3]);

dword &= 0x00ffffff; // 清除最高字节
dword |= ((u32)byte[0]) << 24; // 填入校验字节
return dword;
}

6.2 fw_checksum 工具

计算 Firmware 的 XOR Checksum:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// fw_checksum.c
int main(u8 argc, u8 *argv[])
{
FILE *fp_fwbin = fopen(argv[1], "rb");

u32 fw_size = get_file_len(fp_fwbin);
u32 fw_size_raw = fw_size - 1024; // 排除 Header
u32 fw_dword_cnt = fw_size_raw / 4;

u8 *buffer = create_buf(fw_size);
fread(buffer, sizeof(u8), fw_size, fp_fwbin);

// XOR Checksum 计算
u32 *dword_buf = (u32 *)buffer;
u32 checksum = dword_buf[256]; // 从 offset 1K 开始
for (int i = 1; i < fw_dword_cnt; i++) {
checksum ^= dword_buf[256 + i];
}

printf("firmware raw size: %u, dword checksum: 0x%X\n",
fw_size_raw, checksum);

// 写入 Header 的 DWORD[31] 位置
*((u32 *)(buffer + FW_CHECKSUM_DWORD_OFFSET * 4)) = checksum;

FILE *fp_fwbin_out = fopen(argv[2], "wb");
fwrite(buffer, sizeof(u8), fw_size, fp_fwbin_out);

return 0;
}

7. 启动时序分析

7.1 典型启动时序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+-------+------------------+------------------+------------------+
| 阶段 | SPI Boot | eMMC Boot | FW Download |
+-------+------------------+------------------+------------------+
| T0 | Power On | Power On | Power On |
| T1 | CPU Reset | CPU Reset | CPU Reset |
| T2 | boot.S (ASM) | boot.S (ASM) | boot.S (ASM) |
| T3 | boot_route() | boot_route() | boot_route() |
| T4 | SPI Check OK | SPI Check Fail | Force Download |
| T5 | FW Valid Check | eMMC Boot Check | Wait Host CMD |
| T6 | FW Copy to RAM | FW Copy to RAM | Receive FW Data |
| T7 | boot_exit() | boot_exit() | boot_exit() |
| T8 | Trap Table | Trap Table | Trap Table |
| T9 | InitSystem() | InitSystem() | InitSystem() |
| T10 | main() | main() | main() |
+-------+------------------+------------------+------------------+
| Time | ~10-20 ms | ~50-100 ms | Variable |
+-------+------------------+------------------+------------------+

7.2 启动时间优化技术

  1. Fastboot:热复位跳过 .text 拷贝
  2. SPI 频率优化:动态调整 SPI 时钟分频
  3. 并行初始化:减少串行依赖

8. 总结

BayBridge Bootloader 的设计体现了嵌入式系统启动代码的典型特征:

  1. 精简高效:汇编代码控制在 100 行以内
  2. 多路启动:支持 ROM/SPI/eMMC 多种启动介质
  3. 安全校验:Parity + Checksum 双重保护
  4. 容错机制:启动失败自动切换到 FW Download 模式
  5. 快速启动:Fastboot 减少热复位时间

下一篇文章将详细介绍 Firmware 主流程和 NVMe 协议栈的实现。


附录:关键宏定义

1
2
3
4
5
6
7
8
9
// config.h
#define BB_RAM_SIZE (160*1024) // 160KB RAM
#define BB_ROM_BASE 0x50000000 // ROM 基地址

// boot.h
#define SPI_FLASH_BASE 0x50000000 // SPI Flash 映射地址
#define BOOT_MAX_SIZE 16384 // Bootloader 最大 16KB
#define FW_HEAD_SIZE 1024 // Firmware Header 1KB
#define PARITY_MODE 1 // 1: 奇校验, 0: 偶校验