API GUIDE / SFC / HAL

SFC HAL 接口

sfc hal api

本文面向 BSP、驱动和中间件开发者,说明如何通过 G32S10 SFC HAL 配置 SPI Flash 控制器,并提供公开 API 参考。SFC 寄存器位域细节、NOR/NAND 命令语义、Flash 时序计算、DMA descriptor 格式、cache 维护和板级 pinmux 实现不在本文范围内。

SFC HAL 是控制器级接口,负责设备公共配置、Direct/CDT 事务参数、XIP 模式、CPU FIFO 数据端口、中断分发和状态查询。它不直接提供 NOR/NAND 读写擦除 API。普通应用应优先使用 SFC 中间件;只有需要自定义命令链、特殊数据通路或调试控制器时才直接使用本 HAL。

API 头文件:

  • code/include/drivers/g32s10/sfc/hal_sfc.h

公开实现位于 code/drivers/g32s10/sfc/hal_sfc.c。ll_sfc.h、sfc_regs.h 提供寄存器级 helper、SFC 实例和位域常量,属于 HAL 的内部依赖。code/middleware/sfc/ 中的 im_sfc 在 HAL 之上完成时钟配置、DMA/cache 处理、超时等待和 Flash 协议封装。

SFC HAL 不负责 SFC 时钟使能/切换、GPIO pinmux、Flash 上下电/复位、NOR/NAND 命令语义、DMA descriptor 构造、虚拟/物理地址转换、cache 维护、传输超时等待或 SoC 中断申请。调用方必须完成这些准备,并自行串行化同一控制器的并发访问。


1. 模块概览

1.1 软件分层

组件 作用
hal_sfc 公开 HAL,负责句柄生命周期、设备公共配置、Direct/CDT/XIP 配置、触发控制、CPU FIFO 数据端口、状态和中断回调
ll_sfc 寄存器位域读写 helper,仅供 HAL、驱动内部或高级调试使用
sfc_regs SFC 寄存器布局、SFC 实例、FIFO/CDT/状态位常量
im_sfc Common SFC 中间件,负责时钟、DMA、cache、FIFO 请求服务、IRQ 注册和超时等待;普通应用推荐使用
BSP/应用 提供时钟、引脚复用、Flash 电源/复位、IRQ 资源、XIP 地址映射和板级参数
外部 SPI Flash 接收 SFC 产生的命令、地址和数据帧,不由 HAL 解释其协议

HAL 当前只支持一个 SFC 寄存器块实例 SFC。所有带 hal_sfc_handle_t 的接口都要求句柄中的 instance == SFC。

SFC 的 HAL 软件状态只有 RESET 和 READY 两种,不包含 BUSY。硬件是否正在传输必须通过 hal_sfc_is_working()、hal_sfc_get_busy_state() 或状态位查询,不能只根据 HAL 句柄状态判断。

1.2 运行时关系

直接使用 HAL 时的典型路径:

BSP
  -> 使能 SFC 总线时钟和门控时钟
  -> 配置 CS#、SCLK、MOSI/MISO、WP#、HOLD# 等 pinmux
  -> 完成 Flash 上电和复位释放
  -> hal_sfc_handle_init()
  -> hal_sfc_init()
  -> hal_sfc_set_fifo_threshold() / hal_sfc_device_config()
  -> hal_sfc_status_poll_config()
  -> 可选:hal_sfc_register_irq_callback() + 平台 IRQ 注册
  -> Direct:hal_sfc_direct_transfer_config()
     或 CDT:hal_sfc_cdt_entry_config() + hal_sfc_cdt_execution_config()
     或 XIP:先配置连续读 phase,再配置 XIP
  -> hal_sfc_trigger_start()
  -> CPU 模式服务 FIFO 请求,DMA 模式等待 END,或查询硬件状态
  -> 处理 END / OVER / UNDER,清理状态
  -> hal_sfc_deinit()

使用 Common SFC 中间件时,上述控制器操作通常由 im_sfc_init()、im_sfc_direct_transfer()、im_sfc_cdt_transfer() 和 XIP API 封装。中间件还负责 DMA 地址转换、cache 操作、FIFO 请求服务和超时控制,因此不应在中间件事务进行期间绕过它直接操作同一 SFC 控制器。

1.3 头文件选择

使用方式 包含
配置 SFC HAL、读写 FIFO、处理中断和状态 <hal_sfc.h>
使用 SFC 实例和寄存器常量 已由 <hal_sfc.h> 间接包含
直接操作 LL 寄存器位域 仅驱动内部或高级调试使用,不建议应用直接依赖
访问 NOR/NAND 或使用控制器级事务封装 使用 code/middleware/sfc/include/ 下的中间件头文件

普通 HAL 应用只包含 <hal_sfc.h> 即可,不要自行定义 SFC 寄存器结构或重复实现 FIFO/CDT 布局。

1.4 使用约定

  • 返回 error_code_t 的接口以 IM_OK 表示成功,其他值表示失败。
  • 除 hal_sfc_handle_init()、hal_sfc_init()、hal_sfc_state_get()、hal_sfc_register_irq_callback() 和 hal_sfc_irq_handler() 等明确说明的接口外,配置、触发、数据端口和状态接口都要求句柄处于 HAL_SFC_STATE_READY。
  • 未调用 hal_sfc_handle_init() 或 hal_sfc_init() 时,依赖 READY 的接口返回 IM_ERR_NOT_READY;句柄为空、实例不是 SFC 或字段非法时返回 IM_ERR_INVALID_PARAM。
  • 控制器正在传输或 XIP 已开启时,大部分静态配置接口返回 IM_ERR_BUSY。关闭 XIP 是例外,允许在 XIP 读尚未完全释放时调用。
  • HAL 的配置接口只写寄存器和软件句柄,不会自动清 FIFO、启动传输、等待完成或清理 Flash 内部状态。
  • hal_sfc_trigger_start()、hal_sfc_trigger_stop()、hal_sfc_trigger_fifo_flush() 都是异步触发接口,不等待硬件完成。
  • CPU 模式的数据端口是 32-bit word 端口。传输长度以字节计,但 CPU FIFO 按 word 访问;字节序和 word unit 由 phase 配置决定,调用方负责把 word 拆装为实际协议字节流。
  • DMA 模式下 HAL 只写入硬件地址和 descriptor 使能位。地址转换、物理连续性、对齐、cache clean/invalidate 和 descriptor 链构造由中间件或调用方负责。
  • HAL 不提供互斥保护。多个任务、任务与 IRQ 同时访问配置、数据端口、回调数组或 Flash 时,调用方必须自行同步。
  • 回调运行在中断上下文,不能阻塞、不能睡眠,也不应执行耗时 Flash 操作。需要唤醒任务时使用 RTOS 的安全 IRQ API。
  • 本文按 CONFIG_SDK_HAL_PARAM_CHECK=y 描述参数检查行为。关闭 SDK/HAL 参数检查后,空指针、非法实例和字段范围不保证被拦截,调用方必须自行保证参数有效。

2. 典型流程

2.1 Kconfig 配置

SFC HAL 配置位于 code/drivers/g32s10/sfc/Kconfig,菜单路径为 Drivers -> G32S10 Drivers -> SFC driver。

配置项 默认值 说明
CONFIG_DRIVER_SFC n 编译 SFC HAL,即 hal_sfc.c

仅使用 HAL 时启用:

CONFIG_DRIVER_SFC=y

如果需要时钟、DMA、cache、FIFO 请求服务、IRQ 注册、超时等待和 NOR/NAND 协议封装,应启用中间件:

CONFIG_DRIVER_SFC=y
CONFIG_OS_API=y
CONFIG_MIDDLEWARE_SFC=y
CONFIG_MIDDLEWARE_SFC_NOR=y   # 或 CONFIG_MIDDLEWARE_SFC_NAND=y

CONFIG_MIDDLEWARE_SFC 依赖 CONFIG_DRIVER_SFC 和 CONFIG_OS_API。NOR 与 NAND 中间件在同一个 choice 中二选一。中间件示例位于 code/middleware/sfc/example/,示例 defconfig 为 code/middleware/sfc/example/sfc_example_defconfig。

2.2 板级准备

在调用 hal_sfc_init() 前完成以下准备:

  • 使能 SFC 总线时钟和门控时钟。默认绑定为 HAL_CPM_ID_CLK_CGU_SFC、HAL_CPM_ID_GATE_SFC,默认频率为 50 MHz。
  • 按板卡原理图配置 CS#、SCLK、MOSI、MISO,以及使用的 WP#、HOLD#/RESET# 引脚。HOLD# 和 WP# 的无效电平通过 hal_sfc_device_config() 配置。
  • 完成 Flash 上电和复位释放,确认器件不处于深度掉电、连续读或 busy 状态。
  • 根据 Flash 数据手册确定命令宽度、状态寄存器宽度、采样边沿、采样延时和 CE timing。
  • 使用 IRQ 时,完成平台中断申请、使能,并把 hal_sfc_irq_handler() 注册为 IRQ handler,user_ctx 传 SFC HAL 句柄。
  • 使用 XIP 时,确认 XIP 窗口地址、cache 属性、读命令、dummy 和退出命令;退出 XIP 的代码不能位于正在访问的 XIP 窗口。

时钟准备示例:

#include "hal_cpm.h"
#include "im_error.h"

error_code_t board_sfc_clock_prepare(void)
{
    error_code_t ret;

    ret = hal_cpm_clk_enable(HAL_CPM_ID_CLK_CGU_SFC);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_cpm_clk_set_rate_hz(HAL_CPM_ID_CLK_CGU_SFC,
                                  HAL_SFC_DEFAULT_CLOCK_HZ);
    if (ret != IM_OK) {
        return ret;
    }

    return hal_cpm_clk_enable(HAL_CPM_ID_GATE_SFC);
}

hal_sfc_clock_default_get() 只返回时钟绑定,不会执行上述操作。hal_sfc_init() 也不会使能或切换 CPM 时钟。

2.3 初始化 HAL

下面的配置值是 Common SFC 中间件的常用起点,不代表所有 Flash 都适用。实际产品必须按器件数据手册替换:

#include <stdbool.h>

#include "hal_sfc.h"
#include "im_error.h"

static hal_sfc_handle_t s_sfc;

