【POST】Uboot-POST
本文最后更新于34 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com

什么是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硬件测试,主要功能如下:

  1. 自动检测SPI Flash设备 通过spi_flash_probe自动检测并初始化SPI Flash芯片,获取其容量、扇区大小等信息。

  2. 选择测试区域 选择Flash中间的一个扇区作为测试对象,避免破坏关键数据。

  3. 测试流程

    • 备份原始数据
    • 擦除扇区
    • 写入测试模式1(0x55),并校验
    • 擦除扇区
    • 写入测试模式2(0xAA),并校验
    • 恢复原始数据
  4. 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,测试失败
💡本内容采用 CC BY-NC-SA 4.0 协议,非商业转载需注明作者和出处,商业用途请联系作者授权,衍生作品需采用相同协议。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
🎵 背景音乐
正在播放
00:00 00:00