1. 内存资源约束与挑战
BayBridge 芯片基于 SPARC LEON2 架构,内置 SRAM 空间仅为 160KB。在这有限的空间内需要容纳:
- Bootloader 代码(约 10-12KB)
- Firmware 代码 + 只读数据(约 80-100KB)
- 运行时数据(全局变量、BSS 段)
- 任务队列(32 x 2 个 eMMC 通道)
- 堆栈空间
- 固件参数区(1KB)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| +---------------------------+ 0x40000000 | Trap Table | 1KB +---------------------------+ | .text (代码段) | ~80KB +---------------------------+ | .rodata (只读数据) | ~10KB +---------------------------+ | .data (已初始化数据) | ~5KB +---------------------------+ | .bss (未初始化数据) | ~20KB +---------------------------+ | Stack Space | ~40KB +---------------------------+ | Frame Space | 128B +---------------------------+ | fw_param (固件参数) | 1KB +---------------------------+ 0x40028000 (160KB)
|
2. Linker Script 内存布局设计
2.1 Firmware Linker Script 分析
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
| /* fw_sys.lds */
OUTPUT_ARCH(sparc) __DYNAMIC = 0;
MEMORY { ram : ORIGIN = 0x40000000, LENGTH = 160K }
SECTIONS { /* ===== 代码段 ===== */ .common_text : { _data_start = . ; _common_text_start = . ; . = ALIGN(0x4); /* Trap Table 必须在最前面 (地址 0x40000000) */ src/system/trap/trap_table.ao(.text) src/system/trap/*.ao(.text) /* 其他代码 */ *(.text) /* 只读数据紧跟代码段 */ _rodata_start = . ; *(.rodata*) *(.gnu.linkonce.r*) *(.init) *(.fini) *(.lit) *(.shdata) . = ALIGN(0x4); } > ram _common_text_size = SIZEOF(.common_text); _common_text_end = _common_text_start + _common_text_size;
/* ===== 已初始化数据段 ===== */ .data : AT (_common_text_start + _common_text_size) { . = ALIGN(0x4) + 0x8; /* 8 字节保护间隙 */ *(.data) . = ALIGN(0x4); } > ram _data_size = SIZEOF(.data) + _common_text_size; _data_end = _data_start + _data_size;
/* ===== BSS 段 ===== */ .bss : { _bss_start = . ; . = ALIGN(0x4) + 0x8; /* 8 字节保护间隙 */ *(.bss) . = ALIGN(0x4); } > ram _bss_size = SIZEOF(.bss); _bss_end = _bss_start + _bss_size;
/* ===== 堆栈段 ===== */ .stack : { _stack_bottom = . ; . = ALIGN(0x4) + 0x10; } > ram }
|
2.2 Bootloader 多版本支持
项目为不同启动场景设计了独立的 Linker Script:
| Linker Script |
ORIGIN |
用途 |
boot_rom.lds |
0x00000000 |
内置 ROM 启动 |
boot_flash.lds |
0x50000000 |
SPI Flash XIP 启动 |
fw_sys.lds |
0x40000000 |
RAM 中运行的 Firmware |
fw_simu_ram.lds |
0x40000000 |
仿真调试版本 |
3. 数据结构紧凑设计
3.1 位域 (Bit Field) 优化
1 2 3 4 5 6 7 8 9 10 11 12 13
| typedef struct { u8 ManufacturerId; u8 Reserved:6; u8 DeviceType:2; u8 OemId; u8 ProductName[6]; u8 ProductRevision; u8 ProductSerialNumber[4]; u8 ManufacturingDate; u8 Crc:7; u8 NotUsed:1; } card_cid_struct;
|
3.2 Packed 结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| typedef struct { u32 manufacture_id: 8; u32 oem_id: 8; u32 fw_download_mode: 8; u32 fw_download_mode_rev: 8; u32 fw_update_level: 4; u32 fw_security_level: 4; u32 resv_1_1: 24; u32 emmc_io_ext_internal: 4; u32 main_voltage_ext_internal: 4; u32 resv_100_1: 16; u32 pcie_sideband_io_vol: 1; u32 resv_100_3: 7; } __attribute__((packed, aligned(4))) fw_param_t;
|
优化效果:packed 属性确保结构体成员无间隙对齐,aligned(4) 保证 4 字节对齐以满足 SPARC 访问要求。
3.3 任务信息结构优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| typedef struct { u64 prp1; u64 prp2; u64 address; u16 block_len; u16 nvme_sq_id; u16 nvme_cmd_id; u8 direction; u8 fused_flag; u8 next_pointer; u8 start_card; } task_info_struct;
#define EMPTY 0x00 #define PRE_CANDIDATE 0x01 #define CANDIDATE 0x02 #define EXECUTING 0x03 #define COMPLETED 0x04 #define READY 0x05 #define ERR_STATUS 0x7F
|
内存占用计算:
- 每个任务:34 bytes
- 两个 eMMC 通道各 32 深度:34 × 32 × 2 = 2,176 bytes
- 任务状态数组:1 × 32 × 2 = 64 bytes
- 总计约 2.2KB
4. 静态内存分配策略
4.1 全局数组预分配
1 2 3 4 5 6 7 8 9
| task_info_struct emmc_firmware_task_queue[2][BB_CQ_DEPTH_DEFINE]; u8 emmc_firmware_task_status_flag[2][BB_CQ_DEPTH_DEFINE]; u8 global_emmc_firmware_task_status_map[2][BB_CQ_DEPTH_DEFINE]; u8 global_emmc_firmware_task_status_map_index[2];
#define BB_CQ_DEPTH_DEFINE 0x20 #define BB_NON_CQ_DEPTH_DEFINE 0x20
|
4.2 固定大小缓冲区
1 2 3 4 5 6 7 8 9
| #define DATA_BUFFER_SIZE (4*1024) #define CMD_BUFFER_SIZE (1024) #define PRP_SIZE (4*1024) #define PRP_LIST_SIZE (4*512) #define DATA_BLOCK_SIZE 512
#define FW_ARRAY_SIZE (BB_RAM_SIZE - (6*1024))
|
4.3 固件参数区设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #define fw_param (*(volatile fw_param_t*)(0x40000000 + (BB_RAM_SIZE - 1024)))
|
5. Log 字符串空间优化(重点)
在嵌入式固件中,调试打印的字符串常量会占用 .rodata 段,属于固件二进制的一部分。对于 160KB 的空间限制,字符串优化至关重要。
5.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
| ┌─────────────────────────────────────────────────────────────────────────┐ │ 字符串常量在固件中的占用 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 源代码: │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ db_printf("ERR: cmd44_cq_set_task_params failed with 0x%X\n"); │ │ │ │ db_printf("ERR: cmd45_cq_set_task_address failed with 0x%X\n"); │ │ │ │ db_printf("ERR: cmd46_cq_execute_read_task failed with 0x%X\n");│ │ │ │ db_printf("K_INFO: executing %d fused cmd, direction %d\n"); │ │ │ │ db_printf("ERR: error recovery failed with 0x%x\n"); │ │ │ │ ... │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ .rodata 段: │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ "ERR: cmd44_cq_set_task_params failed with 0x%X\n\0" (47 B) │ │ │ │ "ERR: cmd45_cq_set_task_address failed with 0x%X\n\0" (48 B) │ │ │ │ "ERR: cmd46_cq_execute_read_task failed with 0x%X\n\0"(49 B) │ │ │ │ "K_INFO: executing %d fused cmd, direction %d\n\0" (46 B) │ │ │ │ "ERR: error recovery failed with 0x%x\n\0" (38 B) │ │ │ │ ... │ │ │ │ │ │ │ │ ⚠️ 数百条打印 → 可能占用 10~20KB .rodata 空间! │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
5.2 多级打印控制架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
#define OFF (0) #define FATAL (1) #define ERROR (2) #define WARNING (3) #define INFO (4) #define DEBUG (5) #define TRACE (6) #define ALL (7)
#define LOG(level, ...) \ do { \ if (LOG_LEVEL >= level) { \ db_printf(__VA_ARGS__); \ } \ } while(0)
|
关键设计:当 LOG_LEVEL = OFF 时,所有 LOG() 调用在预处理阶段被移除,字符串常量不会进入编译。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ┌─────────────────────────────────────────────────────────────────────────┐ │ 打印级别与固件版本映射 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 开发阶段 量产阶段 │ │ ───────── ───────── │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ LOG_LEVEL = ALL │ │ LOG_LEVEL = OFF │ │ │ │ 或 DEBUG/TRACE │ │ 或 ERROR │ │ │ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 包含所有字符串 │ │ 仅错误码,无字符串│ │ │ │ FW Size: ~120KB │ │ FW Size: ~100KB │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ │ 用途: 问题定位、功能验证 用途: 客户交付、量产 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
5.3 错误码设计:用数值替代字符串
核心思想:用 32-bit 错误码 编码错误信息,替代冗长的字符串描述。
1 2 3 4 5 6 7 8
| #define LOG_ERROR_STR "ERROR: 0x%08X\n" #define LOG_WARN_STR "WARN: 0x%08X\n" #define LOG_DEBUG_STR "DEBUG: 0x%08X\n"
LOG(ERROR, LOG_ERROR_STR, NVME_ADMIN_FW_COMMIT_INVALID_FW_SLOT);
|
错误码编码规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ┌─────────────────────────────────────────────────────────────────────────┐ │ 32-bit 错误码编码格式 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 31 24 23 16 15 8 7 0 │ │ ┌──────────┬────────────┬────────────┬────────────┐ │ │ │ Level │ Module │ Function │ Detail │ │ │ │ (8-bit) │ (8-bit) │ (8-bit) │ (8-bit) │ │ │ └──────────┴────────────┴────────────┴────────────┘ │ │ │ │ Level: 0x1 = Admin, 0x2 = eMMC, 0xF = BayHub Custom │ │ Module: 0x0 = NVMe Admin, 0x1 = NVMe IO, 0x2 = Host Init... │ │ Function: 具体命令或函数编号 │ │ Detail: 错误细节编码 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
错误码定义示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #define NVME_ADMIN_ABORT_LIMIT_EXCEED 0x10000103 #define NVME_ADMIN_CREATE_IOCQ_INVALID_QID 0x10000301 #define NVME_ADMIN_CREATE_IOCQ_INVALID_QSIZE 0x10000302 #define NVME_ADMIN_FW_COMMIT_INVALID_FW_SLOT 0x10000B06 #define NVME_ADMIN_FW_COMMIT_INVALID_FW_IMAGE 0x10000B07 #define NVME_ADMIN_FW_DOWNLOAD_OVERLAP_RANGE 0x10000C14
#define EMMC_HOST_INIT_ERROR 0x21000000 #define EMMC_CARD_CHECK_ERROR 0x22000000 #define EMMC_CARD_IDENTIFY_ERROR 0x23000000 #define EMMC_RW_ERROR 0x26000000 #define EMMC_ERROR_RECOVERY_ERROR 0x2A000000
|
5.4 模块级打印开关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #define TEST_NVME 0 #define TEST_EMMC 0 #define TEST_SYSTEM 0 #define TEST_FLUSH 1 #define TEST_FW_DOWNLOAD 0 #define TEST_NAMESPACE 0 #define TEST_SHUTDOWN 1
#define LOG_TEST(module_flag, ...) \ do { \ if (TEST_LOG_SWITCH && module_flag) { \ db_printf(__VA_ARGS__); \ } \ } while(0)
LOG_TEST(TEST_EMMC, "CMD44 task_id=%d, block=%d\n", task_id, block_len);
|
5.5 Bootloader 打印分级
1 2 3 4 5 6 7 8 9 10 11 12 13
| #ifdef BOOTLOADER_DEBUG #define Info_Print_BL(...) #define Position_Print_BL(...) #define Key_Info_Print_BL(...) db_printf(__VA_ARGS__) #define Err_Print_BL(...) db_printf(__VA_ARGS__)
#elif defined BOOTLOADER_RELEASE #define Info_Print_BL(...) #define Position_Print_BL(...) #define Key_Info_Print_BL(...) #define Err_Print_BL(...) #endif
|
5.6 空间节省效果对比
| 版本类型 |
LOG_LEVEL |
字符串数量 |
.rodata 占用 |
节省空间 |
| 开发调试版 |
ALL/DEBUG |
~500 条 |
~15KB |
- |
| 测试版本 |
ERROR |
~50 条 |
~2KB |
~13KB |
| 量产版本 |
OFF |
0 条 |
~200B |
~15KB |
1 2 3 4 5 6 7 8 9 10 11
|
#ifdef MP_ASIC #define LOG_LEVEL OFF #define TEST_LOG_SWITCH 0 #endif
#ifdef GENERAL_FPGA #define LOG_LEVEL DEBUG #define TEST_LOG_SWITCH 1 #endif
|
6. 代码空间优化
6.1 编译优化选项
1 2 3 4 5 6 7
| CFLAGS = -c -O3 -Wall -msoft-float -mv8 ${INC_PATH}
|
6.2 条件编译裁剪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #define FW_SIM 0 #define SMART_INFO_ENABLE 1 #define BB_FFU_SUPPORTED 1 #define DISABLE_DATASET_MANAGEMENT_COMMAND 1
#if SMART_INFO_ENABLE smart_info_init(&smart_rw, &smart_data); #endif
#if !DISABLE_DATASET_MANAGEMENT_COMMAND case 0x09: return dataset_management_process(queue_identifier, command); #endif
|
6.3 内联函数与宏
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static inline u64 leon2_cpu_64bits_swap(u64 value) { u32 hi = (u32)(value >> 32); u32 lo = (u32)(value); hi = __builtin_bswap32(hi); lo = __builtin_bswap32(lo); return ((u64)lo << 32) | hi; }
#define set_reg(base, offset, mask, value) \ (*(volatile u32 *)((base) + (offset)) = \ ((*(volatile u32 *)((base) + (offset))) & ~(mask)) | ((value) & (mask)))
|
7. 运行时内存优化
7.1 任务状态位图设计
1 2 3 4 5 6 7
| u8 emmc_firmware_task_status_flag[2][32];
|
7.2 共享缓冲区复用
1 2 3 4 5 6 7 8 9 10 11
| #define BAYBRIDGE_DATA_BUFFER ((volatile u8 *)(BAYBRIDGE_WORKRAM_BASE))
if (global_admin_command_req_dma_channel_flag == 0) { }
|
7.3 堆栈深度控制
1 2 3 4 5 6 7
| set STACK_BASE, %g3 sub %g3, 1024, %g3 sub %g3, 128, %g3 mov %g3, %fp sub %g3, 96, %sp andn %sp, 0x0f, %sp
|
堆栈优化建议:
- 避免深层递归调用
- 大数组使用全局静态分配
- 关键函数使用
__attribute__((noinline)) 控制内联
8. 空间监控与调试
8.1 Map 文件分析
1 2
| $(CROSS_COMPILE)ld -T $(LINKFILE_DIR)/fw_sys.lds -Map=build/debug/fw_sys.map ...
|
Map 文件关键段分析:
1 2 3 4 5 6
| .common_text 0x40000000 0x14a8c .data 0x40014a94 0x0124 .bss 0x40014bb8 0x4c20 ^^^^^^^^^^^^^^ 总计: ~100KB 代码 + 数据 剩余: ~60KB 堆栈空间
|
8.2 运行时堆栈检查
1 2 3 4 5 6 7
| #if TEST_RAM_SPACE
for (u32 i = 0; i < BB_RAM_SIZE/4 - 256; i++) { *(volatile u32 *)(BAYBRIDGE_WORKRAM_BASE + i*4) = 0xDEADBEEF; }
#endif
|
9. 总结
BayBridge 固件空间优化的核心策略:
| 优化方向 |
技术手段 |
节省空间 |
| 字符串优化 |
错误码替代、多级打印控制 |
~10-15KB |
| 数据结构 |
位域、packed、最小类型 |
~5KB |
| 内存分配 |
静态预分配、共享复用 |
~3KB |
| 代码裁剪 |
条件编译、功能开关 |
~5KB |
| 编译优化 |
-O3、内联、宏 |
~2KB |
| 布局设计 |
Linker Script 精确控制 |
- |
关键设计原则:
- 零动态分配:所有内存静态预分配,无 malloc/free
- 固定深度队列:任务队列深度编译时确定
- 缓冲区复用:Data Buffer 根据上下文复用
- 精确布局:Linker Script 控制每个 Section 位置
- 功能可裁剪:通过宏开关移除不需要的功能
- 字符串最小化:量产版用错误码替代字符串描述
字符串优化总结:
1 2 3 4
| 开发版 (LOG_LEVEL=ALL) 量产版 (LOG_LEVEL=OFF) ───────────────────── ───────────────────── db_printf("ERR: ...") → LOG(ERROR, LOG_ERROR_STR, 0x26000005) ~500条字符串, 15KB → 1条格式串 + 错误码, 200B
|
下一篇将分析 IO 性能优化和任务调度的实现细节。