static error_code_t app_sfc_hal_init(void)
{
    hal_sfc_device_config_t device = {
        .status_endian = HAL_SFC_ENDIAN_LSB_FIRST,
        .sample_delay = 0U,
        .command_type = HAL_SFC_COMMAND_1BYTE,
        .status_type = HAL_SFC_STATUS_TYPE_1BYTE,
        .ce_hold_time = HAL_SFC_CE_0P5_CYCLE,
        .ce_setup_time = HAL_SFC_CE_0P5_CYCLE,
        .ce_deselect_interval = HAL_SFC_CE_5_CYCLES,
        .sample_on_even_edge = false,
        .clock_active_low = false,
        .ce_inactive_level = HAL_SFC_LEVEL_HIGH,
        .hold_inactive_level = HAL_SFC_LEVEL_HIGH,
        .wp_inactive_level = HAL_SFC_LEVEL_HIGH,
    };
    hal_sfc_status_poll_config_t poll = {
        .poll_time = 100U,
        .status_expect = 0U,
        .status_mask = 0U,
    };
    error_code_t ret;

    ret = hal_sfc_handle_init(&s_sfc, SFC);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_init(&s_sfc);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_set_fifo_threshold(&s_sfc, 32U);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_device_config(&s_sfc, &device);
    if (ret != IM_OK) {
        return ret;
    }

    return hal_sfc_status_poll_config(&s_sfc, &poll);
}

说明:

  • hal_sfc_handle_init() 只清零句柄、绑定 SFC 并把状态置为 RESET,不访问硬件。
  • hal_sfc_init() 要求句柄处于 RESET,并确认控制器未 working、XIP 未开启;成功后状态为 READY。
  • hal_sfc_init() 只写入 HAL 的启动策略(关闭 SFC clock-gating),不会复位控制器,也不会加载 Flash 或寄存器默认配置。
  • device 中的 CE 字段是编码值,不是纳秒或统一周期数。CE setup/hold 只接受 0.5 到 2 周期,CE deselect 支持 0.5 到 8 周期。
  • hal_sfc_init() 失败后不要直接调用依赖 READY 的接口;应先修正返回错误或重新 hal_sfc_handle_init()。

2.4 最小 Direct 事务

下面用单 phase 的 WREN(0x06) 展示配置、启动和结束轮询流程。该命令没有数据 phase,因此 transfer_length 必须为 0:

#include "im_time.h"

static error_code_t app_sfc_wait_end(hal_sfc_handle_t *hsfc,
                                     uint32_t timeout_ms)
{
    uint32_t start_ms = sys_time_get_ms();

    for (;;) {
        uint32_t pending = 0U;
        error_code_t ret = hal_sfc_get_pending_status(hsfc, &pending);

        if (ret != IM_OK) {
            return ret;
        }

        if ((pending & (HAL_SFC_STATUS_OVER | HAL_SFC_STATUS_UNDER)) != 0U) {
            (void)hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_ALL);
            return IM_ERR_HARDWARE;
        }

        if ((pending & HAL_SFC_STATUS_END) != 0U) {
            (void)hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_END);
            return IM_OK;
        }

        if ((uint32_t)(sys_time_get_ms() - start_ms) >= timeout_ms) {
            (void)hal_sfc_trigger_stop(hsfc);
            (void)hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_ALL);
            return IM_ERR_TIMEOUT;
        }
    }
}

static error_code_t app_sfc_write_enable(hal_sfc_handle_t *hsfc)
{
    hal_sfc_direct_transfer_config_t config = {0};
    error_code_t ret;

    config.direction = HAL_SFC_DIRECTION_WRITE;
    config.phase_count = 1U;
    config.phases[0].phase.command_enabled = true;
    config.phases[0].phase.command = 0x06U;
    config.phases[0].phase.transfer_mode = HAL_SFC_TRANSFER_STANDARD_SPI;
    config.data_transfer.operation_mode = HAL_SFC_OPERATION_CPU;
    config.data_transfer.transfer_length = 0U;

    ret = hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_ALL);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_trigger_fifo_flush(hsfc);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_direct_transfer_config(hsfc, &config);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_trigger_start(hsfc);
    if (ret != IM_OK) {
        return ret;
    }

    return app_sfc_wait_end(hsfc, 100U);
}

说明:

  • app_sfc_wait_end() 使用 sys_time_get_ms(),调用前必须初始化系统计时;也可换成产品自己的超时机制。
  • 该示例按轮询方式工作,未启用中断。中断路径见 hal_sfc_irq_handler() 和 hal_sfc_register_irq_callback()。
  • 命令和 CE/采样参数必须与 Flash 数据手册一致;HAL 不检查 0x06 是否真的是当前器件的写使能命令。
  • hal_sfc_direct_transfer_config() 只配置事务;真正启动硬件的是 hal_sfc_trigger_start()。

2.5 数据通路选择

场景 推荐方式 调用方责任
小批量 CPU 读写 Direct + HAL_SFC_OPERATION_CPU 在 TRAN_REQ/RECE_REQ 时服务 FIFO,并遵守 transfer_length
大批量、低 CPU 占用 Direct/CDT + HAL_SFC_OPERATION_DMA 提供合法总线地址或 descriptor,处理 cache 和地址转换
多条命令与硬件状态轮询 CDT 构建完整 CDT 链,设置 status expect/mask 和 link_next
直接从 Flash 取指/读数据 XIP 先配置连续读 phase,处理 XIP 窗口、cache 和退出流程
普通 NOR/NAND 文件或 MTD 访问 im_sfc 中间件 使用 im_sfc_device、im_sfc_nor、im_sfc_nand 或 DFS

CPU FIFO 深度为 64 个 32-bit word。hal_sfc_data_write() 和 hal_sfc_data_read() 不检查 FIFO 满/空;需要批量搬运时优先使用 hal_sfc_data_write_burst() / hal_sfc_data_read_burst(),它们会在开始时检查一次空间或数据量。


3. 配置和关键类型

3.1 HAL 状态

枚举 值 含义
HAL_SFC_STATE_RESET 0 句柄已绑定,但 HAL 尚未初始化
HAL_SFC_STATE_READY 1 HAL 已初始化,可以配置和启动事务

RESET 不是硬件复位状态,READY 也不表示控制器空闲。硬件活动状态通过以下接口查询:

  • hal_sfc_is_working():SR.WORKING,控制器是否正在执行传输。
  • hal_sfc_is_xip_enabled():GLB1.XIP_EN,XIP 是否开启。
  • hal_sfc_get_busy_state():SR.BUSY,SPI 数据传输状态编码。

3.2 基础枚举

类型 取值 说明
hal_sfc_direction_t HAL_SFC_DIRECTION_READ=0, HAL_SFC_DIRECTION_WRITE=1 本次事务的数据方向
hal_sfc_operation_mode_t HAL_SFC_OPERATION_CPU=0, HAL_SFC_OPERATION_DMA=1 CPU FIFO 或 DMA 数据通路
hal_sfc_burst_mode_t 8BEAT=1, 16BEAT=2, 32BEAT=3 DMA 最大递增 burst 长度
hal_sfc_endian_t LSB_FIRST=0, MSB_FIRST=1 状态或数据字节序
hal_sfc_level_t LOW=0, HIGH=1 输出引脚无效电平
hal_sfc_command_type_t 1BYTE=0, 2BYTE=1 command 字段宽度
hal_sfc_status_type_t 1BYTE=0、2BYTE=1、3BYTE=2、4BYTE=3 硬件轮询状态宽度
hal_sfc_word_unit_t 1BYTE=0, 2BYTE=1, 4BYTE=2 数据 phase 的 word unit
hal_sfc_phase_format_t FORMAT_0=0, FORMAT_1=1 phase 组件顺序格式
hal_sfc_cdt_address_kind_t COLUMN=0, ROW=1, STATUS0=2, STATUS1=3 CDT entry 选择哪一个地址寄存器

hal_sfc_transfer_mode_t 的取值不是一个连续区间:

取值 枚举 说明
0 HAL_SFC_TRANSFER_STANDARD_SPI 标准单线 SPI
1 HAL_SFC_TRANSFER_DUAL_INPUT_OUTPUT_SPI Dual input/output
2 HAL_SFC_TRANSFER_DUAL_IO_SPI Dual I/O
3 HAL_SFC_TRANSFER_FULL_DUAL_IO_SPI Full dual I/O
5 HAL_SFC_TRANSFER_QUAD_INPUT_OUTPUT_SPI Quad input/output
6 HAL_SFC_TRANSFER_QUAD_IO_SPI Quad I/O
7 HAL_SFC_TRANSFER_FULL_QUAD_IO_SPI Full quad I/O

取值 4 未定义,传入会返回 IM_ERR_INVALID_PARAM。

3.3 CE timing 编码

hal_sfc_ce_cycles_t 以半个 SFC 时钟周期为单位编码。值 0 到 15 依次对应 0.5、1.0、1.5 到 8.0 个周期:

编码范围 周期范围 字段限制
0..15 0.5..8.0 周期 ce_deselect_interval 全范围可用
0..3 0.5..2.0 周期 ce_hold_time、ce_setup_time 只允许此范围

虽然枚举中定义了 HAL_SFC_CE_2P5_CYCLES 到 HAL_SFC_CE_8_CYCLES,hal_sfc_device_config() 不会接受把它们用于 CE hold/setup。相关限制来自控制器字段定义,不是枚举值缺失。

3.4 设备公共配置

hal_sfc_device_config_t 写入 DEV_CONF:

字段 类型 说明
status_endian hal_sfc_endian_t Flash 状态字节序
sample_delay uint8_t 采样延时编码,0..31
command_type hal_sfc_command_type_t 1 字节或 2 字节 command
status_type hal_sfc_status_type_t 硬件轮询状态宽度,1..4 字节
ce_hold_time hal_sfc_ce_cycles_t CE hold 编码,仅 0.5..2 周期
ce_setup_time hal_sfc_ce_cycles_t CE setup 编码,仅 0.5..2 周期
ce_deselect_interval hal_sfc_ce_cycles_t CE deselect 编码,0.5..8 周期
sample_on_even_edge bool 选择偶数 SFC 时钟边沿采样
clock_active_low bool SCLK 有效低电平
ce_inactive_level hal_sfc_level_t CE# 无效电平
hold_inactive_level hal_sfc_level_t HOLD# 无效电平
wp_inactive_level hal_sfc_level_t WP# 无效电平

该结构不包含 SFC 时钟频率、pinmux、Flash 地址或命令。hal_sfc_device_config() 直接写编码后的 DEV_CONF,不根据 ns/Hz 自动换算 timing。

3.5 Direct phase 和事务配置

单个 phase 由 hal_sfc_phase_config_t 描述:

字段 说明
address_width 地址宽度编码:0 表示无地址;1..5 表示 1..5 字节;6/7 都表示 6 字节
poll_enabled 开启硬件状态轮询;轮询会覆盖该 phase 的传输方向和 DATA_EN
command_enabled 发送 command 组件
phase_format 组件顺序格式 0/1
dummy_cycles dummy 时钟数,0..63
data_enabled 发送或接收数据组件
command 命令值;实际发送 1 或 2 字节由 command_type 决定,HAL 不检查高位是否超出 1 字节
data_endian 数据字节序
word_unit 数据 word unit
transfer_mode 该 phase 的 SPI 线模式

