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;
}
该示例只完成控制器侧进入动作,调用方还必须:
- 按 Flash 数据手册确认读命令、地址宽度、dummy、mode bits 和 Quad 使能位。
- 在进入 XIP 前确认 XIP 窗口映射和 cache 属性,必要时执行 cache clean/invalidate。
- 等待控制器 END 或依赖中间件完成预置事务,确认 Flash 已进入连续读模式。
- 从非 XIP 内存执行退出流程;退出时通常需要 stop/flush、清除 XIP_EN、等待 XIP 释放,并发送器件要求的退出帧。
- 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。