文章目录[隐藏]
什么是POST
POST (Power-On-Self-Test),是U-Boot引导加载程序中的一种自检机制,即开机自检。其主要作用是在系统上电或复位时,对硬件经行基本的自检的诊断,以确保关键硬件部件(如内存,CPU,外设等)工作正常。
可以做哪些测试
举例说明:
- CPU test
- Cache test
- Memory test
- Ethernet test
- Serial channels test
- Watchdog timer test
- RTC test
- I2C test
- SPI test
- USB test
工程构建参考
不是所有的U-Boot版本都默认启用POST,需要在配置时开启(如CONFIG_POST等选项)。 以下基于POST框架的SPI测试工程可以作为一个参考,经过验证是可行的。
SPI Flash POST Test
本教程以实际的 SPI Flash POST 测试实现为例,提供了从头开始开发完整 POST 测试用例的全面指南。
Structure
u-boot/
├── post/drivers/spi_flash.c # Test implementation file
├── post/tests.c # Test registration file
├── post/drivers/Makefile # Build configuration
├── include/post.h # Test ID definitions
├── include/configs/xxx.h # Board-level configuration
└── configs/xxx_defconfig # Board configuration
步骤 1:板级配置
文件:include/configs/xxx.h
POST 字存储配置:
- 位置:OCM HIGH SRAM (0xe1fe0000)
- 大小:128KB OCM SRAM 区域
- 用途:启用 Zephyr 应用集成进行诊断
/* POST(开机自检)支持 */ #define CONFIG_POST CONFIG_SYS_POST_SPI_FLASH /* POST 字存储配置 */ #define CONFIG_SYS_POST_WORD_ADDR 0xe1fe0000
配置说明:
- CONFIG_POST:定义启用 POST 测试的位掩码
- CONFIG_SYS_POST_WORD_ADDR:POST 状态存储地址
文件:configs/xxx_defconfig
- CONFIG_POST=y
- CONFIG_CMD_DIAG=y
# CONFIG_POST_AUTOSTART=y
如需启用开机自检,请开启 CONFIG_POST_AUTOSTART 注释。
配置说明:
- CONFIG_POST:启用 POST 框架。
- CONFIG_CMD_DIAG:启用诊断命令。
步骤2:创建测试文件
File: post/drivers/spi_flash.c
设计原则:
- 设备检测:使用标准配置参数探测 SPI Flash
- 参数验证:检查设备参数的有效性
- 安全地址选择:避开关键区域(U-Boot、环境变量等)
代码如下:
/*
* @copyright Copyright (c) 2025 SYSTech Co.
* @license This project is CLOSED SOURCE, All Rights Reserved
*/
#include <common.h>
#include <post.h>
#include <spi.h>
#include <spi_flash.h>
/*
* SPI Flash test data
*/
#define SPI_FLASH_TEST_PATTERN1 0x55
#define SPI_FLASH_TEST_PATTERN2 0xAA
#define SPI_FLASH_TEST_SIZE 0x1000 /* 4KB test size */
static struct spi_flash *flash = NULL;
/*
* Test a single SPI Flash sector by reading, erasing, writing and verifying
*/
static int spi_flash_post_test_sector(u32 offset) {
u8 *test_buf = NULL;
u8 *backup_buf = NULL;
int ret = -1;
u32 test_size = (SPI_FLASH_TEST_SIZE < flash->sector_size)
? SPI_FLASH_TEST_SIZE
: flash->sector_size;
printf(" -> Testing sector at 0x%08x (size: 0x%x bytes)\n", offset,
test_size);
/* Allocate buffers */
test_buf = malloc(test_size);
backup_buf = malloc(test_size);
if (!test_buf || !backup_buf) {
printf(" X Failed to allocate test buffers\n");
goto cleanup;
}
/* Read original data for backup */
printf(" -> Reading original data for backup...\n");
ret = spi_flash_read(flash, offset, test_size, backup_buf);
if (ret) {
printf(" X Failed to read original data (ret=%d)\n", ret);
goto cleanup;
}
/* Fill test buffer with pattern 1 */
memset(test_buf, SPI_FLASH_TEST_PATTERN1, test_size);
/* Erase sector */
printf(" -> Erasing sector...\n");
ret = spi_flash_erase(flash, offset, flash->sector_size);
if (ret) {
printf(" X Failed to erase sector (ret=%d)\n", ret);
goto cleanup;
}
/* Write test pattern 1 */
printf(" -> Writing pattern 1 (0x%02x)...\n", SPI_FLASH_TEST_PATTERN1);
ret = spi_flash_write(flash, offset, test_size, test_buf);
if (ret) {
printf(" X Failed to write pattern 1 (ret=%d)\n", ret);
goto cleanup;
}
/* Read back and verify pattern 1 */
printf(" -> Verifying pattern 1...\n");
memset(test_buf, 0, test_size);
ret = spi_flash_read(flash, offset, test_size, test_buf);
if (ret) {
printf(" X Failed to read back pattern 1 (ret=%d)\n", ret);
goto cleanup;
}
/* Verify pattern 1 */
for (u32 i = 0; i < test_size; i++) {
if (test_buf[i] != SPI_FLASH_TEST_PATTERN1) {
printf(" X Pattern 1 verification failed at offset 0x%x: "
"expected 0x%02x, got 0x%02x\n",
i, SPI_FLASH_TEST_PATTERN1, test_buf[i]);
ret = -1;
goto cleanup;
}
}
printf(" ■ Pattern 1 verification passed\n");
/* Fill test buffer with pattern 2 */
memset(test_buf, SPI_FLASH_TEST_PATTERN2, test_size);
/* Erase sector again */
printf(" -> Erasing sector for pattern 2...\n");
ret = spi_flash_erase(flash, offset, flash->sector_size);
if (ret) {
printf(" X Failed to erase sector for pattern 2 (ret=%d)\n", ret);
goto cleanup;
}
/* Write test pattern 2 */
printf(" -> Writing pattern 2 (0x%02x)...\n", SPI_FLASH_TEST_PATTERN2);
ret = spi_flash_write(flash, offset, test_size, test_buf);
if (ret) {
printf(" X Failed to write pattern 2 (ret=%d)\n", ret);
goto cleanup;
}
/* Read back and verify pattern 2 */
printf(" -> Verifying pattern 2...\n");
memset(test_buf, 0, test_size);
ret = spi_flash_read(flash, offset, test_size, test_buf);
if (ret) {
printf(" X Failed to read back pattern 2 (ret=%d)\n", ret);
goto cleanup;
}
/* Verify pattern 2 */
for (u32 i = 0; i < test_size; i++) {
if (test_buf[i] != SPI_FLASH_TEST_PATTERN2) {
printf(" X Pattern 2 verification failed at offset 0x%x: "
"expected 0x%02x, got 0x%02x\n",
i, SPI_FLASH_TEST_PATTERN2, test_buf[i]);
ret = -1;
goto cleanup;
}
}
printf(" ■ Pattern 2 verification passed\n");
/* Restore original data */
printf(" -> Restoring original data...\n");
ret = spi_flash_erase(flash, offset, flash->sector_size);
if (ret) {
printf(" ! Warning: Failed to erase for restore (ret=%d)\n", ret);
/* Continue anyway to try restore */
}
ret = spi_flash_write(flash, offset, test_size, backup_buf);
if (ret) {
printf(" ! Warning: Failed to restore original data (ret=%d)\n", ret);
/* Don't fail the test for restore failure */
ret = 0;
}
printf(" ■ Sector test completed successfully\n");
ret = 0;
cleanup:
if (test_buf)
free(test_buf);
if (backup_buf)
free(backup_buf);
return ret;
}
/*
* SPI Flash POST test entry point
*/
int spi_flash_post_test(int flags) {
int ret = 0;
u32 test_offset;
u32 post_word;
printf("\n=== SPI Flash POST Test ===\n");
/* Display and update POST Word for test start */
post_word = post_word_load();
printf(" ◆ POST Word: 0x%08x (", post_word);
for (int i = 31; i >= 0; i--) {
printf("%d", (post_word >> i) & 1);
if (i % 4 == 0 && i != 0)
printf(" ");
}
printf(") -> ");
/* Set test in progress flag */
post_word |= CONFIG_SYS_POST_SPI_FLASH;
post_word_store(post_word);
printf("0x%08x (", post_word);
for (int i = 31; i >= 0; i--) {
printf("%d", (post_word >> i) & 1);
if (i % 4 == 0 && i != 0)
printf(" ");
}
printf(") [test bit set]\n");
/* Probe SPI Flash */
printf("Hardware Detection:\n");
flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS,
CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE);
if (!flash) {
printf(" X FAILED: Cannot detect SPI flash device\n");
printf("\n=== Test Result: FAILED ===\n");
return -1;
}
printf(" ■ Device detected: %s\n", flash->name);
printf(" ■ Total size: %u bytes (%u MB)\n", flash->size,
flash->size / (1024 * 1024));
printf(" ■ Sector size: %u bytes (%u KB)\n", flash->sector_size,
flash->sector_size / 1024);
/* Basic sanity checks */
if (flash->size == 0 || flash->sector_size == 0) {
printf(" X FAILED: Invalid flash parameters\n");
printf("\n=== Test Result: FAILED ===\n");
ret = -1;
goto cleanup;
}
/* Use a test offset that avoids critical areas */
test_offset = (flash->size / 2) & ~(flash->sector_size - 1);
if (test_offset + flash->sector_size > flash->size) {
test_offset = flash->size - flash->sector_size;
}
printf("\nTest Execution:\n");
printf(" ◆ Test location: 0x%08x (middle of flash)\n", test_offset);
printf(" ◆ Test size: 0x%x bytes (%u KB)\n", SPI_FLASH_TEST_SIZE,
SPI_FLASH_TEST_SIZE / 1024);
printf(" ◆ Test patterns: 0x%02x, 0x%02x\n", SPI_FLASH_TEST_PATTERN1,
SPI_FLASH_TEST_PATTERN2);
/* Perform the actual test */
ret = spi_flash_post_test_sector(test_offset);
/* Update POST Word based on test result */
printf("\nPOST Word Update:\n");
post_word = post_word_load();
printf(" Before clear: 0x%08x (", post_word);
for (int i = 31; i >= 0; i--) {
printf("%d", (post_word >> i) & 1);
if (i % 4 == 0 && i != 0)
printf(" ");
}
printf(")\n");
if (ret == 0) {
post_word &= ~CONFIG_SYS_POST_SPI_FLASH;
post_word_store(post_word);
printf(" After clear: 0x%08x (", post_word);
for (int i = 31; i >= 0; i--) {
printf("%d", (post_word >> i) & 1);
if (i % 4 == 0 && i != 0)
printf(" ");
}
printf(") [test bit cleared]\n");
} else {
printf(" Test failed, bit remains set: 0x%08x (", post_word);
for (int i = 31; i >= 0; i--) {
printf("%d", (post_word >> i) & 1);
if (i % 4 == 0 && i != 0)
printf(" ");
}
printf(")\n");
}
cleanup:
if (flash) {
spi_flash_free(flash);
flash = NULL;
}
printf("\n");
if (ret == 0) {
printf("=== Test Result: PASSED ===\n");
} else {
printf("=== Test Result: FAILED ===\n");
}
printf("============================\n");
return ret;
}
这段代码实现了U-Boot自检(POST)中的SPI Flash硬件测试,主要功能如下:
-
自动检测SPI Flash设备 通过spi_flash_probe自动检测并初始化SPI Flash芯片,获取其容量、扇区大小等信息。
-
选择测试区域 选择Flash中间的一个扇区作为测试对象,避免破坏关键数据。
-
测试流程
- 备份原始数据
- 擦除扇区
- 写入测试模式1(0x55),并校验
- 擦除扇区
- 写入测试模式2(0xAA),并校验
- 恢复原始数据
-
POST Word管理 用POST Word记录测试状态,测试开始时设置对应位,测试通过后清除,失败则保留。
步骤3:定义测试ID
每个POST测试项都需要唯一的标识,方便在代码中区分和管理不同的硬件测试。测试ID通常用于POST Word(自检状态)中,记录每个测试项的执行和通过/失败状态,这样可以在系统重启或下次上电时追踪哪些测试通过,哪些失败。新增或删除测试项时,只需定义或移除对应的ID。
File: include/post.h
/* Add after existing POST test ID definitions */
#define CONFIG_SYS_POST_SPI_FLASH 0x02000000
ID 选择原则:
- 使用位掩码,每个测试占用一个唯一的位
- 检查现有定义并选择未使用的位值
- 建议使用较大的值以避免与标准测试冲突
步骤4:将测试注册到POST框架
File: post/tests.c
添加函数声明
/* Add in the extern declaration section at the top of the file */
extern int spi_flash_post_test(int flags);
注册测试列表
/* Add test entry to post_list array */
#if CONFIG_POST & CONFIG_SYS_POST_SPI_FLASH
{
"SPI Flash test", // Test description
"spi_flash", // Test name (used by diag command)
"This test verifies SPI flash read, write, and erase operations.", // Detailed description
POST_RAM | POST_SLOWTEST | POST_MANUAL, // Test attributes ( | POST_POWERON)
&spi_flash_post_test, // Test function pointer
NULL, // Initialization function (optional)
NULL, // Cleanup function (optional)
CONFIG_SYS_POST_SPI_FLASH // Test ID
},
#endif
如果要启用开机自检,请启用 POST_POWERON
测试属性说明:
POST_RAM
:测试需要 RAM,在 RAM 初始化后运行POST_SLOWTEST
:慢速测试,可能需要相当长的时间POST_MANUAL
:手动测试,不在自动 POST 模式下运行
可选标志:
#define POST_POWERON 0x01 /* 开机启动时测试运行 */
#define POST_NORMAL 0x02 /* 正常启动时测试运行 */
#define POST_SLOWTEST 0x04 /* 测试很慢,通过按键启用 */
#define POST_POWERTEST 0x08 /* 看门狗复位后测试运行 */
#define POST_ROM 0x100 /* 在 ROM 中运行测试 */
#define POST_RAM 0x200 /* 在 RAM 中运行测试 */
#define POST_MANUAL 0x400 /* 测试可以手动执行 */
#define POST_REBOOT 0x800 /* 测试可能导致重启 */
#define POST_PREREL 0x1000 /* 重定位前测试运行 */
步骤5:配置构建
File: post/drivers/Makefile
# Add SPI Flash test to build list
obj-y += flash.o i2c.o memory.o rtc.o spi_flash.o
构建选项:
- 无条件编译:
obj-y += spi_flash.o
- 条件编译:
obj-$(CONFIG_SYS_POST_SPI_FLASH) += spi_flash.o
步骤6:编译测试
编译:
# 1. Set build environment
export ARCH=arm
export CROSS_COMPILE=arm-zephyr-eabi-
# 2. Clean previous builds
make distclean
# 3. Configure target board
make fmql_rbs2530_defconfig
# 4. Build U-Boot
make -j
# 5. Check if SPI Flash POST was compiled in
objdump -t u-boot | grep spi_flash_post_test
测试日志:
sysfly> md.l 0xe1fe0000 1
e1fe0000: dead0081 ....
sysfly> diag
Available hardware tests:
spi_flash - SPI Flash test
Use 'diag [
<test1> [<test2> ...]]' to get more info.
Use 'diag run [
<test1> [<test2> ...]]' to run tests.
sysfly> diag run spi_flash
POST spi_flash
=== SPI Flash POST Test ===
◆ POST Word: 0xdead0081 (1101 1110 1010 1101 0000 0000 1000 0001) -> 0xdead0081 (1101 1110 1010 1101 0000 0000 1000 0001) [test bit set]
Hardware Detection:
SF: Detected n25q128 with page size 256 Bytes, erase size 64 KiB, total 16 MiB
■ Device detected: n25q128
■ Total size: 16777216 bytes (16 MB)
■ Sector size: 65536 bytes (64 KB)
Test Execution:
◆ Test location: 0x00800000 (middle of flash)
◆ Test size: 0x1000 bytes (4 KB)
◆ Test patterns: 0x55, 0xaa
-> Testing sector at 0x00800000 (size: 0x1000 bytes)
-> Reading original data for backup...
-> Erasing sector...
-> Writing pattern 1 (0x55)...
-> Verifying pattern 1...
■ Pattern 1 verification passed
-> Erasing sector for pattern 2...
-> Writing pattern 2 (0xaa)...
-> Verifying pattern 2...
■ Pattern 2 verification passed
-> Restoring original data...
■ Sector test completed successfully
POST Word Update:
Before clear: 0xdead0081 (1101 1110 1010 1101 0000 0000 1000 0001)
After clear: 0xdcad0081 (1101 1100 1010 1101 0000 0000 1000 0001) [test bit cleared]
=== Test Result: PASSED ===
============================
PASSED
sysfly> md.l 0xe1fe0000 1
e1fe0000: dcad0081 ....
输出符号含义:
Symbol | Meaning | Usage scenario |
---|---|---|
■ |
Success/positive status | Test passed, detection successful, operation completed |
X |
Error/failure | Test failed, device detection failed, operation error |
-> |
Operation in progress | Steps being executed |
◆ |
Informational display | Configuration information, test parameters |
! |
Warning | Non-fatal error, warning message |
预期结果:
测试结果:PASSED
步骤 7:POST 字状态寄存器分析
状态寄存器地址
- 地址:
0xe1fe0000
(OCM HIGH SRAM) - 大小: 32 位字
- 访问: 通过 ‘md.l 0xe1fe0000 1’ 命令检查
SPI Flash 测试位定义
- SPI Flash 测试位:
0x02000000
(位 25) - 位位置: 25
位 25 状态 | 含义 | 描述 |
---|---|---|
|
测试未执行或测试通过 | 初始状态或成功后清除 |
1 |
测试正在进行或测试失败 | 执行期间设置,失败时保持 |
二进制位变化:
之前:1101 1110 1010 1101 0000 0000 1000 0001 (0xdead0081)
之后:1101 1100 1010 1101 0000 0000 1000 0001 (0xdcad0081)
^
第25位从1更改为0
结果验证:
=> md.l 0xe1fe0000 1
e1fe0000: dcad0081 # 第25位=0,测试成功
失败输出示例:
SPI_flash POST
=== SPI Flash POST 测试 ===
POST 字: 0xdcad0081 (1101 1100 1010 1101 0000 0000 1000 0001) -> 0xdead0081 (1101 1110 1010 1101 0000 0000 1000 0001) [测试位已设置]
硬件检测:
SF:探测 SPI 闪存失败
X 设备检测失败
=== 测试结果:失败 ===
============================
失败
故障状态验证:
=> md.l 0xe1fe0000 1
e1fe0000: dead0081 # 第 25 位=1,测试失败