一个 phase 至少要启用 command、address、dummy 或 data 中至少一项,否则返回 IM_ERR_INVALID_PARAM。CLK_MODE/DDR 不在该结构中暴露,硬件保持 SDR 默认编码。

hal_sfc_direct_phase_config_t 在 phase 配置之外增加:

字段 说明
device_address Flash 侧地址低 32 位
device_address_plus Flash 侧地址高 16 位

hal_sfc_direct_transfer_config_t 描述一次完整 Direct 事务:

字段 说明
direction 所有有效 phase 共用的数据方向
phase_count 有效 phase 数,1..6
phases[6] 有效 phase 后未使用的槽位会被 HAL 清零
data_transfer CPU/DMA 数据通路、字节长度和 DMA 操作数

Direct 配置约束:

  • 最多只能有一个“非 polling 且 data_enabled=true”的 phase;多个会返回 IM_ERR_INVALID_PARAM。
  • polling phase 即使设置 data_enabled 也不算数据 phase,且轮询会覆盖方向和 DATA_EN。
  • 没有有效数据 phase 时,HAL 强制执行 transfer_length=0。
  • hal_sfc_direct_transfer_config() 会清除 CDT_EN、选择 Direct 模式,但不 flush FIFO、不启动传输、不等待 END。
  • 未使用的 phase slot 的地址和 CFG 寄存器会被清零,避免残留旧 phase。

3.6 数据通路和 DMA

hal_sfc_data_transfer_config_t:

字段 说明
operation_mode CPU 使用 DR/FIFO;DMA 使用硬件 DMA 数据通路
transfer_length SFC_TRAN_LEN,单位为字节;0 表示无数据传输
dma.descriptor_enabled 选择 DMA descriptor 模式;仅允许与 HAL_SFC_OPERATION_DMA 组合
dma.memory_address descriptor 关闭时使用的 DMA 内存地址
dma.descriptor_address descriptor 开启时使用的首个 descriptor 地址

这些地址是硬件地址操作数,不是调用方缓冲区指针。HAL 不执行虚拟/物理地址转换、地址范围检查、对齐检查、cache 维护或 descriptor 链构造。DMA 模式下 transfer_length 仍由调用方填写;CDT 中 data_enabled=false 时 HAL 会强制清 0。

3.7 状态轮询、CDT 和 XIP 配置

hal_sfc_status_poll_config_t:

字段 说明
poll_time 全局轮询间隔计数,CDT 轮询使用;Direct polling phase 也受硬件轮询机制影响
status_expect Direct 模式期望的 Flash 状态值
status_mask Direct 模式状态比较掩码

hal_sfc_cdt_entry_config_t 描述一个 4-word CDT entry:

字段 说明
link_next 执行完当前 entry 后继续执行下一项;索引 63 不能置位
address_kind 选择 column、row、status0 或 status1 地址寄存器
phase 当前 entry 的 phase 配置
status_expect polling entry 的期望状态
status_mask polling entry 的状态比较掩码

hal_sfc_cdt_execution_config_t 描述一次 CDT 链执行:

字段 说明
command_index 起始 CDT entry,0..63
data_enabled 本次执行是否有数据 phase;false 时强制 transfer_length=0
direction 本次执行的数据方向
column_address / row_address 对应地址寄存器值
status_address0 / status_address1 对应状态地址寄存器值
data_transfer CPU/DMA 数据通路和长度

HAL 只校验单个 entry 和单次 execution 的基本字段,不校验整条 CDT 链是否完整、link 是否能终止、链中有效数据 phase 是否唯一,也不启动或等待链执行。

hal_sfc_xip_mode_config_t:

字段 说明
mode_bits Flash 连续读 mode bits;关闭 XIP 时忽略
enabled true 进入 XIP,false 请求退出 XIP

XIP 读 phase 不在此结构中配置,必须先通过 hal_sfc_direct_transfer_config() 配置连续读命令、地址、dummy、mode 和线模式。XIP 的 mode byte、地址宽度和退出命令与 Flash 型号强相关,普通应用应使用中间件封装。

3.8 状态和中断位

状态位与中断 mask 位使用相同的 5 个 bit 位置,但语义不同:状态位表示“事件 pending”,mask 位表示“屏蔽该事件”。

状态枚举 值 中断枚举 说明
HAL_SFC_STATUS_UNDER 0x01 HAL_SFC_INTERRUPT_UNDER RX FIFO 下溢
HAL_SFC_STATUS_OVER 0x02 HAL_SFC_INTERRUPT_OVER TX FIFO 上溢
HAL_SFC_STATUS_RECE_REQ 0x04 HAL_SFC_INTERRUPT_RECE_REQ RX FIFO 有数据可读
HAL_SFC_STATUS_TRAN_REQ 0x08 HAL_SFC_INTERRUPT_TRAN_REQ TX FIFO 需要数据
HAL_SFC_STATUS_END 0x10 HAL_SFC_INTERRUPT_END 传输结束
HAL_SFC_STATUS_ALL 0x1F HAL_SFC_INTERRUPT_ALL 全部可清除状态/全部中断

IRQ callback 类型与状态位的映射不同,注册时必须使用 hal_sfc_irq_type_t:

callback 类型 值 对应状态/mask
HAL_SFC_IRQ_TYPE_END 0 END / 0x10
HAL_SFC_IRQ_TYPE_TRAN_REQ 1 TRAN_REQ / 0x08
HAL_SFC_IRQ_TYPE_RECE_REQ 2 RECE_REQ / 0x04
HAL_SFC_IRQ_TYPE_OVER 3 OVER / 0x02
HAL_SFC_IRQ_TYPE_UNDER 4 UNDER / 0x01

hal_sfc_get_pending_status() 返回原始 SR & 0x1F,不受 interrupt mask 影响。hal_sfc_irq_handler() 内部使用“pending 且未被 mask”的状态,并一次只处理一个事件。

3.9 HAL 句柄

struct hal_sfc_handle {
    sfc_regs_t *instance;
    hal_sfc_state_t state;
    hal_sfc_irq_callback_t irq_callbacks[HAL_SFC_IRQ_TYPE_COUNT];
    void *user_data;
};
字段 说明
instance SFC 寄存器块指针,正常为 SFC
state RESET 或 READY 软件状态
irq_callbacks[5] 按 hal_sfc_irq_type_t 索引的回调
user_data 调用方上下文,句柄初始化和 deinit 时清零,可由调用方赋值

句柄由调用方分配,HAL 不分配或释放内存。hal_sfc_handle_init() 会整体清零句柄,因此之前注册的回调和 user_data 都会失效。

3.10 常用常量

常量 值 说明
HAL_SFC_PHASE_COUNT 6 Direct phase 最大数量
HAL_SFC_IRQ_TYPE_COUNT 5 回调类型数量
HAL_SFC_FIFO_DEPTH_WORDS 64 CPU FIFO 深度,单位 32-bit word
HAL_SFC_FIFO_THRESHOLD_MIN 1 FIFO threshold 最小值
HAL_SFC_FIFO_THRESHOLD_MAX 63 FIFO threshold 最大值
HAL_SFC_SAMPLE_DELAY_CODE_MAX 31 采样延时编码最大值
HAL_SFC_PHASE_ADDRESS_WIDTH_MAX 7 地址宽度编码最大值
HAL_SFC_PHASE_DUMMY_CYCLES_MAX 63 dummy cycles 最大值
HAL_SFC_CDT_COMMAND_INDEX_MAX 63 最大 CDT entry 索引,共 64 项
HAL_SFC_DEFAULT_CLOCK_HZ 50 MHz 默认 SFC 总线时钟描述值
HAL_SFC_CCU_CFCR_ADDRESS 0x12200FE0 XIP CPU AHB burst 控制字段地址
HAL_SFC_CCU_CFCR_BURST_SHIFT 27 CPU AHB burst 字段起始位
HAL_SFC_CCU_CFCR_BURST_MASK 0x3 << 27 CPU AHB burst 字段掩码
HAL_SFC_CCU_CFCR_BURST_NORMAL 0 普通 burst 策略
HAL_SFC_CCU_CFCR_BURST_XIP 2 XIP burst 策略

SFC FIFO 是 64 个 32-bit word,但 FIFO threshold 最大只能配置为 63,不能配置为满深度 64。CDT 共 64 个 command entry,每项由 4 个 32-bit word 组成。

3.11 常见错误码和问题定位

3.11.1 常见错误码

错误码 HAL 中的典型原因 处理建议
IM_OK 调用成功 对异步接口仍需继续等待 END 并检查 OVER/UNDER
IM_ERR_INVALID_PARAM 空句柄/空配置、实例不是 SFC、枚举越界、phase 非法、多个数据 phase、CDT 索引 63 link、FIFO burst 请求过大 按 API 条目逐字段校验;不要在关闭参数检查后依赖 HAL 拦截
IM_ERR_NOT_READY 未 handle_init、未 init、已 deinit,或状态不是 READY 先完成 handle init 和 HAL init;检查时钟和实例
IM_ERR_BUSY 控制器 working、XIP 已开启、重复 init、启动正在执行的事务 等待 END/working 清零;退出 XIP;不要并发改写配置
IM_ERR_HARDWARE hal_sfc_data_write_burst() 读到 FIFO count > 64;轮询看到 OVER/UNDER 后由上层转换为硬件错误 停止、flush FIFO、清全部状态,检查数据通路和请求服务逻辑;必要时复位控制器
IM_ERR_TIMEOUT HAL 不返回;由调用方轮询或中间件在等待 END 时产生 确认时钟/pinmux/Flash 电源和命令;执行 stop/flush/clear 后重试

SFC 的 OVER/UNDER 是状态位,不是 error_code_t。裸 HAL 调用方必须在轮询或 IRQ 回调中把它们转换为错误处理;只等待 END 可能掩盖 FIFO 上溢/下溢。

3.11.2 常见问题定位

现象 优先检查
所有配置接口返回 IM_ERR_NOT_READY 是否先调用 hal_sfc_handle_init() 再调用 hal_sfc_init();是否在 hal_sfc_deinit() 后忘记重新 init
配置接口返回 IM_ERR_INVALID_PARAM phase_count 是否为 1..6;是否多个 DATA_EN 非 polling phase;CE hold/setup 是否超过 2 周期;descriptor 是否误用于 CPU 模式;burst 请求是否超过 FIFO 空间
启动返回 IM_ERR_BUSY 上次事务是否仍在 working;XIP 是否未退出;是否重复触发 start
END 永远不来 时钟/pinmux/Flash 电源、phase 顺序、command/address/dummy、CE 和采样 timing;是否真的调用了 trigger start;IRQ mask 或平台 IRQ 是否未使能
CPU 写数据后出现 OVER 是否在 FIFO 满时继续写;是否没有检查 get_fifo_word_count();是否把 DMA 事务误当 CPU 事务服务
CPU 读数据出现 UNDER 或读到旧值 是否在 RECE_REQ/FIFO count 有效前读;是否忽略 transfer_length 与实际 word 数;是否读取空 FIFO;RX 请求是否未被及时处理
IRQ handler 不进入或只处理一次 平台 IRQ 是否 request/enable;user_ctx 是否为 HAL 句柄;对应 mask 是否解除;错误/END handler 是否已把全部中断屏蔽;是否只处理了一个 pending 事件
回调访问 user_data 为 NULL 在 hal_sfc_handle_init()/deinit() 后是否重新设置 hsfc->user_data;是否注册回调前未绑定上下文
XIP 卡死或取指异常 是否从正在退出的 XIP 窗口取指;mode bits/地址宽度/读命令是否正确;XIP 窗口 cache 和映射是否正确;是否使用了中间件退出流程
轮询状态明明存在但 handler 不响应 状态是否被 interrupt mask 屏蔽;hal_sfc_get_pending_status() 看原始值,handler 看 pending & ~mask

