API GUIDE / EFUSE / HAL

G32S10 EFUSE HAL 接口

HAL 层接口:HAL_EFUSE_SEGMENT_ID_COUNT 仅为数量哨兵,不是合法段 ID。

1. 资源模型与公开类型

1.1 HAL 段 ID

typedef enum hal_efuse_segment_id {
    HAL_EFUSE_SEGMENT_CHIP_ID = 0,
    /* ... */
    HAL_EFUSE_SEGMENT_OSC_TRIM,
    HAL_EFUSE_SEGMENT_ID_COUNT,
} hal_efuse_segment_id_t;

HAL_EFUSE_SEGMENT_ID_COUNT 仅为数量哨兵,不是合法段 ID。段元数据由 HAL 固化,调用方不能传入裸地址替代段 ID。

核心段如下:

段 ID段名起始地址大小写入约束
CHIP_IDCHIP_ID0x0012受 STATE.CHIP_ID_PRT 保护。
PROTPROT0x0C2需要特殊段解锁。
SPECIALSPECIAL0x0E2需要特殊段解锁。
PROT_BKPROT_BK0x102需要特殊段解锁。
RETENTRETENT0x1214受 STATE.RETENT_PRT 保护。
SCBOOTSCBOOT0x2032受 STATE.SCBOOT_PRT 保护。
OS_KEYOS_KEY0x4016受 STATE.OS_KEY_PRT 保护。
USER_KEYUSER_KEY0x5016受 STATE.USER_KEY_PRT 保护。
SCBOOT_BKSCBOOT_BK0x6032受 STATE.SCBOOT_BAK_PRT 保护。
PSRAM_INIT_PROTPSRAM_INIT_PROT0x804需要特殊段解锁。
PSRAM_TYPE 至 PSRAM_RESERVED2PSRAM 参数段0x84 至 0xE4各 4分别受 PSRAM_STATE 对应保护位保护。
RESERVEDRESERVED0xE821可写;无保护位,写入前读回校验 old | input。
SPECIAL_BKSPECIAL_BK0xFD2需要特殊段解锁。
OSC_TRIMOSC_TRIM0xFF1受 STATE.OSC_TRIM_PRT 保护。

PSRAM 参数段包含 PSRAM_CFG_REG、PSRAM_TPU、PSRAM_TRST、PSRAM_TCEM、PSRAM_THS_INIT、PSRAM_TDPD_INIT、PSRAM_THS、PSRAM_TXPHS、PSRAM_TXHS、PSRAM_TDPD、PSRAM_TXPDPD、PSRAM_TXDPD、PSRAM_COMMAND_TIMING、PSRAM_RW_LATENCY、PSRAM_MR_INIT、PSRAM_CSN_DUMMY、PSRAM_TCQLZ、PSRAM_MEMORY_SPECIAL、PSRAM_TRP、PSRAM_RWDS_CTRL、PSRAM_TRWR 和 3 个保留参数段。

1.2 VDDQ GPIO 与 HAL 句柄

typedef struct hal_efuse_gpio {
    gpio_regs_t *gpiox;
    uint32_t pin_mask;
} hal_efuse_gpio_t;

typedef struct hal_efuse_handle {
    efuse_regs_t *efusex;
    hal_efuse_gpio_t gpio_power;
    bool is_initialized;
    bool is_timing_ready;
} hal_efuse_handle_t;
字段说明
efusex目标 EFUSE 寄存器实例,通常为 EFUSE。
gpio_power写入期间驱动 VDDQ 的单个有效 GPIO。GPIO 的申请、复用和方向配置由 BSP 负责。
is_initializedhal_efuse_handle_init() 成功后为 true。
is_timing_readyhal_efuse_set_timing() 成功后为 true。所有段读写都要求该标志为 true。

HAL 不拥有全局锁。同一 EFUSE 控制器的并发访问必须由 BSP 或调用方串行化。

2. HAL 接口规范

2.1 查询、初始化和时序

接口功能关键约束
hal_efuse_segment_is_valid(seg_id)检查段 ID 是否处于合法枚举范围。纯查询,可在中断上下文调用。
hal_efuse_get_segment_name(seg_id)返回段名;非法 ID 返回 "UNKNOWN"。纯查询,可在中断上下文调用。
hal_efuse_segment_is_protected(handle, seg_id, is_protected)读取 STATE 或 PSRAM_STATE 对应保护位,报告段是否已写保护。需已初始化且 timing-ready 的 handle;无保护位的段返回 false。
hal_efuse_handle_init(handle, efusex, gpio_power)绑定 EFUSE 实例与 VDDQ GPIO,清除 timing-ready 状态。gpio_power 必须是有效单 pin mask;已初始化的 handle 返回 IM_ERR_BUSY。
hal_efuse_handle_deinit(handle)强制 VDDQ 低电平并清空软件句柄。不访问 EFUSE 寄存器;返回后 handle 不可继续使用。
hal_efuse_set_timing(handle, rate_hz)按 EFUSE 输入时钟计算并写入 CFG。需已初始化、rate_hz >= 1000,且控制器空闲。成功前不得进行读写。
hal_efuse_regs_dump(handle)输出已命名的可读寄存器。仅当 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_EFUSE_ENABLE_DEBUG_HELPERS 均启用时可用,否则返回 IM_ERR_UNSUPPORTED。