4. API 参考

4.1 API 总览

hal_sfc.h

API 用途
hal_sfc_handle_init() 清零句柄并绑定 SFC 实例(需要空闲句柄)
hal_sfc_init() 初始化 HAL 并使能使用(要求 RESET)
hal_sfc_deinit() 软复位控制器并反初始化句柄(需要 READY)
hal_sfc_state_get() 读取 HAL 软件状态
hal_sfc_clock_default_get() 获取 SFC 时钟绑定和默认频率
hal_sfc_set_fifo_threshold() 设置 FIFO 请求阈值(需要 READY)
hal_sfc_enable_write_protect() 使能硬件 WP 输出控制(需要 READY)
hal_sfc_disable_write_protect() 关闭硬件 WP 输出控制(需要 READY)
hal_sfc_set_burst_mode() 设置 DMA 最大 burst 长度(需要 READY)
hal_sfc_device_config() 配置设备公共 timing/采样/电平(需要 READY)
hal_sfc_status_poll_config() 配置轮询间隔和 Direct 状态比较(需要 READY)
hal_sfc_direct_transfer_config() 配置一次 Direct 事务(需要 READY)
hal_sfc_phase_count_is_valid() 检查 phase 数量
hal_sfc_direct_transfer_has_data() 检查 Direct 配置是否搬运数据
hal_sfc_cdt_entry_config() 配置一个 CDT entry(需要 READY)
hal_sfc_cdt_execution_config() 配置一次 CDT 链执行(需要 READY)
hal_sfc_xip_mode_config() 进入或请求退出 XIP(需要 READY)
hal_sfc_xip_cpu_burst_config() 设置 XIP 前后 CPU AHB burst(需要 READY)
hal_sfc_trigger_start() 启动已配置事务/XIP(需要 READY)
hal_sfc_trigger_stop() 触发停止所有传输(需要 READY)
hal_sfc_trigger_fifo_flush() 触发 FIFO 清空(需要 READY)
hal_sfc_data_write() 写一个 word 到 CPU 数据端口(需要 READY)
hal_sfc_data_write_burst() 连续写多个 word(需要 READY)
hal_sfc_data_read() 从 CPU 数据端口读一个 word(需要 READY)
hal_sfc_data_read_burst() 连续读多个 word(需要 READY)
hal_sfc_get_device0_status() 读取 Flash device 0 实时状态(需要 READY)
hal_sfc_get_device1_status() 读取 Flash device 1 实时状态(需要 READY)
hal_sfc_get_fifo_word_count() 读取 FIFO 当前 word 数(需要 READY)
hal_sfc_is_working() 查询控制器 working 位(需要 READY)
hal_sfc_is_xip_enabled() 查询 XIP_EN(需要 READY)
hal_sfc_get_busy_state() 读取 SPI BUSY 编码(需要 READY)
hal_sfc_get_pending_status() 读取原始 pending 状态(需要 READY)
hal_sfc_register_irq_callback() 注册或注销一个 IRQ 回调
hal_sfc_irq_handler() 分发一个未屏蔽中断事件
hal_sfc_clear_status() 清除选中状态位(需要 READY)
hal_sfc_set_interrupt_mask() 整体替换中断 mask(需要 READY)
hal_sfc_interrupt_mask() 追加屏蔽中断(需要 READY)
hal_sfc_interrupt_unmask() 解除选中中断屏蔽(需要 READY)

4.2 通用约定

  • 返回 error_code_t 的接口以 IM_OK 表示成功。失败时不要使用本次调用写出或改写的数据,除非接口另有说明。
  • 所有需要句柄的接口都检查 hsfc != NULL、instance == SFC 和 state 字段合法性。关闭 CONFIG_SDK_HAL_PARAM_CHECK 后不保证拦截非法指针。
  • 参数检查中,非法枚举、越界索引、非法 bit mask、空配置结构和非法 phase 返回 IM_ERR_INVALID_PARAM。
  • 句柄未初始化、已 deinit 或状态不是 READY 时,依赖 READY 的接口返回 IM_ERR_NOT_READY。
  • 控制器 working 或 XIP 开启时,静态配置接口返回 IM_ERR_BUSY。具体的例外在对应 API 条目中说明。
  • HAL 不返回 IM_ERR_TIMEOUT。超时、命令未完成和 Flash 忙等待必须由调用方或中间件实现。
  • hal_sfc_data_write_burst() 在检测到异常 FIFO 计数时返回 IM_ERR_HARDWARE;其他接口通常不直接返回 IM_ERR_HARDWARE,硬件上溢/下溢通过 OVER/UNDER 状态报告。
  • 中断处理顺序固定为 RECE_REQ > TRAN_REQ > OVER > UNDER > END。每次调用只处理一个未屏蔽事件。
  • 带 _burst 的数据接口在开始时校验一次容量,之后连续访问数据端口;它们不阻塞、不分配内存,可在任务上下文或 IRQ 上下文调用。

4.3 生命周期 API

hal_sfc_handle_init()

error_code_t hal_sfc_handle_init(hal_sfc_handle_t *hsfc, sfc_regs_t *sfcx);

清零 HAL 句柄,绑定 SFC 寄存器块,并把软件状态置为 HAL_SFC_STATE_RESET。函数只初始化软件状态,不读写 SFC 寄存器。

参数 说明
hsfc 调用方分配的 HAL 句柄,不能为 NULL
sfcx SFC 寄存器块,当前必须为 SFC

返回:

  • IM_OK:句柄已清零并绑定。
  • IM_ERR_INVALID_PARAM:hsfc/sfcx 为空或 sfcx 不是 SFC。

说明:

  • 整个句柄会被清零,已有回调、user_data 和状态都会丢失。
  • 不要在控制器仍在传输、XIP 尚未退出时重新初始化句柄;本接口不会停止硬件或触发复位。
  • 初始化后必须再调用 hal_sfc_init() 才能使用依赖 READY 的接口。

hal_sfc_init()

error_code_t hal_sfc_init(hal_sfc_handle_t *hsfc);

按 HAL 启动策略初始化已绑定的 SFC 控制器。函数要求 HAL 状态为 RESET,并确认硬件未 working、XIP 未开启;成功时关闭 SFC clock-gating 并把状态置为 READY。

参数 说明
hsfc 已由 hal_sfc_handle_init() 初始化的句柄

返回:

  • IM_OK:句柄进入 READY。
  • IM_ERR_INVALID_PARAM:句柄为空、实例非法或状态字段非法。
  • IM_ERR_BUSY:句柄已经是 READY,或控制器 working/XIP 已开启。

说明:

  • 本接口不使能 CPM 时钟、不配置 pinmux、不复位 Flash,也不加载设备配置。
  • 本接口不会恢复 SFC 寄存器默认值;需要清除旧配置时应先 hal_sfc_deinit(),或显式重写相关字段。
  • hal_sfc_init() 成功后,调用方仍需配置 device_config、FIFO threshold 和轮询参数,再启动事务。
  • 如果之前调用了 hal_sfc_deinit(),可重新调用本接口进入 READY。

hal_sfc_deinit()

error_code_t hal_sfc_deinit(hal_sfc_handle_t *hsfc);

触发 SFC 控制器 soft reset,清空全部 IRQ 回调和 user_data,并把 HAL 状态置为 RESET。函数不关闭 CPM 时钟,也不恢复 GPIO 或 Flash 状态。

参数 说明
hsfc 处于 READY 的 HAL 句柄

返回:

  • IM_OK:复位已触发,句柄已反初始化。
  • IM_ERR_INVALID_PARAM:句柄为空、实例非法或状态字段非法。
  • IM_ERR_NOT_READY:句柄不处于 READY。
  • IM_ERR_BUSY:控制器仍在 working 或 XIP 仍开启。

说明:

  • soft reset 是异步触发,本接口不等待复位完成。需要再次使用时,重新调用 hal_sfc_init() 并重建配置。
  • deinit 前应先停止传输、处理 END/错误状态,并在需要时退出 Flash 的连续读模式。
  • 不负责 Flash 上下电、GPIO 释放或平台 IRQ 的 release/disable。

hal_sfc_state_get()

error_code_t hal_sfc_state_get(const hal_sfc_handle_t *hsfc,
                               hal_sfc_state_t *state_out);

读取 HAL 软件生命周期状态。

参数 说明
hsfc HAL 句柄,不能为 NULL
state_out 输出 RESET 或 READY,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄、实例、状态字段或输出指针非法。

说明:

  • 不要求 READY,可用于初始化失败后的诊断。
  • 返回值不能代替 hal_sfc_is_working();READY 时控制器仍可能正在传输或处于 XIP。

4.4 配置 API

hal_sfc_clock_default_get()

error_code_t hal_sfc_clock_default_get(hal_sfc_clock_config_t *config_out);

返回 SFC 的 CPM 时钟绑定和默认频率,不访问硬件,也不需要 HAL 句柄。

参数 说明
config_out 输出时钟配置,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:config_out == NULL。

输出固定为:

字段 值
clock_id HAL_CPM_ID_CLK_CGU_SFC
gate_id HAL_CPM_ID_GATE_SFC
name "cgu_sfc"
default_rate_hz HAL_SFC_DEFAULT_CLOCK_HZ(50 MHz)

本接口只描述绑定。调用方或中间件仍需使能时钟、设置频率和门控;hal_sfc_init() 不会自动执行这些操作。

hal_sfc_set_fifo_threshold()

error_code_t hal_sfc_set_fifo_threshold(hal_sfc_handle_t *hsfc,
                                        uint8_t threshold);

设置 CPU FIFO 的请求 threshold,单位为 32-bit word。

参数 说明
hsfc READY 状态的 HAL 句柄
threshold HAL_SFC_FIFO_THRESHOLD_MIN 到 HAL_SFC_FIFO_THRESHOLD_MAX,即 1..63

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄非法或 threshold 不在 1..63。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • threshold 影响 TX/RX FIFO 请求的触发时机。具体水位行为应结合 Flash 速率、CPU 延迟和中间件实现调整。
  • 不能设置为 64;FIFO 满深度为 64,但 threshold 字段最大合法值为 63。

hal_sfc_enable_write_protect()

error_code_t hal_sfc_enable_write_protect(hal_sfc_handle_t *hsfc);

使能 SFC 的硬件写保护输出控制。

参数 说明
hsfc READY 状态的 HAL 句柄

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄非法。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • 该接口只改变 SFC 侧 WP# 输出控制,不会修改 Flash 状态寄存器中的块保护位,也不能阻止软件发出的写命令。
  • WP# 的无效电平由 hal_sfc_device_config().wp_inactive_level 配置;外部上拉/下拉仍由板级设计决定。

hal_sfc_disable_write_protect()

error_code_t hal_sfc_disable_write_protect(hal_sfc_handle_t *hsfc);

关闭 SFC 的硬件写保护输出控制。

参数 说明
hsfc READY 状态的 HAL 句柄

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄非法。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • 关闭 WP 输出不等于解除 Flash 内部写保护;Flash 的 SR 保护位必须按器件协议单独处理。
  • 在 Flash 编程/擦除前是否需要保持 WP 无效,应由板级和器件要求决定。

hal_sfc_set_burst_mode()

error_code_t hal_sfc_set_burst_mode(hal_sfc_handle_t *hsfc,
                                    hal_sfc_burst_mode_t burst_mode);

设置 DMA 最大递增 burst 长度。

参数 说明
hsfc READY 状态的 HAL 句柄
burst_mode HAL_SFC_BURST_8BEAT、HAL_SFC_BURST_16BEAT 或 HAL_SFC_BURST_32BEAT

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄非法或 burst_mode 不在合法枚举范围。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • 该字段只影响 DMA 数据通路,不改变 CPU FIFO 访问宽度。
  • HAL 不检查 DMA 地址、缓冲区长度或总线对齐;这些由调用方/中间件保证。

hal_sfc_device_config()

error_code_t hal_sfc_device_config(hal_sfc_handle_t *hsfc,
                                   const hal_sfc_device_config_t *config);

配置 SFC 设备公共 timing、采样边沿、状态宽度和引脚无效电平,并写入 DEV_CONF。

参数 说明
hsfc READY 状态的 HAL 句柄
config 设备公共配置,不能为 NULL

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄或配置非法;常见原因是 CE hold/setup 超过 2 周期、CE deselect 超过 8 周期、采样延时大于 31、命令或状态宽度枚举非法。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • 函数直接写编码值,不根据时钟频率或 ns 计算 timing。
  • ce_hold_time 和 ce_setup_time 只接受 0.5..2 周期编码;ce_deselect_interval 接受 0.5..8 周期。
  • 不配置 SFC 时钟频率、pinmux、Flash 的 SR 保护位或 XIP 模式。
  • 修改采样边沿/延时和 CE timing 前应停止传输并清空 FIFO;配置后首次使用应通过回读或示波器验证。

hal_sfc_status_poll_config()

error_code_t hal_sfc_status_poll_config(
    hal_sfc_handle_t *hsfc,
    const hal_sfc_status_poll_config_t *config);

配置硬件轮询的全局间隔,以及 Direct 模式下的期望状态和比较掩码。

参数 说明
hsfc READY 状态的 HAL 句柄
config 轮询配置,不能为 NULL

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄或配置为空。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • poll_time 是硬件计数,不是毫秒。具体轮询间隔与 SFC 时钟和控制器字段宽度有关。
  • Direct polling phase 由 hal_sfc_direct_transfer_config() 配置;本接口提供全局间隔和 Direct 的 expect/mask。
  • CDT polling 使用本接口的 poll_time,但 expect/mask 来自每个 CDT entry。
  • HAL 不等待轮询完成,也不把状态比较结果转换为 error_code_t。

4.5 Direct 模式 API

hal_sfc_direct_transfer_config()

error_code_t hal_sfc_direct_transfer_config(
    hal_sfc_handle_t *hsfc,
    const hal_sfc_direct_transfer_config_t *config);

配置一次 Direct 事务的全部 phase、地址、方向和数据通路,并选择 Direct 模式。

参数 说明
hsfc READY 状态的 HAL 句柄
config Direct 事务配置,不能为 NULL

返回:

  • IM_OK:配置写入成功。
  • IM_ERR_INVALID_PARAM:句柄/配置非法、phase 数不在 1..6、方向非法、数据通路非法、任一 phase 字段非法,或多个非 polling data phase。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • 函数会写 6 个 phase slot,并清零未使用 slot 的 CFG 和地址寄存器,避免旧配置残留。
  • 最多一个“非 polling 且 DATA_EN”的 phase。polling phase 即使 data_enabled=true 也不计入数据 phase。
  • 没有有效数据 phase 时,HAL 强制 transfer_length=0,覆盖调用方传入的非零值。
  • 函数选择 Direct 模式(清除 CDT_EN)并写入方向、phase 数和数据通路设置。
  • 不 flush FIFO、不预装 TX 数据、不启动事务、不等待 END,也不清理 OVER/UNDER。
  • CPU 模式不会自动服务 FIFO;调用方必须在 TRAN_REQ/RECE_REQ 时读写数据端口,或改用 DMA 模式。

hal_sfc_phase_count_is_valid()

bool hal_sfc_phase_count_is_valid(uint8_t phase_count);

检查 Direct 模式 phase 数量是否有效。

参数 说明
phase_count 待检查的 phase 数量

返回:

  • true:1 <= phase_count <= HAL_SFC_PHASE_COUNT。
  • false:0 或大于 6。

说明:

  • 纯软件 helper,不访问硬件,不要求句柄。
  • 传入 0 返回 false;Direct 事务至少需要一个 phase。

hal_sfc_direct_transfer_has_data()

bool hal_sfc_direct_transfer_has_data(
    const hal_sfc_direct_transfer_config_t *config);

检查 Direct 配置中是否存在有效的数据 phase。

参数 说明
config Direct 配置,可为 NULL

返回:

  • true:至少存在一个非 polling 且 data_enabled=true 的 phase。
  • false:config == NULL、phase_count 非法,或没有有效数据 phase。

说明:

  • 扫描范围受 phase_count 和 HAL_SFC_PHASE_COUNT 限制,不会越界读取 phase 数组。
  • polling phase 即使 DATA_EN 为 1 也返回 false。
  • false 可能表示“合法的无数据事务”,也可能表示配置无效;应先用 hal_sfc_phase_count_is_valid() 区分。

hal_sfc_direct_transfer_config() 使用该 helper 决定是否强制清 transfer_length。调用方也可在配置前用它校验自己的数据路径。

4.6 CDT 模式 API

hal_sfc_cdt_entry_config()

error_code_t hal_sfc_cdt_entry_config(
    hal_sfc_handle_t *hsfc,
    uint8_t entry_index,
    const hal_sfc_cdt_entry_config_t *config);

配置一个 4-word CDT command entry,包括 link、地址选择、phase、期望状态和状态掩码。

参数 说明
hsfc READY 状态的 HAL 句柄
entry_index CDT entry 索引,0..HAL_SFC_CDT_COMMAND_INDEX_MAX(0..63)
config 单个 CDT entry 配置,不能为 NULL

返回:

  • IM_OK:4 个 word 写入成功。
  • IM_ERR_INVALID_PARAM:句柄/配置非法、entry_index 越界、地址类型非法、phase 非法,或索引 63 设置了 link_next。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • entry 由 entry_index * 4 定位,依次写入 word0、word1、status_expect 和 status_mask。
  • 最后一个 entry(索引 63)不能链接到不存在的下一项。
  • HAL 不校验整条链:不检查起始索引之前/之后的残留项、链是否闭环、最后一项是否终止,也不检查链中是否只有一个有效数据 phase。
  • 配置 entry 不会启动链;链执行参数由 hal_sfc_cdt_execution_config() 写入,启动仍使用 hal_sfc_trigger_start()。

hal_sfc_cdt_execution_config()

error_code_t hal_sfc_cdt_execution_config(
    hal_sfc_handle_t *hsfc,
    const hal_sfc_cdt_execution_config_t *config);

配置一次 CDT 命令链执行的起始索引、地址寄存器、数据方向和 CPU/DMA 数据通路,并选择 CDT 模式。

参数 说明
hsfc READY 状态的 HAL 句柄
config CDT 执行配置,不能为 NULL

返回:

  • IM_OK:配置写入成功。
  • IM_ERR_INVALID_PARAM:句柄/配置非法、command_index 大于 63、方向非法或数据通路非法。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器 working 或 XIP 开启。

说明:

  • 函数写入 command index 寄存器、column/row/status 地址寄存器,并选择 CDT_EN。
  • data_enabled=false 时强制 transfer_length=0,即使 data_transfer.transfer_length 非零。
  • data_enabled 不会检查 CDT 链中是否确实存在数据 phase;调用方必须在构建链时保证一致。
  • 函数不 flush FIFO、不清 pending、不启动链、不等待 END。
  • CPU 模式仍需在请求中断/轮询中服务 FIFO;DMA 模式需提前准备物理地址或 descriptor 链。

4.7 XIP API

hal_sfc_xip_mode_config()

error_code_t hal_sfc_xip_mode_config(
    hal_sfc_handle_t *hsfc,
    const hal_sfc_xip_mode_config_t *config);

使能 XIP,或请求退出 XIP 并清除 mode bit。

参数 说明
hsfc READY 状态的 HAL 句柄
config XIP 模式配置,不能为 NULL

返回:

  • IM_OK:使能请求或退出请求已被接受。
  • IM_ERR_INVALID_PARAM:句柄或配置为空。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:enabled=true 时控制器 working 或 XIP 已经开启。

说明:

  • enabled=true:写 mode_bits,选择 Flash device 0,并置 XIP_EN。HAL 不验证 mode_bits 与具体 Flash 是否匹配。
  • 使能前必须先通过 hal_sfc_direct_transfer_config() 配置连续读 phase;HAL 不会自动生成 XIP 读命令。
  • enabled=false:允许在 working 或 XIP 期间调用。硬件可能延迟到当前 XIP read 结束后才真正释放 XIP_EN;调用方必须轮询 hal_sfc_is_xip_enabled() 确认。
  • 退出请求会清除 XIP_EN 和 mode bit;mode_bits 被忽略。
  • 控制器退出 XIP 不等于 Flash 已退出连续读模式。是否需要发送 0xFF 等退出帧由 Flash 型号和中间件决定。
  • 本接口不启动 XIP 读流程、不切换 CPU AHB burst、不处理 XIP 窗口 cache,也不保证退出代码位于非 XIP 内存。

hal_sfc_xip_cpu_burst_config()