hal_efuse_set_timing() 使用当前输入时钟周期计算 RD_ADJ、RD_STROBE、WR_ADJ 和 WR_STROBE。读窗口最小值为 500 ns,写调整最小值为 3.8 us,写 strobe 编程脉冲限制在 10.8 至 13.2 us。时钟变更后必须重新调用该接口。

2.2 段级读接口

error_code_t hal_efuse_read_segment(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    uint8_t *buf_out,
    uint32_t len_bytes);

error_code_t hal_efuse_read_segment_ex(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    uint8_t *buf_out,
    uint32_t len_bytes,
    bool use_backup);

从段首读取 1..segment_size byte。非 _ex 接口固定 use_backup = false;_ex 接口将 use_backup 映射至 CTRL.REDN,供备份/REDN 路径访问。

读操作流程:

  1. 检查 handle 已初始化且已配置时序。

  2. 检查段 ID 和 buffer/长度。

  3. 清理完成状态;特殊段自动向 SPEEN 写入解锁键。

  4. 以最多 4 byte 的事务写 CTRL.RDEN,轮询 STATE.RD_DONE。

  5. 从 DATA 按 little-endian 字节顺序拆包,最后复位控制器。

2.3 偏移读接口

error_code_t hal_efuse_read(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    uint32_t start_byte,
    uint32_t size_bytes,
    uint8_t *buf_out);

error_code_t hal_efuse_read_ex(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    uint32_t start_byte,
    uint32_t size_bytes,
    uint8_t *buf_out,
    bool use_backup);

start_byte + size_bytes 不得超过段大小。size_bytes == 0 时允许 buf_out == NULL,直接返回 IM_OK,不访问硬件。当前 HAL 为保持统一段访问语义,会先读取完整段到内部临时 buffer,再复制请求范围。

2.4 段级写接口

error_code_t hal_efuse_write_segment(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    const uint8_t *buf,
    uint32_t len_bytes);

error_code_t hal_efuse_write_segment_ex(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    const uint8_t *buf,
    uint32_t len_bytes,
    bool use_backup);

从段首写入 1..segment_size byte。写操作先读当前值,并对每个字节计算:

program_value = input & ~old
expected      = old | input

因此,已为 1 的 bit 不会重复烧写,输入中的 0 不能清除已有 1。若没有待烧写 bit,HAL 仍执行保护检查和读回验证。

写入一个 1 至 4 byte 分块时,HAL 会:

  1. 检查段是否允许写入,以及 STATE 或 PSRAM_STATE 对应保护位是否已置位。

  2. 特殊段自动解锁;普通段按元数据判断是否需要 SPEEN。

  3. 拉高 VDDQ GPIO,等待至少 10 us 稳定。

  4. 写 DATA 和 CTRL.WREN,轮询 STATE.WR_DONE。

  5. 无论事务结果如何都拉低 VDDQ,并清除写完成状态。

  6. 重新读取目标范围,验证结果等于 expected。

2.5 偏移写接口

error_code_t hal_efuse_write(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    const uint8_t *buf,
    uint32_t start_byte,
    uint32_t size_bytes);

error_code_t hal_efuse_write_ex(
    const hal_efuse_handle_t *handle,
    hal_efuse_segment_id_t seg_id,
    const uint8_t *buf,
    uint32_t start_byte,
    uint32_t size_bytes,
    bool use_backup);

偏移写会先读取完整段,再只编程请求范围,最后只验证请求范围。范围约束与偏移读相同;size_bytes == 0 直接返回 IM_OK,不触发任何编程。

2.6 HAL 调用约束

  • 除 hal_efuse_segment_is_valid() 和 hal_efuse_get_segment_name() 外,HAL 接口会访问硬件、GPIO 或轮询,不应在中断上下文调用。

  • 调用方应在时钟稳定后调用 hal_efuse_set_timing();任何可能改变 EFUSE 输入时钟的操作后均需重新配置。

  • VDDQ GPIO 应由 BSP 正确申请、初始化为输出低电平,并且不得被其他功能复用。

  • PROT、SPECIAL、PROT_BK、PSRAM_INIT_PROT 和 SPECIAL_BK 的写入可能改变后续段的可访问性,必须单独评审。