error_code_t hal_sfc_xip_cpu_burst_config(hal_sfc_handle_t *hsfc,
                                          bool enable);

设置 G32S10 CCU_CFCR[28:27] 的 CPU AHB burst 策略。

参数 说明
hsfc READY 状态的 HAL 句柄
enable true 写入值 2(XIP 策略);false 写入值 0(普通策略)

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 写入地址固定为 HAL_SFC_CCU_CFCR_ADDRESS(0x12200FE0),不是 SFC 寄存器块内的字段。
  • 该接口不检查 working/XIP,也不返回 IM_ERR_BUSY。调用方必须保证在安全的系统状态下切换,避免影响正在执行的 AHB 访问。
  • 推荐顺序:使能 XIP 前设为 true,XIP 退出完成后再设为 false。
  • 该字段不会自动失效 cache,也不会建立 XIP 地址映射。

4.8 触发和数据端口 API

hal_sfc_trigger_start()

error_code_t hal_sfc_trigger_start(hal_sfc_handle_t *hsfc);

写入 TRIG.START,启动已配置的 Direct、CDT 或 XIP 流程。

参数 说明
hsfc READY 状态的 HAL 句柄

返回:

  • IM_OK:启动触发已写入。
  • IM_ERR_INVALID_PARAM:句柄非法。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_BUSY:控制器已经 working。

说明:

  • 不校验事务配置是否完整、phase 数量是否合理、FIFO 是否预装或 Flash 是否就绪。
  • 不等待启动完成,也不等待 END。
  • 启动后调用方必须通过 IRQ 或轮询处理 RECE_REQ、TRAN_REQ、OVER、UNDER、END。
  • 如果使用 IRQ,确认对应 mask 已解除;HAL 默认不会自动解除 mask。

hal_sfc_trigger_stop()

error_code_t hal_sfc_trigger_stop(hal_sfc_handle_t *hsfc);

写入 TRIG.STOP,触发终止所有 SFC 传输。

参数 说明
hsfc READY 状态的 HAL 句柄

返回:

  • IM_OK:停止触发已写入。
  • IM_ERR_INVALID_PARAM:句柄非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 不检查控制器是否 working,也不返回 IM_ERR_BUSY。
  • 异步触发,不等待硬件停止;需要确认状态时应检查 hal_sfc_is_working() 或 pending 状态。
  • 不自动 flush FIFO、不清状态、不恢复配置,也不保证 Flash 内部正在执行的编程/擦除命令被撤销。
  • 超时或异常中止后,通常应停止、flush FIFO、清状态,再重新配置事务。

hal_sfc_trigger_fifo_flush()

error_code_t hal_sfc_trigger_fifo_flush(hal_sfc_handle_t *hsfc);

写入 TRIG.FLUSH,触发硬件 FIFO 清空。

参数 说明
hsfc READY 状态的 HAL 句柄

返回:

  • IM_OK:flush 触发已写入。
  • IM_ERR_INVALID_PARAM:句柄非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 异步触发,不等待 FIFO 清空完成。
  • 不检查 working/XIP;不要在不了解事务状态时随意 flush,否则可能丢失已接收数据和未发送数据。
  • 不清 pending 状态,也不复位控制器;通常与 hal_sfc_clear_status() 配合使用。

hal_sfc_data_write()

error_code_t hal_sfc_data_write(hal_sfc_handle_t *hsfc, uint32_t data);

向 SFC CPU 数据端口写一个 32-bit word。

参数 说明
hsfc READY 状态的 HAL 句柄
data 待写入 FIFO 的 word

返回:

  • IM_OK:word 已写入数据端口。
  • IM_ERR_INVALID_PARAM:句柄非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 不检查 FIFO 是否已满。FIFO 满时继续写可能触发 OVER 或破坏传输,调用方必须先用 hal_sfc_get_fifo_word_count() 确认空闲空间。
  • 不检查当前方向、phase 是否启用 DATA_EN,也不判断是否应响应 TRAN_REQ。
  • word 的字节排列由 phase 的 data_endian 和 word_unit 决定;调用方负责按 Flash 协议组装数据。
  • DMA 模式不应通过本接口搬运同一事务的数据。

hal_sfc_data_write_burst()

error_code_t hal_sfc_data_write_burst(hal_sfc_handle_t *hsfc,
                                      const uint32_t *data_in,
                                      uint32_t word_count);

连续向 CPU 数据端口写入多个 word,用于一次服务 TX FIFO 请求。

参数 说明
hsfc READY 状态的 HAL 句柄
data_in 源缓冲区,至少包含 word_count 个 word
word_count 要写入的 word 数,必须大于 0,且不超过当前 FIFO 空闲空间

返回:

  • IM_OK:全部 word 写入成功。
  • IM_ERR_INVALID_PARAM:句柄/缓冲区为空、word_count==0,或 word_count 大于 64 - FIFO count。
  • IM_ERR_NOT_READY:句柄未初始化。
  • IM_ERR_HARDWARE:读到的 FIFO count 大于 64,说明硬件计数异常。

说明:

  • 函数在开始时读取一次 FIFO count,计算 free_words = 64 - count,然后连续写数据端口。TX FIFO 在同一 burst 内只会消耗数据,因此一次检查可覆盖整次调用。
  • 请求过大时整次调用返回 IM_ERR_INVALID_PARAM,不会写入部分数据。
  • 不阻塞、不分配内存,可从任务上下文或 IRQ 上下文调用;调用方仍需处理并发访问。
  • 不自动清除 TRAN_REQ;IRQ 回调中应结合中间件状态决定是否清除或屏蔽该请求。
  • DMA 模式不使用本接口。

hal_sfc_data_read()

error_code_t hal_sfc_data_read(const hal_sfc_handle_t *hsfc,
                               uint32_t *data_out);

从 SFC CPU 数据端口读取一个 32-bit word。

参数 说明
hsfc READY 状态的 HAL 句柄
data_out 输出 word,不能为 NULL

返回:

  • IM_OK:读取完成。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 不检查 FIFO 是否为空。空 FIFO 读出的数据没有可检测的有效位,可能为旧值或无效值。
  • 调用前应先检查 hal_sfc_get_fifo_word_count()、RECE_REQ 或中间件维护的剩余长度。
  • 不判断当前 phase 是否启用 DATA_EN,也不负责清除 RECE_REQ。
  • DMA 模式不应通过本接口读取同一事务的数据。

hal_sfc_data_read_burst()

error_code_t hal_sfc_data_read_burst(const hal_sfc_handle_t *hsfc,
                                     uint32_t *data_out,
                                     uint32_t word_count);

连续从 CPU 数据端口读取多个 word,用于一次排空 RX FIFO 请求。

参数 说明
hsfc READY 状态的 HAL 句柄
data_out 目的缓冲区,至少包含 word_count 个 word
word_count 要读取的 word 数,必须大于 0,且不超过当前 FIFO 中的 word 数

返回:

  • IM_OK:全部 word 读取成功。
  • IM_ERR_INVALID_PARAM:句柄/缓冲区为空、word_count==0,或请求数大于当前 FIFO word 数。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 函数开始时读取 FIFO count 并校验;请求过大时整次调用失败,不会读取超出当前数据量的 word。
  • 不阻塞、不分配内存,可从任务上下文或 IRQ 上下文调用;调用方仍需处理并发访问。
  • 不自动清除 RECE_REQ,也不跟踪事务总长度;调用方必须结合 transfer_length 和已搬运字节数判断是否完成。
  • DMA 模式不使用本接口。

4.9 状态查询 API

hal_sfc_get_device0_status()

error_code_t hal_sfc_get_device0_status(const hal_sfc_handle_t *hsfc,
                                        uint32_t *status_out);

读取 Flash device 0 的实时状态寄存器 DEV0_STA_RT。

参数 说明
hsfc READY 状态的 HAL 句柄
status_out 输出原始状态值,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 返回硬件字段原值,HAL 不按 NOR/NAND 状态位解释。
  • 该状态不是普通 pending 中断状态;需要 END/OVER/UNDER 时使用 hal_sfc_get_pending_status()。

hal_sfc_get_device1_status()

error_code_t hal_sfc_get_device1_status(const hal_sfc_handle_t *hsfc,
                                        uint32_t *status_out);

读取 Flash device 1 的实时状态寄存器 DEV1_STA_RT。

参数 说明
hsfc READY 状态的 HAL 句柄
status_out 输出原始状态值,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 返回原始字段值,不表示当前事务一定选择了 device 1。
  • HAL 没有提供通用的 device 选择配置 API;多 Flash 配置需要额外寄存器配置或中间件支持。

hal_sfc_get_fifo_word_count()

error_code_t hal_sfc_get_fifo_word_count(const hal_sfc_handle_t *hsfc,
                                         uint32_t *count_out);

读取 SFC FIFO 当前包含的 32-bit word 数。

参数 说明
hsfc READY 状态的 HAL 句柄
count_out 输出 FIFO word 数,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 正常 FIFO 深度为 64,计数可用于 burst 前的空间/数据量检查。
  • 计数由硬件实时更新,读取后到实际读写之间可能发生变化;调用方需保证同一 FIFO 不被其他执行流同时服务。
  • 不要用该接口替代 END/OVER/UNDER 事件处理。

hal_sfc_is_working()

error_code_t hal_sfc_is_working(const hal_sfc_handle_t *hsfc,
                                bool *working_out);

查询 SR.WORKING,判断控制器是否正在执行传输。

参数 说明
hsfc READY 状态的 HAL 句柄
working_out 输出 working 状态,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • true 表示硬件仍在执行事务;false 不保证 END 已产生或 FIFO 已排空。
  • 配置接口使用该状态拒绝并发修改;启动前可用它确认控制器空闲。

hal_sfc_is_xip_enabled()

error_code_t hal_sfc_is_xip_enabled(const hal_sfc_handle_t *hsfc,
                                    bool *enabled_out);

查询 GLB1.XIP_EN。

参数 说明
hsfc READY 状态的 HAL 句柄
enabled_out 输出 XIP 使能状态,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 退出 XIP 是异步的;调用 hal_sfc_xip_mode_config(false) 后必须轮询本接口直到返回 false。
  • XIP_EN 清零不等于 Flash 已退出连续读模式,Flash 侧退出流程仍需单独完成。

hal_sfc_get_busy_state()

error_code_t hal_sfc_get_busy_state(const hal_sfc_handle_t *hsfc,
                                    uint32_t *busy_out);

读取 SR.BUSY 两位编码。

参数 说明
hsfc READY 状态的 HAL 句柄
busy_out 输出 0..3 的原始编码,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 该字段用于区分 SPI idle、发送、接收和等待传输等状态,具体编码含义以 SoC 寄存器手册为准。
  • 它不同于 HAL 软件状态和 WORKING 位;诊断时应结合 pending 状态、FIFO count 和 Flash 返回值。

hal_sfc_get_pending_status()

error_code_t hal_sfc_get_pending_status(const hal_sfc_handle_t *hsfc,
                                        uint32_t *status_out);

读取原始的 END、TRAN_REQ、RECE_REQ、OVER、UNDER pending 状态。

参数 说明
hsfc READY 状态的 HAL 句柄
status_out 输出 ORed HAL_SFC_STATUS_*,不能为 NULL

返回:

  • IM_OK:读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针非法。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 返回值为 SR & 0x1F,不受 interrupt mask 影响;IRQ handler 内部使用的是“pending 且未被 mask”的快照。
  • 读取不会自动清除状态,必须调用 hal_sfc_clear_status() 清除选中位。
  • 应先保存本次快照,再根据快照处理;清除后重新读取可能得到新的硬件事件。

4.10 中断和状态清除 API

hal_sfc_register_irq_callback()

error_code_t hal_sfc_register_irq_callback(
    hal_sfc_handle_t *hsfc,
    const hal_sfc_irq_type_t type,
    hal_sfc_irq_callback_t callback);

注册或注销一个 SFC IRQ callback。

参数 说明
hsfc SFC HAL 句柄,不能为 NULL
type HAL_SFC_IRQ_TYPE_END 到 HAL_SFC_IRQ_TYPE_UNDER
callback 回调函数;传 NULL 表示注销该类型

返回:

  • IM_OK:注册/注销成功。
  • IM_ERR_INVALID_PARAM:句柄、实例或类型非法。

说明:

  • 本接口不要求 READY,也不检查 working/XIP;但 hal_sfc_handle_init() 和 hal_sfc_deinit() 会清空全部回调。
  • callback 运行在 IRQ 上下文,不能阻塞;可通过 hsfc->user_data 获取调用方上下文。
  • 回调数组没有并发保护。不要在回调正在执行时从其他任务改写同一槽位。
  • 本接口只修改 HAL 回调表;平台 IRQ 的 request、enable、disable 和 release 仍由调用方完成。

hal_sfc_irq_handler()

void hal_sfc_irq_handler(uint32_t irq_id, void *user_ctx);

分发一个未屏蔽的 SFC 中断事件。

参数 说明
irq_id 平台中断号;SFC handler 不使用该值
user_ctx SFC HAL 句柄指针,不能为 NULL

返回:无。参数非法、实例错误或没有未屏蔽 pending 时直接返回。

处理优先级和动作:

优先级 事件 动作
1 RECE_REQ 清除 RECE_REQ,然后调用 RECE_REQ callback
2 TRAN_REQ 清除 TRAN_REQ,然后调用 TRAN_REQ callback
3 OVER 屏蔽全部中断、清除全部状态,然后调用 OVER callback
4 UNDER 屏蔽全部中断、清除全部状态,然后调用 UNDER callback
5 END 屏蔽全部中断、清除 END,然后调用 END callback

说明:

  • 每次调用只处理一个事件;若一次中断快照中有多个 pending 位,其余事件需要后续中断或再次调用 handler 处理。
  • 请求事件先清自身状态再回调,回调可以立即读写 FIFO;错误事件和 END 会先屏蔽全部中断,后续继续传输前必须重新配置 mask。
  • pending 计算为 SR & ~SFC_INTC & 0x1F;被 mask 的事件不会触发回调,但仍可通过 hal_sfc_get_pending_status() 观察到。
  • 不检查 HAL 状态是否为 READY,也不检查 user_ctx 指向的句柄是否仍在有效生命周期内。调用方必须保证句柄存活。
  • 回调可以为 NULL;事件仍会被清除/屏蔽,只是不执行用户动作。

hal_sfc_clear_status()

error_code_t hal_sfc_clear_status(hal_sfc_handle_t *hsfc,
                                  uint32_t status_flags);

通过 SCR 写 1 清除选中的 pending 状态位。

参数 说明
hsfc READY 状态的 HAL 句柄
status_flags 非零 ORed HAL_SFC_STATUS_*,只允许 0x1F 内位

返回:

  • IM_OK:清除请求已写入。
  • IM_ERR_INVALID_PARAM:句柄非法、status_flags==0 或包含 0x1F 之外的位。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 写 1 清除对应位,写 0 不影响;可使用 HAL_SFC_STATUS_ALL 清除全部可清除状态。
  • 不检查 working/XIP,可用于事务结束或错误恢复。
  • 清除 pending 状态不等于终止硬件传输、flush FIFO 或恢复 Flash;需要时组合使用 trigger/stop/flush。

hal_sfc_set_interrupt_mask()

error_code_t hal_sfc_set_interrupt_mask(hal_sfc_handle_t *hsfc,
                                        uint32_t interrupts);

整体替换 SFC 中断 mask。

参数 说明
hsfc READY 状态的 HAL 句柄
interrupts ORed HAL_SFC_INTERRUPT_*;0 表示不屏蔽任何中断

返回:

  • IM_OK:写入成功。
  • IM_ERR_INVALID_PARAM:句柄非法或包含 0x1F 之外的位。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 与 hal_sfc_interrupt_mask() 不同,本接口允许传 0,因为“清空 mask”是合法操作。
  • 传 HAL_SFC_INTERRUPT_ALL 会屏蔽所有事件;错误和 END handler 也会自动执行该动作。
  • 不检查 working/XIP,可在事务中进行动态 mask 调整,但调用方需避免与 IRQ handler 竞争。

hal_sfc_interrupt_mask()

error_code_t hal_sfc_interrupt_mask(hal_sfc_handle_t *hsfc,
                                    uint32_t interrupts);

在现有 mask 上追加屏蔽选中的中断源。

参数 说明
hsfc READY 状态的 HAL 句柄
interrupts 非零 ORed HAL_SFC_INTERRUPT_* 位

返回:

  • IM_OK:mask 更新成功。
  • IM_ERR_INVALID_PARAM:句柄非法、interrupts==0 或包含非法位。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 只置位传入的 mask 位,不清除其他已屏蔽位。
  • 不能传入 0;如要整体清空 mask,使用 hal_sfc_set_interrupt_mask(hsfc, 0U)。
  • 不检查 working/XIP。

hal_sfc_interrupt_unmask()

error_code_t hal_sfc_interrupt_unmask(hal_sfc_handle_t *hsfc,
                                      uint32_t interrupts);

在现有 mask 上解除选中中断源的屏蔽。

参数 说明
hsfc READY 状态的 HAL 句柄
interrupts 非零 ORed HAL_SFC_INTERRUPT_* 位

返回:

  • IM_OK:unmask 更新成功。
  • IM_ERR_INVALID_PARAM:句柄非法、interrupts==0 或包含非法位。
  • IM_ERR_NOT_READY:句柄未初始化。

说明:

  • 只清除传入的 mask 位,不影响其他已屏蔽位。
  • 重新启用前应先清除旧 pending,避免立即再次触发历史事件。
  • 不检查 working/XIP;不要在 END/OVER/UNDER handler 尚未完成清理时盲目 unmask。

5. 典型完整示例

以下示例用于说明 HAL 调用顺序,不是可直接量产的 Flash 驱动。命令、地址宽度、dummy、采样、CE timing、XIP mode bits 和退出命令必须按实际器件与板卡修改。

5.1 CPU FIFO 读取 JEDEC ID

示例使用标准 RDID(0x9F),读取 3 字节 ID。transfer_length 是 3 字节,但 CPU FIFO 只需要读取一个 32-bit word;这里按 LSB-first 提取低 3 字节。

#include <stdbool.h>

#include "hal_sfc.h"
#include "im_error.h"
#include "im_time.h"

static error_code_t app_sfc_cleanup(hal_sfc_handle_t *hsfc,
                                    error_code_t ret)
{
    (void)hal_sfc_trigger_stop(hsfc);
    (void)hal_sfc_trigger_fifo_flush(hsfc);
    (void)hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_ALL);
    return ret;
}

static error_code_t app_sfc_read_jedec_id(hal_sfc_handle_t *hsfc,
                                          uint8_t id[3])
{
    hal_sfc_direct_transfer_config_t config = {0};
    uint32_t fifo_words[1] = {0U};
    uint32_t start_ms;
    bool got_data = false;
    error_code_t ret;

    config.direction = HAL_SFC_DIRECTION_READ;
    config.phase_count = 1U;
    config.phases[0].phase.command_enabled = true;
    config.phases[0].phase.command = 0x9FU;
    config.phases[0].phase.data_enabled = true;
    config.phases[0].phase.data_endian = HAL_SFC_ENDIAN_LSB_FIRST;
    config.phases[0].phase.word_unit = HAL_SFC_WORD_UNIT_1BYTE;
    config.phases[0].phase.transfer_mode = HAL_SFC_TRANSFER_STANDARD_SPI;
    config.data_transfer.operation_mode = HAL_SFC_OPERATION_CPU;
    config.data_transfer.transfer_length = 3U;

    ret = hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_ALL);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_trigger_fifo_flush(hsfc);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_direct_transfer_config(hsfc, &config);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_trigger_start(hsfc);
    if (ret != IM_OK) {
        return ret;
    }

    start_ms = sys_time_get_ms();
    for (;;) {
        uint32_t pending = 0U;
        uint32_t fifo_count = 0U;

        ret = hal_sfc_get_pending_status(hsfc, &pending);
        if (ret != IM_OK) {
            return app_sfc_cleanup(hsfc, ret);
        }

        if ((pending & (HAL_SFC_STATUS_OVER | HAL_SFC_STATUS_UNDER)) != 0U) {
            return app_sfc_cleanup(hsfc, IM_ERR_HARDWARE);
        }

        ret = hal_sfc_get_fifo_word_count(hsfc, &fifo_count);
        if (ret != IM_OK) {
            return app_sfc_cleanup(hsfc, ret);
        }

        if (!got_data && (fifo_count != 0U)) {
            ret = hal_sfc_data_read_burst(hsfc, fifo_words, 1U);
            if (ret != IM_OK) {
                return app_sfc_cleanup(hsfc, ret);
            }
            got_data = true;
            (void)hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_RECE_REQ);
        }

        if ((pending & HAL_SFC_STATUS_END) != 0U) {
            (void)hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_END);

            if (!got_data) {
                return app_sfc_cleanup(hsfc, IM_ERR_HARDWARE);
            }

            id[0] = (uint8_t)(fifo_words[0] >> 0U);
            id[1] = (uint8_t)(fifo_words[0] >> 8U);
            id[2] = (uint8_t)(fifo_words[0] >> 16U);
            return IM_OK;
        }

        if ((uint32_t)(sys_time_get_ms() - start_ms) >= 100U) {
            return app_sfc_cleanup(hsfc, IM_ERR_TIMEOUT);
        }
    }
}

注意事项:

  • 示例假定 phase、数据长度和 FIFO word 数之间满足“3 字节装入 1 个 word”的关系。实际 word_unit 和字节序必须按 Flash 协议调整。
  • CPU 模式不能用 DMA 代替 FIFO 服务;如果改用 DMA,必须填写合法硬件地址并处理 cache。
  • 轮询框架不启用 IRQ。真实产品若同时注册了回调,应设置 mask,避免轮询与中断路径重复消费同一请求。
  • 读取空 FIFO 不会报错;因此示例先检查 fifo_count,再调用 hal_sfc_data_read_burst()。
  • END 和 RECE_REQ 可能在同一快照或不同轮询中出现;处理顺序应以实际硬件状态和中间件实现为准。

5.2 CDT 构建擦除命令链

下面的链由 WREN(0x06)、SECTOR_ERASE(0x20) 和 RDSR(0x05) 硬件轮询组成。轮询等待 WIP(status bit 0)清零。命令和状态位来自常见 JEDEC NOR,实际 Flash 必须按数据手册确认:

#include <stdbool.h>

#include "hal_sfc.h"
#include "im_error.h"

static error_code_t app_sfc_cdt_entry(hal_sfc_handle_t *hsfc,
                                      uint8_t index,
                                      uint16_t command,
                                      uint8_t address_width,
                                      hal_sfc_cdt_address_kind_t address_kind,
                                      bool data_enabled,
                                      bool poll_enabled,
                                      bool link_next,
                                      uint32_t status_expect,
                                      uint32_t status_mask)
{
    hal_sfc_cdt_entry_config_t entry = {0};

    entry.link_next = link_next;
    entry.address_kind = address_kind;
    entry.phase.command_enabled = true;
    entry.phase.command = command;
    entry.phase.address_width = address_width;
    entry.phase.data_enabled = data_enabled;
    entry.phase.poll_enabled = poll_enabled;
    entry.phase.transfer_mode = HAL_SFC_TRANSFER_STANDARD_SPI;
    entry.status_expect = status_expect;
    entry.status_mask = status_mask;

    return hal_sfc_cdt_entry_config(hsfc, index, &entry);
}

static error_code_t app_sfc_erase_sector(hal_sfc_handle_t *hsfc,
                                         uint32_t flash_address)
{
    hal_sfc_cdt_execution_config_t execution = {0};
    error_code_t ret;

    ret = app_sfc_cdt_entry(hsfc, 0U, 0x06U, 0U,
                            HAL_SFC_CDT_ADDRESS_COLUMN,
                            false, false, true, 0U, 0U);
    if (ret != IM_OK) {
        return ret;
    }

    ret = app_sfc_cdt_entry(hsfc, 1U, 0x20U, 3U,
                            HAL_SFC_CDT_ADDRESS_ROW,
                            false, false, true, 0U, 0U);
    if (ret != IM_OK) {
        return ret;
    }

    ret = app_sfc_cdt_entry(hsfc, 2U, 0x05U, 0U,
                            HAL_SFC_CDT_ADDRESS_COLUMN,
                            false, true, false, 0U, 0x01U);
    if (ret != IM_OK) {
        return ret;
    }

    execution.command_index = 0U;
    execution.data_enabled = false;
    execution.direction = HAL_SFC_DIRECTION_WRITE;
    execution.row_address = flash_address;
    execution.data_transfer.operation_mode = HAL_SFC_OPERATION_CPU;
    execution.data_transfer.transfer_length = 0U;

    ret = hal_sfc_clear_status(hsfc, HAL_SFC_STATUS_ALL);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_trigger_fifo_flush(hsfc);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_cdt_execution_config(hsfc, &execution);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_trigger_start(hsfc);
    if (ret != IM_OK) {
        return ret;
    }

    return app_sfc_wait_end(hsfc, 3000U);
}

注意事项:

  • app_sfc_wait_end() 复用 2.4 节的轮询 helper。
  • 第 2 个 entry 使用 row 地址寄存器;HAL 不会把 flash_address 自动拆分成多个 phase 地址。
  • 第 3 个 entry 的 status_expect=0、status_mask=0x01 表示等待 WIP=0。不同 Flash 的状态位和 busy 极性可能不同。
  • 最后一项 link_next=false;索引 63 更不能链接。整条链的完整性和数据 phase 唯一性由调用方保证。
  • app_sfc_wait_end() 等待的是控制器 END,不代表 Flash 已退出内部 busy。CDT 的 RDSR polling phase 用于这一点;如果没有 polling phase,必须另加状态读取。
  • 擦除地址、4 KB 边界、保护位和电源条件由 Flash 驱动保证。本文示例会破坏目标扇区数据。

5.3 XIP 使能边界

HAL 的 XIP 使能顺序与中间件 im_sfc_xip_enable() 一致,但一个可靠的 XIP 流程还包含 Flash 连续读预置、cache 属性和退出帧。下面只展示控制器侧关键调用,不能直接作为通用实现:

static error_code_t app_sfc_xip_enter(hal_sfc_handle_t *hsfc)
{
    hal_sfc_direct_transfer_config_t read_phase = {0};
    hal_sfc_xip_mode_config_t xip = {0};
    error_code_t ret;

    /* 示例字段:0xEB、3 字节地址 + mode、4 dummy、Quad I/O。 */
    read_phase.direction = HAL_SFC_DIRECTION_READ;
    read_phase.phase_count = 1U;
    read_phase.phases[0].phase.command_enabled = true;
    read_phase.phases[0].phase.command = 0xEBU;
    read_phase.phases[0].phase.address_width = 4U; /* 地址字节 + mode 字节,按器件确认 */
    read_phase.phases[0].phase.dummy_cycles = 4U;
    read_phase.phases[0].phase.data_enabled = true;
    read_phase.phases[0].phase.transfer_mode = HAL_SFC_TRANSFER_QUAD_IO_SPI;

    ret = hal_sfc_direct_transfer_config(hsfc, &read_phase);
    if (ret != IM_OK) {
        return ret;
    }

    ret = hal_sfc_xip_cpu_burst_config(hsfc, true);
    if (ret != IM_OK) {
        return ret;
    }

    xip.mode_bits = 0xA0U; /* 必须与 Flash 的 continuous-read mode byte 一致 */
    xip.enabled = true;
    ret = hal_sfc_xip_mode_config(hsfc, &xip);
    if (ret != IM_OK) {
        (void)hal_sfc_xip_cpu_burst_config(hsfc, false);
        return ret;
    }

    ret = hal_sfc_trigger_start(hsfc);
    if (ret != IM_OK) {
        xip.enabled = false;
        (void)hal_sfc_xip_mode_config(hsfc, &xip);
        (void)hal_sfc_xip_cpu_burst_config(hsfc, false);
        return ret;
    }

    return IM_OK;
}

该示例只完成控制器侧进入动作,调用方还必须:

  1. 按 Flash 数据手册确认读命令、地址宽度、dummy、mode bits 和 Quad 使能位。
  2. 在进入 XIP 前确认 XIP 窗口映射和 cache 属性,必要时执行 cache clean/invalidate。
  3. 等待控制器 END 或依赖中间件完成预置事务,确认 Flash 已进入连续读模式。
  4. 从非 XIP 内存执行退出流程;退出时通常需要 stop/flush、清除 XIP_EN、等待 XIP 释放,并发送器件要求的退出帧。
  5. XIP 退出后恢复 hal_sfc_xip_cpu_burst_config(false),重新配置普通命令模式并做回读校验。

由于退出命令、mode byte、缓存和取指位置都与器件/板级强相关,普通 NOR 应用应使用 im_sfc_nor_xip_enable() / im_sfc_nor_xip_disable(),Common 层应用使用 im_sfc_xip_enable() / im_sfc_xip_disable()。


6. 使用约束和检查清单

6.1 关键约束

  • SFC HAL 是控制器级 API,不实现 NOR/NAND 页、块、ECC、坏块和状态轮询策略;普通存储访问应使用 SFC 中间件。
  • HAL 不配置时钟、pinmux、Flash 电源/复位、平台 IRQ 或 XIP 地址窗口。
  • HAL 不计算 Flash timing、命令、dummy 或 XIP mode bits;这些必须来自器件数据手册和板级验证。
  • HAL 不等待 trigger 完成,不实现超时,不自动重试,也不自动回滚失败的事务。
  • HAL 不管理 DMA 的虚拟/物理地址、descriptor 链、缓冲区对齐和 cache。
  • CPU 数据端口按 32-bit word 访问;长度以字节计,调用方负责 word 拆分、字节序和 FIFO 水位。
  • 配置接口大多在 working/XIP 时返回 IM_ERR_BUSY;不要用轮询重试来强行改写正在使用的控制器。
  • HAL 不提供内部锁;同一 SFC 的配置、IRQ、FIFO 和状态访问必须由调用方串行化。
  • hal_sfc_deinit() 触发 soft reset 并清空回调,但不会关闭 CPM 时钟、释放 GPIO 或退出 Flash 连续读模式。
  • XIP 只能在确认窗口映射、cache 属性、Flash 命令和退出路径后启用;退出代码不得从 XIP 窗口取指。

6.2 首次集成检查

  • 已启用 CONFIG_DRIVER_SFC,并确认 SFC 实例地址和中断号与板级一致。
  • 已使能 HAL_CPM_ID_CLK_CGU_SFC 和 HAL_CPM_ID_GATE_SFC,并确认实际总线频率。
  • 已配置 CS#、SCLK、MOSI、MISO、WP#、HOLD#/RESET# 的 pinmux 和上/下拉。
  • 已按 Flash 数据手册确认上电、复位释放、命令宽度、状态宽度、CE timing 和采样边沿。
  • 已调用 hal_sfc_handle_init()、hal_sfc_init()、hal_sfc_set_fifo_threshold()、hal_sfc_device_config() 和 hal_sfc_status_poll_config()。
  • Direct/CDT 配置中的 phase 顺序、地址宽度、dummy、线模式、方向、transfer_length 与器件协议一致。
  • 最多只有一个非 polling 数据 phase;无数据事务的 transfer_length 为 0。
  • CPU 模式已实现 TRAN_REQ/RECE_REQ 的 FIFO 服务、长度跟踪和 OVER/UNDER 处理;DMA 模式已完成对齐、物理地址、descriptor 和 cache 处理。
  • 使用 IRQ 时已注册 hal_sfc_irq_handler()、设置 user_ctx 和 user_data、完成平台 IRQ request/enable,并正确配置 mask。
  • 每次事务结束后已处理 END、清除 pending、flush FIFO(需要时),并在失败时执行 stop/clear/重新初始化。
  • 使用 XIP 前已验证读命令、mode bits、地址宽度、dummy、窗口映射、cache 属性和 Flash 退出命令;退出流程在非 XIP 内存执行。
  • 已用逻辑分析仪、回读校验或中间件错误日志确认波形、数据和 Flash 状态,而不仅仅是 IM_OK。