API GUIDE / SFC / LL

SFC LL 接口

sfc ll api

本文面向 BSP、SoC 驱动、SFC HAL 维护者和高级调试开发者,说明 G32S10 SFC 的 LL(Low Level)寄存器访问接口。ll_sfc.h 只提供 static inline 的寄存器读写和位域打包 helper,不是可直接承担 Flash 协议、DMA、cache、超时或并发管理的事务驱动。

API 头文件:

  • code/include/drivers/g32s10/sfc/ll_sfc.h
  • code/include/drivers/g32s10/sfc/sfc_regs.h(寄存器布局、SFC 实例、位域掩码和少量 CDT 常量)

ll_sfc.h 没有对应 .c 文件,所有接口在包含头文件后内联展开。寄存器地址、字段宽度和控制序列必须与 G32S10 SFC 规格一致;本文不展开 SPI Flash 命令语义、DMA descriptor 格式、cache 维护、GPIO pinmux、时钟树和板级电气参数。

LL 层不负责参数校验、错误码、状态机、互斥、超时、时钟使能、pinmux、Flash 上下电/复位、DMA 地址转换、cache 操作或中断注册。普通应用应优先使用 hal_sfc.h 或 Common SFC 中间件;直接调用 LL 接口时,调用方承担全部正确性责任。


1. 模块概览

ll_sfc.h 是 SFC HAL 和中间件的寄存器级基础层,只有一个受支持的实例 SFC。接口不维护软件句柄,也不判断控制器是否正在传输;每次调用都直接访问当前寄存器值。

组件 作用
ll_sfc 提供 static inline 寄存器读写、位域 set/get、状态检查和 CPU 数据端口访问
sfc_regs 定义 sfc_regs_t、SFC 实例、寄存器偏移、字段 mask/shift 和 CDT 布局常量
hal_sfc 在 LL 层之上提供参数检查、句柄状态、事务配置、中断回调和 XIP API
im_sfc / im_sfc_device 在 HAL 之上封装 Flash 命令、DMA、cache、IRQ、超时和板级参数

LL 层的使用边界如下:

  • 所有寄存器接口只接受有效的 SFC 寄存器块指针;除 ll_sfc_is_instance() 外均不检查指针、索引或字段范围。
  • 返回值只有 void、bool 和 uint32_t,没有 error_code_t。写接口成功与否无法通过返回值判断。
  • 字段 setter 通常使用读改写,只修改目标位域;全寄存器 setter(如 ll_sfc_set_glb0())会覆盖整个寄存器,包括保留位。
  • TRIG 和 SCR 是只写寄存器,SR、FSM 和设备实时状态寄存器是只读寄存器。SCR 为写 1 清零,写 0 无效果。
  • LL 层不提供锁、原子操作或访问顺序保证。多个执行上下文共享 SFC 时,读改写序列必须由调用方串行化。
  • 控制器正在传输或 XIP 已开启时,不应改动作业中的 phase、地址、数据长度、CDT 或 XIP 相关配置。
  • 配置完成后,传输由 ll_sfc_trigger_start() 启动;LL 层不等待 END、不服务 FIFO,也不清理 Flash 内部 busy 状态。

本文覆盖 ll_sfc.h 的 195 个公开内联接口,按其访问的寄存器分为 18 个功能组:

功能组 主要寄存器 用途
实例检查 — 判断指针是否为 SFC
全局配置 GLB0 方向、phase 数、FIFO 阈值、CPU/DMA、CDT、描述符、burst
设备公共配置 DEV_CONF 端序、采样延时/边沿、命令宽度、状态宽度、CE timing、引脚无效电平
设备状态 DEV_STA_EXP、DEV0_STA_RT、DEV_STA_MSK 硬件轮询期望值、实时值和比较掩码
Phase 配置 0 TRAN_CFG0[0..5] phase 的命令、地址宽度、poll、dummy、format、数据使能
传输长度和地址 TRAN_LEN、DEV_ADDR、DEV_ADDR_PLUS、MEM_ADDR Direct 模式的 Flash 地址、DMA 内存地址和字节长度
触发 TRIG 启动、停止、FIFO flush
状态 SR working、busy、END、FIFO 请求、overflow/underflow、FIFO 字数
状态清除 SCR 写 1 清除 SR 状态和对应中断标志
中断控制 INTC 按事件屏蔽/解除屏蔽中断
状态机快照 FSM AHB、SPI、DMAC、RMC 状态,用于调试
时钟门控 CGE flow、DMA、RMC、SPI、REG 子模块门控
CDT 执行选择 CMD_IDX 起始 CDT entry、方向和本次是否传输数据
CDT 地址 COL_ADDR、ROW_ADDR、STA_ADDR0/1、DES_ADDR CDT entry 使用的列/行/状态地址和 DMA 描述符地址
全局配置 1 GLB1 软复位、XIP、DQS、连续读 mode bits、chip select
Device 1 状态 DEV1_STA_RT 第二设备端口的实时状态值
Phase 配置 1 TRAN_CFG1[0..5] phase 数据端序、word unit、SPI transfer mode
CDT 和数据端口 CDT[0..255]、DR 64 条 CDT entry 及 CPU 32-bit 数据端口

2. 典型流程

2.1 前置条件与包含方式

LL 头文件没有独立的 Kconfig 选项和源文件。通过 SFC HAL 使用时,hal_sfc.h 已经包含 ll_sfc.h;直接使用寄存器接口时,可显式包含:

#include "ll_sfc.h"

调用任何会访问硬件的接口前,板级必须完成以下准备:

  • 使能 SFC 总线时钟和门控时钟,确认 CPM 没有关闭控制器时钟。
  • 配置 CS#、SCLK、MOSI/MISO、WP#、HOLD#/RESET# 等 pinmux,并保证外部 Flash 已上电且退出复位。
  • 确认当前没有其它任务、IRQ 或 DMA 路径在操作同一控制器。
  • 根据 Flash 和板级时序确定 DEV_CONF、phase、dummy、采样点和 XIP mode bits。
  • DMA 模式下确认 buffer 物理地址、对齐、连续性和 cache 维护策略;LL 层只写地址寄存器。

LL 头文件本身不会打开时钟,也不会把控制器从复位、XIP 或 busy 状态中安全退出。若控制器状态未知,应通过 HAL 初始化/复位流程或平台复位建立已知起点。

2.2 Direct 模式配置顺序

Direct 模式使用 GLB0.CDT_EN=0,按 GLB0.PHASE_NUM 指定的 phase 数执行 TRAN_CFG0[n] 和 TRAN_CFG1[n]。推荐的配置顺序如下:

  1. 确认 SR.WORKING=0 且 GLB1.XIP_EN=0;必要时先停止、flush 和清除旧状态。
  2. 配置 DEV_CONF:命令/状态端序、状态宽度、采样延时、采样边沿、CPOL/CPHA 和 CE timing。
  3. 配置本次使用的 TRAN_CFG0[n]、TRAN_CFG1[n],并写入相应的 DEV_ADDR[n]/DEV_ADDR_PLUS[n]。
  4. 写 TRAN_LEN,单位为字节;没有数据 phase 时必须为 0。
  5. 选择 CPU 或 DMA 数据通路,并在 DMA 非 descriptor 模式下写 MEM_ADDR,descriptor 模式下写 DES_ADDR。
  6. 在 GLB0 中设置方向、phase 数、CDT_EN=0、DES_EN 和 OP_MODE。不要无条件覆盖不相关字段。
  7. 必要时配置 DEV_STA_EXP、DEV_STA_MSK、POLL_TIME 和 INTC。
  8. 清除旧的 END/TREQ/RREQ/OVER/UNDER 状态,flush FIFO,然后写 TRIG.START=1。

TRAN_CFG0[n]、TRAN_CFG1[n]、DEV_ADDR[n] 和 DEV_ADDR_PLUS[n] 的 n 范围都是 0 到 5。GLB0.PHASE_NUM 的有效值为 1 到 6,不能写 0。

2.3 CPU FIFO 数据服务

CPU 模式下,GLB0.OP_MODE=0,数据通过 32-bit 寄存器 DR 访问。典型服务流程如下:

  1. 写入 TRAN_LEN 后启动事务;SR.FIFONUM[6:0] 表示当前 FIFO 中的 word 数。
  2. 发送方向:等待 SR.TRAN_REQ,检查 FIFO 空间,再调用 ll_sfc_write_data() 写入数据。
  3. 接收方向:等待 SR.RECE_REQ,检查 SR.FIFONUM,再调用 ll_sfc_read_data() 读取数据。
  4. 传输完成时处理 SR.END;出现 SR.OVER 或 SR.UNDER 时停止事务并清理状态。
  5. 消费请求后,按中断/轮询策略清除对应的 TREQ/RREQ 状态,最后清除 END。

FIFO 深度、阈值和一次可搬运的 word 数由控制器规格决定。LL 层不会拦截空读、满写或超长 burst;读取空 FIFO 可能得到无效旧数据,写入已满 FIFO 可能造成 overflow。SR.FIFONUM 的读取值只代表采样时刻,不能替代硬件流控。

2.4 CDT 模式配置顺序

CDT 模式使用 GLB0.CDT_EN=1,由 CMD_IDX 选择起始 entry,并沿 CDT_WORD0.LINK 执行命令链。每个 entry 固定占用 4 个 word:

  1. 计算 word_index = entry_index * 4,依次写 word0、word1、word2、word3;ll_sfc_set_cdt_word() 接收的是 word index,不是 entry index。
  2. word0 配置 link、数据端序、word unit、transfer mode 和地址寄存器类型。
  3. word1 配置命令、地址宽度、poll、dummy、phase format、数据使能。
  4. word2/word3 分别是该 entry 的状态期望值和状态掩码。
  5. 写 CMD_IDX 的起始索引、方向和 CDT_DATA_EN,并写本次使用的列地址、行地址、状态地址。
  6. 配置本次 CDT 执行的数据长度和 CPU/DMA 数据通路,设置 GLB0.CDT_EN=1。
  7. 清状态、flush FIFO,写 TRIG.START=1;完成后检查 END 和错误状态。

最后一个 entry 的 LINK 必须为 0,索引 63 不能再链接到下一项。整条链的有效性、地址字段选择、poll 条件和数据 phase 的唯一性由调用方保证。

2.5 XIP 和软件复位

XIP 使能前必须先配置好连续读 phase、Flash mode bits 和地址映射。控制器处于 working 状态时不要打开 XIP;进入 XIP 后,不要再改动普通命令 phase 或 CDT 配置。

配置连续读 phase -> 配置 GLB1.MODE_BIT -> 置 GLB1.XIP_EN=1 -> 通过映射窗口取指/读数据
                                                                  |
                                    需要重配时 <- 清 XIP_EN 和 MODE_BIT

ll_sfc_set_reset() 向 GLB1.RESET 写入复位持续值并启动控制器软复位;硬件递减计数后自动释放,软件不需要轮询该字段。复位后应重新建立 DEV_CONF、phase、GLB0/GLB1、中断和地址配置,不要假定旧配置仍然有效。复位不涉及外部 Flash、时钟或 pinmux。

2.6 并发、错误和清理原则

  • 所有字段 setter 都是“读-改-写”序列;同一寄存器被任务和 IRQ 同时修改时可能丢写。调用方必须使用临界区或统一的所有者任务。
  • LL 层无法报告总线错误、参数错误或控制器拒绝。调用方需要自行检查 SR、FSM 和设备状态。
  • 发生 over/under、超时或命令失败时,建议先写 TRIG.STOP=1,再写 TRIG.FLUSH=1,最后用 SCR 清除全部可清除状态。
  • 不要在 WORKING=1 时修改 GLB0 的 phase/模式字段、TRAN_CFG*、CDT 或 XIP 配置。
  • 写全寄存器接口时,应先用对应 getter(若寄存器可读)取得当前值,再修改目标字段,避免误清保留位或其它配置。

3. 配置和关键类型

3.1 实例和寄存器布局

sfc_regs_t 由 struct sfc_regs 定义,唯一公开实例为 SFC:

typedef struct sfc_regs sfc_regs_t;
#define SFC ((sfc_regs_t *)(uintptr_t)SFC_IOBASE)

SFC_IOBASE 为 0x13440000。ll_sfc_is_instance() 只比较指针是否等于 SFC;其它指针对应的寄存器对象不被 LL 接口支持。

寄存器 偏移 访问属性 说明
GLB0 0x000 R/W 全局模式、phase 数、FIFO 阈值、CPU/DMA、CDT/descriptor、burst
DEV_CONF 0x004 R/W 设备公共时序、端序、采样和引脚无效电平
DEV_STA_EXP 0x008 R/W 轮询期望状态
DEV0_STA_RT 0x00C RO Device 0 实时状态
DEV_STA_MSK 0x010 R/W 状态比较掩码
TRAN_CFG0[6] 0x014–0x028 R/W phase 0..5 的命令/地址/dummy/数据配置
TRAN_LEN 0x02C R/W 传输字节数
DEV_ADDR[6] 0x030–0x044 R/W Direct 模式 phase 地址低 32 位
DEV_ADDR_PLUS[6] 0x048–0x05C R/W Direct 模式 phase 地址高 16 位
MEM_ADDR 0x060 R/W 非 descriptor DMA 内存地址
TRIG 0x064 WO start/stop/FIFO flush
SR 0x068 RO 传输和 FIFO 状态
SCR 0x06C WO, W1C 清除 SR 状态和中断标志
INTC 0x070 R/W 中断屏蔽,1=masked
FSM 0x074 RO 各子模块状态机快照
CGE 0x078 R/W 子模块时钟门控使能
CMD_IDX 0x07C R/W CDT 起始 entry、方向和传输数据使能
COL_ADDR 0x080 R/W CDT 列地址
ROW_ADDR 0x084 R/W CDT 行地址
STA_ADDR0 0x088 R/W CDT 状态地址 0
STA_ADDR1 0x08C R/W CDT 状态地址 1
DES_ADDR 0x090 R/W DMA descriptor 地址
GLB1 0x094 R/W 软复位、XIP、DQS、mode bits、chip select
DEV1_STA_RT 0x098 RO Device 1 实时状态
TRAN_CFG1[6] 0x09C–0x0B0 R/W phase 0..5 的端序、word unit、transfer mode
CDT[256] 0x800–0xBFF R/W 64 个 entry × 4 word
DR 0x1000 R/W CPU 32-bit 数据端口

3.2 通用参数和访问规则

规则 说明
实例 除 ll_sfc_is_instance() 外的所有接口都应按 ll_sfc_*(SFC, ...) 调用
phase index 0..5,所有 TRAN_CFG0/1、DEV_ADDR、DEV_ADDR_PLUS 接口共用
CDT word index 0..255,每个 entry 对应连续 4 个 word
CDT entry index 0..63,经 CMD_IDX 选择,最后一 entry 不应再 link
FIFO threshold 1..63,0 无效
phase count 1..6,0 无效
address width 0 无地址,1..5 对应 1..5 字节,6/7 都表示 6 字节
dummy cycles 0..63,单位为 SFC clock cycle
command 16-bit 字段;实际宽度由 DEV_CONF.CMD_TYPE 选择 1 或 2 字节
word unit 0=1 字节,1=2 字节,2=4 字节,其他值保留
CE hold/setup 0..3,对应 0.5、1、1.5、2 个 SFC clock cycle
CE deselect 0..15,对应 0.5 到 8 个 SFC clock cycle,步进 0.5
CPU FIFO 32-bit word 端口;字节序和 word unit 由 phase 配置决定

字段 setter 只清除并写入自己的字段,但读改写本身不带锁;若同一寄存器可能被 ISR 修改,必须把整个读改写序列放入临界区。全寄存器 setter 不保留旧值,调用方必须提供完整的 32-bit 值。

3.3 GLB0 全局配置

字段 位 编码/范围 说明
POLL_TIME 31:16 16-bit 硬件轮询操作之间的间隔计数
DES_EN 15 0/1 DMA descriptor 模式使能;使能后 DMA 使用 DES_ADDR
CDT_EN 14 0/1 CDT 命令链模式使能
TRAN_DIR 13 0/1 0=Flash 到 FIFO/内存,1=FIFO/内存到 Flash
THRESHOLD 12:7 1..63 CPU FIFO 阈值,单位 word;0 无效
OP_MODE 6 0/1 0=CPU FIFO,1=DMA
PHASE_NUM 5:3 1..6 Direct 模式有效 phase 数
WP_EN 2 0/1 硬件写保护使能
BURST_MD 1:0 01/10/11 DMA burst:8/16/32 beat;00 保留,复位默认 11

GLB0 的模式和 phase 字段应在启动新事务前完整确定。CDT 模式下 CDT_EN=1;DMA descriptor 只有在 OP_MODE=1 时才应使能。

3.4 DEV_CONF 设备公共配置

字段 位 编码/范围 说明
STA_ENDIAN 31 0/1 状态数据 LSB-first/MSB-first
SMP_DELAY 20:16 0..31 采样延时编码,实际延时取决于器件、PCB/IO 和 SFC 采样时钟
CMD_TYPE 15 0/1 命令字段 1/2 字节
STA_TYPE 14:13 0..3 硬件轮询状态宽度:1/2/3/4 字节
THOLD 12:11 0..3 CE 最后一个时钟沿后的 hold:0.5/1/1.5/2 cycle
TSETUP 10:9 0..3 首个时钟沿前的 CE setup:0.5/1/1.5/2 cycle
TSH 8:5 0..15 两次 CE 有效之间的 deselect 间隔:0.5..8 cycle
CPHA 4 0/1 0=奇数边沿采样,1=偶数边沿采样
CPOL 3 0/1 0=SCLK 高有效,1=SCLK 低有效
CE_DL 2 0/1 CE 无效电平:低/高
HOLD_DL 1 0/1 HOLD 无效电平:低/高
WP_DL 0 0/1 WP 无效电平:低/高

SMP_DELAY、CE timing 和引脚无效电平必须按实际 Flash/板级时序配置。整个 DEV_CONF 可在控制器 idle 时通过 ll_sfc_set_dev_conf() 一次写入,也可用字段 setter 保持其它位不变。

3.5 设备状态轮询

Direct 模式使用 DEV_STA_EXP、DEV0_STA_RT 和 DEV_STA_MSK 完成硬件状态轮询:

寄存器 访问 说明
DEV_STA_EXP R/W 期望的状态值
DEV0_STA_RT RO 硬件实时采样到的 Device 0 状态
DEV_STA_MSK R/W 参与比较的状态位;具体比较规则以 SFC 规格为准

只有 phase 的 POLL_EN=1 时才会进入该 phase 的轮询流程。轮询会覆盖该 phase 的普通方向和 DATA_EN 行为;POLL_TIME 控制轮询间隔。CDT 模式下,每个 entry 使用自己的 word2/word3 作为期望值和掩码。

3.6 Phase 配置

每个 active phase 由 TRAN_CFG0[n] 和 TRAN_CFG1[n] 共同描述。GLB0.PHASE_NUM 必须与被使用的 phase 数一致。

TRAN_CFG0[n]:

字段 位 编码/范围 说明
CLK_MODE 31:29 000 规格只定义 SDR,保持默认值,不要自行改写成其它编码
ADDR_WIDTH 28:26 0..7 0 无地址;1..5 为 1..5 字节;6/7 为 6 字节
POLL_EN 25 0/1 硬件状态轮询使能,优先于方向和 DATA_EN
CMD_EN 24 0/1 命令 phase 使能
PHASE_FORMAT 23 0/1 硬件 phase 组件顺序格式 0/1
DMY_BITS 22:17 0..63 dummy SFC clock cycle 数
DATA_EN 16 0/1 数据 phase 使能
CMD 15:0 16-bit 命令值;实际有效宽度由 CMD_TYPE 决定

TRAN_CFG1[n]:

字段 位 编码/范围 说明
DATA_ENDIAN 18 0/1 数据 LSB-first/MSB-first
WORD_UNIT 17:16 0..2 数据单元 1/2/4 字节,3 保留
TRAN_MD 7:4 0,1,2,3,5,6,7 SPI line mode;4 保留

TRAN_MD 的常用编码为:0 标准 SPI,1 dual input/output,2 dual I/O,3 full dual I/O,5 quad input/output,6 quad I/O,7 full quad I/O。不同 Flash 命令允许的 line mode 必须按器件数据手册确认。

3.7 传输长度、地址和数据通路

配置项 说明
TRAN_LEN 传输字节数;0 表示没有数据 phase。没有数据 phase 时必须为 0
DEV_ADDR[n] Direct 模式 phase 的 Flash 地址低 32 位
DEV_ADDR_PLUS[n] Direct 模式 phase 的 Flash 地址高 16 位,只有低 16 位有效
MEM_ADDR DMA 且 DES_EN=0 时的内存地址
DES_ADDR DMA descriptor 模式下的描述符地址;与 MEM_ADDR 互斥使用

Direct 模式可以把 DEV_ADDR_PLUS[n] 与 DEV_ADDR[n] 组成最多 48-bit 的 Flash 地址。CDT 模式不使用 DEV_ADDR[n];entry 的 ADDR_KIND 从 COL_ADDR、ROW_ADDR、STA_ADDR0、STA_ADDR1 中选择地址操作数。

CPU 模式只使用 DR;DMA 模式由控制器访问 MEM_ADDR 或 DES_ADDR。LL 层不检查地址对齐、物理连续性、cache 一致性或 descriptor 内容。

3.8 SR、SCR 和 INTC

SR 为只读状态:

字段 位 说明
FIFONUM 22:16 CPU FIFO 当前 word 数
WORKING 7 控制器正在执行传输
BUSY 6:5 SPI 数据路径 busy 编码,具体取值见规格
END 4 传输/命令链完成
TRAN_REQ 3 发送 FIFO 请求
RECE_REQ 2 接收 FIFO 请求
OVER 1 FIFO overflow
UNDER 0 FIFO underflow

SFC_SR_INT_MASK 包含 END/TREQ/RREQ/OVER/UNDER 五位。ll_sfc_get_pending_status() 返回 SR & SFC_SR_INT_MASK,不按 INTC 过滤;如果需要“已使能且挂起”的中断位,调用方应再清除 masked 位。

SCR 使用相同的事件位布局,写 1 清除,写 0 无效果:

SCR 位 名称 清除对象
4 CLR_END SR.END 和 END 中断标志
3 CLR_TREQ SR.TRAN_REQ 和 TREQ 中断标志
2 CLR_RREQ SR.RECE_REQ 和 RREQ 中断标志
1 CLR_OVER SR.OVER 和 OVER 中断标志
0 CLR_UNDR SR.UNDER 和 UNDER 中断标志

INTC 中每一位为 1 表示对应中断被屏蔽,为 0 表示开放。INTC 和 SR 使用相同的五位事件映射;SFC_INTC_MASK_ALL 屏蔽全部事件。

3.9 FSM 和 CGE

FSM 只读,用于调试控制器内部状态:

字段 位 说明
AHB 19:16 AHB 状态机快照
SPI 15:8 SPI 状态机快照
DMAC 7:4 DMA 控制器状态机快照
RMC 2:0 RMC 状态机快照

状态编码属于内部实现信息,不应作为稳定软件协议。生产逻辑应使用 SR.WORKING/BUSY/END 和 Flash 状态,而不是依赖 FSM 的具体数值。

CGE 控制子模块时钟门控,1 表示开启对应门控功能:

位 名称 子模块
5 FLOW_EN flow control
3 DMA_EN DMAC/AHB master
2 RMC_EN RMC
1 SPI_EN SPI/clock generator
0 REG_EN register interface

ll_sfc_set_clock_gate(SFC, 0U) 会关闭全部门控;SFC_CGE_EN_ALL 会全部开启。该寄存器控制的是门控功能,不是 SFC 总线时钟的开关。

3.10 CDT 四字布局和 CMD_IDX

每个 CDT entry 由连续 4 个 32-bit word 组成。entry_index 为 0..63,对应 word_index = entry_index * 4。

Word 字段 位 说明
word0 LINK 31 1=继续执行下一个 entry,0=链结束
word0 ENDIAN 18 数据端序
word0 WORD_UNIT 17:16 数据单元 1/2/4 字节
word0 TRAN_MODE 7:4 SPI line mode
word0 ADDR_KIND 1:0 0=COL,1=ROW,2=STA0,3=STA1
word1 CLK_MODE 31:29 SDR,保持默认编码
word1 ADDR_WIDTH 28:26 地址宽度编码,规则同 TRAN_CFG0
word1 POLL_EN 25 轮询 phase 使能
word1 CMD_EN 24 命令 phase 使能
word1 PHASE_FORMAT 23 phase 组件顺序格式
word1 DMY_BITS 22:17 dummy cycle 数
word1 DATA_EN 16 数据 phase 使能
word1 CMD 15:0 命令值
word2 STATUS_EXPECT 31:0 该 entry 的状态期望值
word3 STATUS_MASK 31:0 该 entry 的状态比较掩码

CMD_IDX:

字段 位 说明
CDT_DATA_EN 31 本次 CDT 执行是否搬运数据
CDT_DIR 30 0=Flash 读,1=Flash 写
CMD_IDX 5:0 起始 CDT entry 索引,0..63

CDT 的 word0 对应 TRAN_CFG1 的语义,word1 对应 TRAN_CFG0 的语义,但字段位置不同,不能直接把寄存器值原样复制为另一种格式。

3.11 GLB1、XIP 和软复位

字段 位 编码/范围 说明
RESET 20:15 0..63 写入即启动软复位;数值为 HCLK 周期,0 表示硬件自动释放;硬件自动递减
TRAN_DIR_SEL 14:13 00 保持默认值
MODE_BIT 12:5 8-bit XIP 连续读 mode bits
XIP_EN 4 0/1 XIP 使能
DQS_EN 2 0/1 DQS 使能;规格要求保持默认值
CHIP_SEL 1:0 00 当前仅默认值 00 有效,其它值保留

ll_sfc_set_reset() 只触发控制器复位,不等待 Flash、时钟或 pinmux。RESET=0 表示由硬件自动释放;需要明确的复位持续时间时,应写入非 0 的 HCLK 周期值。清空该字段不是取消复位的软件接口。复位前应确认没有正在进行的事务,复位后重新配置所有寄存器。

3.12 常用常量

常量 值/含义
SFC_IOBASE 0x13440000,SFC 寄存器基地址
SFC_CDT_ENTRY_COUNT 64 个 CDT entry
SFC_CDT_WORDS_PER_ENTRY 每个 entry 4 个 word
SFC_SR_INT_MASK END/TREQ/RREQ/OVER/UNDER 五位集合
SFC_SCR_CLR_ALL 清除全部可清除状态
SFC_INTC_MASK_ALL 屏蔽全部中断
SFC_CGE_EN_ALL 开启全部时钟门控功能
SFC_TRIG_START_MASK 启动传输
SFC_TRIG_STOP_MASK 终止全部传输
SFC_TRIG_FLUSH_MASK flush FIFO
SFC_GLB1_RESET_MASK 软复位字段 mask
SFC_GLB1_XIP_EN_MASK XIP 使能位 mask

LL 层没有错误码表:所有寄存器写接口都没有失败返回,读取接口也不区分“读到 0”和“访问失败”。错误检测必须由调用方结合 SR、FSM、设备状态和应用超时完成。


4. API 参考

4.1 API 总览

ll_sfc.h

下表按功能组汇总全部接口。sfcx 统一表示 sfc_regs_t *,合法实例为 SFC;详细参数和约束见后续各节。

实例检查

API 用途
ll_sfc_is_instance() 判断指针是否为受支持的 SFC 实例

GLB0 全局配置

API 用途
ll_sfc_get_glb0() 读取完整 GLB0
ll_sfc_set_glb0() 覆盖写入完整 GLB0
ll_sfc_set_poll_time() 设置硬件轮询间隔
ll_sfc_get_poll_time() 读取硬件轮询间隔
ll_sfc_enable_dma_descriptor() 开启 DMA descriptor 模式
ll_sfc_disable_dma_descriptor() 关闭 DMA descriptor 模式
ll_sfc_is_dma_descriptor_enabled() 查询 descriptor 模式
ll_sfc_enable_cdt() 开启 CDT 模式
ll_sfc_disable_cdt() 关闭 CDT 模式
ll_sfc_is_cdt_enabled() 查询 CDT 模式
ll_sfc_set_transfer_direction() 设置 Flash 读写方向
ll_sfc_is_write_transfer() 查询是否为写 Flash 方向
ll_sfc_set_fifo_threshold() 设置 CPU FIFO 阈值
ll_sfc_get_fifo_threshold() 读取 CPU FIFO 阈值
ll_sfc_set_operation_mode() 选择 CPU FIFO 或 DMA
ll_sfc_is_dma_operation_mode() 查询是否为 DMA 模式
ll_sfc_set_phase_count() 设置 Direct 模式 phase 数
ll_sfc_get_phase_count() 读取 phase 数
ll_sfc_enable_write_protect() 开启硬件写保护
ll_sfc_disable_write_protect() 关闭硬件写保护
ll_sfc_is_write_protect_enabled() 查询硬件写保护
ll_sfc_set_burst_mode() 设置 DMA burst 长度编码
ll_sfc_get_burst_mode() 读取 DMA burst 长度编码

DEV_CONF 设备公共配置

API 用途
ll_sfc_get_dev_conf() 读取完整 DEV_CONF
ll_sfc_set_dev_conf() 覆盖写入完整 DEV_CONF
ll_sfc_set_status_endian() 设置状态端序
ll_sfc_is_status_msb_first() 查询状态是否 MSB-first
ll_sfc_set_sample_delay() 设置采样延时编码
ll_sfc_get_sample_delay() 读取采样延时编码
ll_sfc_set_command_type() 设置 1/2 字节命令宽度
ll_sfc_is_two_byte_command() 查询是否使用 2 字节命令
ll_sfc_set_status_type() 设置轮询状态宽度
ll_sfc_get_status_type() 读取轮询状态宽度
ll_sfc_set_ce_hold_time() 设置 CE hold 时间编码
ll_sfc_get_ce_hold_time() 读取 CE hold 时间编码
ll_sfc_set_ce_setup_time() 设置 CE setup 时间编码
ll_sfc_get_ce_setup_time() 读取 CE setup 时间编码
ll_sfc_set_ce_deselect_interval() 设置 CE deselect 间隔
ll_sfc_get_ce_deselect_interval() 读取 CE deselect 间隔
ll_sfc_set_sample_edge() 设置采样边沿
ll_sfc_is_sampled_on_even_edge() 查询是否在偶数边沿采样
ll_sfc_set_clock_polarity() 设置 SCLK 极性
ll_sfc_is_clock_active_low() 查询 SCLK 是否低有效
ll_sfc_set_ce_inactive_level() 设置 CE 无效电平
ll_sfc_is_ce_inactive_high() 查询 CE 无效电平是否为高
ll_sfc_set_hold_inactive_level() 设置 HOLD 无效电平
ll_sfc_is_hold_inactive_high() 查询 HOLD 无效电平是否为高
ll_sfc_set_wp_inactive_level() 设置 WP 无效电平
ll_sfc_is_wp_inactive_high() 查询 WP 无效电平是否为高

设备状态

API 用途
ll_sfc_set_dev_status_expect() 写轮询期望状态
ll_sfc_get_dev_status_expect() 读轮询期望状态
ll_sfc_get_dev0_status() 读 Device 0 实时状态
ll_sfc_set_dev_status_mask() 写状态比较掩码
ll_sfc_get_dev_status_mask() 读状态比较掩码

TRAN_CFG0 phase 配置

API 用途
ll_sfc_get_tran_cfg0() 读取一个 phase 的完整配置
ll_sfc_set_tran_cfg0() 覆盖写入一个 phase 的完整配置
ll_sfc_set_tran_cfg0_clock_mode() 设置 phase clock mode
ll_sfc_get_tran_cfg0_clock_mode() 读取 phase clock mode
ll_sfc_set_tran_cfg0_address_width() 设置 phase 地址宽度
ll_sfc_get_tran_cfg0_address_width() 读取 phase 地址宽度
ll_sfc_enable_tran_cfg0_poll() 开启 phase 硬件轮询
ll_sfc_disable_tran_cfg0_poll() 关闭 phase 硬件轮询
ll_sfc_is_tran_cfg0_poll_enabled() 查询 phase 轮询使能
ll_sfc_enable_tran_cfg0_command() 开启命令 phase
ll_sfc_disable_tran_cfg0_command() 关闭命令 phase
ll_sfc_is_tran_cfg0_command_enabled() 查询命令 phase 使能
ll_sfc_set_tran_cfg0_phase_format() 设置 phase 组件格式
ll_sfc_is_tran_cfg0_format1() 查询是否使用 format 1
ll_sfc_set_tran_cfg0_dummy_cycles() 设置 dummy cycle 数
ll_sfc_get_tran_cfg0_dummy_cycles() 读取 dummy cycle 数
ll_sfc_enable_tran_cfg0_data() 开启数据 phase
ll_sfc_disable_tran_cfg0_data() 关闭数据 phase
ll_sfc_is_tran_cfg0_data_enabled() 查询数据 phase 使能
ll_sfc_set_tran_cfg0_command() 设置 phase 命令值
ll_sfc_get_tran_cfg0_command() 读取 phase 命令值

传输长度和地址

API 用途
ll_sfc_set_transfer_length() 写传输字节数
ll_sfc_get_transfer_length() 读传输字节数
ll_sfc_set_device_address() 写 phase 地址低 32 位
ll_sfc_get_device_address() 读 phase 地址低 32 位
ll_sfc_set_device_address_plus() 写 phase 地址高 16 位
ll_sfc_get_device_address_plus() 读 phase 地址高 16 位
ll_sfc_set_memory_address() 写非 descriptor DMA 内存地址
ll_sfc_get_memory_address() 读非 descriptor DMA 内存地址

触发

API 用途
ll_sfc_trigger_start() 写 TRIG.START 启动传输
ll_sfc_trigger_stop() 写 TRIG.STOP 终止传输
ll_sfc_trigger_fifo_flush() 写 TRIG.FLUSH 清空 FIFO

SR 状态

API 用途
ll_sfc_get_status() 读取完整 SR
ll_sfc_get_fifo_word_count() 读取 FIFO word 数
ll_sfc_is_working() 查询 controller working
ll_sfc_get_busy_state() 读取 SPI busy 编码
ll_sfc_is_end() 查询 END
ll_sfc_is_tran_req() 查询发送请求
ll_sfc_is_rece_req() 查询接收请求
ll_sfc_is_over() 查询 FIFO overflow
ll_sfc_is_under() 查询 FIFO underflow
ll_sfc_get_pending_status() 读取五位待处理状态

SCR 状态清除

API 用途
ll_sfc_clear_status_bits() 按 mask 写 1 清除状态
ll_sfc_clear_end_status() 清除 END
ll_sfc_clear_tran_req_status() 清除 TREQ
ll_sfc_clear_rece_req_status() 清除 RREQ
ll_sfc_clear_over_status() 清除 OVER
ll_sfc_clear_under_status() 清除 UNDER
ll_sfc_clear_all_status_bits() 清除全部可清除状态

INTC 中断控制

API 用途
ll_sfc_set_interrupt_mask() 覆盖写完整中断屏蔽值
ll_sfc_get_interrupt_mask() 读取中断屏蔽值
ll_sfc_is_end_interrupt_masked() 查询 END 中断是否屏蔽
ll_sfc_is_treq_interrupt_masked() 查询 TREQ 中断是否屏蔽
ll_sfc_is_rreq_interrupt_masked() 查询 RREQ 中断是否屏蔽
ll_sfc_is_over_interrupt_masked() 查询 OVER 中断是否屏蔽
ll_sfc_is_undr_interrupt_masked() 查询 UNDER 中断是否屏蔽
ll_sfc_mask_interrupts() 按 mask 屏蔽中断
ll_sfc_mask_end_interrupt() 屏蔽 END 中断
ll_sfc_mask_treq_interrupt() 屏蔽 TREQ 中断
ll_sfc_mask_rreq_interrupt() 屏蔽 RREQ 中断
ll_sfc_mask_over_interrupt() 屏蔽 OVER 中断
ll_sfc_mask_undr_interrupt() 屏蔽 UNDER 中断
ll_sfc_unmask_interrupts() 按 mask 开放中断
ll_sfc_unmask_end_interrupt() 开放 END 中断
ll_sfc_unmask_treq_interrupt() 开放 TREQ 中断
ll_sfc_unmask_rreq_interrupt() 开放 RREQ 中断
ll_sfc_unmask_over_interrupt() 开放 OVER 中断
ll_sfc_unmask_undr_interrupt() 开放 UNDER 中断
ll_sfc_mask_all_interrupts() 屏蔽全部中断
ll_sfc_unmask_all_interrupts() 开放全部中断

FSM 调试状态

API 用途
ll_sfc_get_fsm() 读取完整 FSM
ll_sfc_get_ahb_fsm() 读取 AHB 状态机快照
ll_sfc_get_spi_fsm() 读取 SPI 状态机快照
ll_sfc_get_dmac_fsm() 读取 DMAC 状态机快照
ll_sfc_get_rmc_fsm() 读取 RMC 状态机快照

CGE 时钟门控

API 用途
ll_sfc_get_clock_gate() 读取完整 CGE
ll_sfc_set_clock_gate() 覆盖写入完整 CGE
ll_sfc_enable_flow_clock_gate() 开启 flow-control 门控
ll_sfc_disable_flow_clock_gate() 关闭 flow-control 门控
ll_sfc_is_flow_clock_gate_enabled() 查询 flow-control 门控
ll_sfc_enable_dma_clock_gate() 开启 DMA/AHB 门控
ll_sfc_disable_dma_clock_gate() 关闭 DMA/AHB 门控
ll_sfc_is_dma_clock_gate_enabled() 查询 DMA/AHB 门控
ll_sfc_enable_rmc_clock_gate() 开启 RMC 门控
ll_sfc_disable_rmc_clock_gate() 关闭 RMC 门控
ll_sfc_is_rmc_clock_gate_enabled() 查询 RMC 门控
ll_sfc_enable_spi_clock_gate() 开启 SPI/clock-generator 门控
ll_sfc_disable_spi_clock_gate() 关闭 SPI/clock-generator 门控
ll_sfc_is_spi_clock_gate_enabled() 查询 SPI/clock-generator 门控
ll_sfc_enable_reg_clock_gate() 开启寄存器接口门控
ll_sfc_disable_reg_clock_gate() 关闭寄存器接口门控
ll_sfc_is_reg_clock_gate_enabled() 查询寄存器接口门控

CMD_IDX CDT 执行选择

API 用途
ll_sfc_get_cmd_idx_register() 读取完整 CMD_IDX
ll_sfc_set_cmd_idx_register() 覆盖写入完整 CMD_IDX
ll_sfc_enable_cmd_idx_cdt_data() 允许本次 CDT 执行传输数据
ll_sfc_disable_cmd_idx_cdt_data() 禁止本次 CDT 执行传输数据
ll_sfc_is_cmd_idx_cdt_data_enabled() 查询 CDT 数据使能
ll_sfc_set_cmd_idx_transfer_direction() 设置 CDT 数据方向
ll_sfc_is_cmd_idx_write_transfer() 查询 CDT 是否写 Flash
ll_sfc_set_cmd_idx_index() 设置起始 CDT entry
ll_sfc_get_cmd_idx_index() 读取起始 CDT entry

CDT 和 DMA 地址

API 用途
ll_sfc_set_column_address() 写 CDT 列地址
ll_sfc_get_column_address() 读 CDT 列地址
ll_sfc_set_row_address() 写 CDT 行地址
ll_sfc_get_row_address() 读 CDT 行地址
ll_sfc_set_status_address0() 写 CDT 状态地址 0
ll_sfc_get_status_address0() 读 CDT 状态地址 0
ll_sfc_set_status_address1() 写 CDT 状态地址 1
ll_sfc_get_status_address1() 读 CDT 状态地址 1
ll_sfc_set_descriptor_address() 写 DMA descriptor 地址
ll_sfc_get_descriptor_address() 读 DMA descriptor 地址

GLB1、XIP 和软复位

API 用途
ll_sfc_get_glb1() 读取完整 GLB1
ll_sfc_set_glb1() 覆盖写入完整 GLB1
ll_sfc_set_reset() 写复位持续值并启动软复位
ll_sfc_get_reset() 读取当前复位倒计时
ll_sfc_set_tran_dir_select() 设置 TRAN_DIR_SEL,应保持 0
ll_sfc_get_tran_dir_select() 读取 TRAN_DIR_SEL
ll_sfc_set_mode_bit() 设置 XIP 连续读 mode bits
ll_sfc_get_mode_bit() 读取 XIP 连续读 mode bits
ll_sfc_enable_xip() 开启 XIP
ll_sfc_disable_xip() 关闭 XIP
ll_sfc_is_xip_enabled() 查询 XIP 状态
ll_sfc_enable_dqs() 开启 DQS,规格要求保持默认
ll_sfc_disable_dqs() 关闭 DQS,规格要求保持默认
ll_sfc_is_dqs_enabled() 查询 DQS 状态
ll_sfc_set_chip_select() 设置 chip select 编码,默认 00
ll_sfc_get_chip_select() 读取 chip select 编码

Device 1 和 TRAN_CFG1

API 用途
ll_sfc_get_dev1_status() 读取 Device 1 实时状态
ll_sfc_get_tran_cfg1() 读取一个 phase 的 TRAN_CFG1
ll_sfc_set_tran_cfg1() 覆盖写入一个 phase 的 TRAN_CFG1
ll_sfc_set_tran_cfg1_data_endian() 设置 phase 数据端序
ll_sfc_is_tran_cfg1_data_msb_first() 查询数据是否 MSB-first
ll_sfc_set_tran_cfg1_word_unit() 设置 phase word unit
ll_sfc_get_tran_cfg1_word_unit() 读取 phase word unit
ll_sfc_set_tran_cfg1_transfer_mode() 设置 phase SPI line mode
ll_sfc_get_tran_cfg1_transfer_mode() 读取 phase SPI line mode

CDT 和数据端口

API 用途
ll_sfc_get_cdt_word() 读取指定 CDT word
ll_sfc_set_cdt_word() 写入指定 CDT word
ll_sfc_read_data() 从 CPU 数据端口读 32-bit word
ll_sfc_write_data() 向 CPU 数据端口写 32-bit word

4.2 通用约定

  • 除 ll_sfc_is_instance() 外,所有接口的第一个参数都是 sfc_regs_t *sfcx,且必须指向 SFC。LL 层不解引用前检查空指针,也不验证实例。
  • void 接口只表示写操作已发出,不表示硬件接受了配置;bool 接口返回当前采样到的一个状态条件;uint32_t 接口返回原始寄存器或字段值。
  • 参数类型是 uint8_t、uint16_t 或 uint32_t 不代表字段范围已校验。调用方必须按 3.2 节和寄存器规格截断或验证。
  • 字段 setter 使用读改写;如果调用者与 ISR 共享同一寄存器,必须保证读改写原子。
  • 全寄存器 setter 会覆盖整个寄存器;不要把多个字段简单的 OR 值写入,除非该值已经包含所有需要保留的位。
  • 所有 is_* 接口只读取当前状态,不清除状态。状态和中断标志通过 SCR 清除。
  • LL 层没有错误码、超时和重试。应用层若需要 error_code_t,应在 LL 调用外层自行检查并返回。

4.3 实例检查 API

API 参数 返回 说明
ll_sfc_is_instance(sfcx) const sfc_regs_t *sfcx bool 仅当 sfcx == SFC 时返回 true;不访问寄存器

4.4 GLB0 API

以下接口访问 GLB0。ll_sfc_set_glb0() 覆盖完整寄存器,其余 setter 只修改对应字段并保留其它位。

API 参数 返回 说明
ll_sfc_get_glb0(sfcx) — uint32_t 读取完整 GLB0
ll_sfc_set_glb0(sfcx, value) uint32_t value void 覆盖写 GLB0;调用方负责保留有效字段和保留位
ll_sfc_set_poll_time(sfcx, poll_time) uint16_t poll_time void 写 POLL_TIME[31:16],轮询间隔计数
ll_sfc_get_poll_time(sfcx) — uint32_t 返回 POLL_TIME
ll_sfc_enable_dma_descriptor(sfcx) — void 置 DES_EN,DMA 使用 DES_ADDR
ll_sfc_disable_dma_descriptor(sfcx) — void 清 DES_EN
ll_sfc_is_dma_descriptor_enabled(sfcx) — bool 查询 DES_EN
ll_sfc_enable_cdt(sfcx) — void 置 CDT_EN
ll_sfc_disable_cdt(sfcx) — void 清 CDT_EN
ll_sfc_is_cdt_enabled(sfcx) — bool 查询 CDT_EN
ll_sfc_set_transfer_direction(sfcx, direction) uint8_t direction,0/1 void 写 TRAN_DIR;0=读 Flash,1=写 Flash
ll_sfc_is_write_transfer(sfcx) — bool TRAN_DIR=1 时返回 true
ll_sfc_set_fifo_threshold(sfcx, threshold) uint8_t threshold,1..63 void 写 THRESHOLD[12:7],单位 word;0 无效
ll_sfc_get_fifo_threshold(sfcx) — uint32_t 返回 FIFO 阈值
ll_sfc_set_operation_mode(sfcx, mode) uint8_t mode,0/1 void 0=CPU FIFO,1=DMA
ll_sfc_is_dma_operation_mode(sfcx) — bool OP_MODE=1 时返回 true
ll_sfc_set_phase_count(sfcx, phase_count) uint8_t phase_count,1..6 void 写 Direct 模式 phase 数;0 无效
ll_sfc_get_phase_count(sfcx) — uint32_t 返回 PHASE_NUM
ll_sfc_enable_write_protect(sfcx) — void 置 WP_EN
ll_sfc_disable_write_protect(sfcx) — void 清 WP_EN
ll_sfc_is_write_protect_enabled(sfcx) — bool 查询 WP_EN
ll_sfc_set_burst_mode(sfcx, burst_mode) uint8_t burst_mode,1..3 void 01=8 beat,10=16 beat,11=32 beat;00 保留
ll_sfc_get_burst_mode(sfcx) — uint32_t 返回 BURST_MD

4.5 DEV_CONF API

以下接口访问 DEV_CONF。除完整寄存器 get/set 外,字段 helper 会保留未涉及的配置。

API 参数 返回 说明
ll_sfc_get_dev_conf(sfcx) — uint32_t 读取完整 DEV_CONF
ll_sfc_set_dev_conf(sfcx, value) uint32_t value void 覆盖写完整 DEV_CONF
ll_sfc_set_status_endian(sfcx, endian) uint8_t endian,0/1 void 状态数据 LSB/MSB-first
ll_sfc_is_status_msb_first(sfcx) — bool 查询状态是否 MSB-first
ll_sfc_set_sample_delay(sfcx, delay) uint8_t delay,0..31 void 写 SMP_DELAY[20:16]
ll_sfc_get_sample_delay(sfcx) — uint32_t 返回采样延时编码
ll_sfc_set_command_type(sfcx, command_type) uint8_t command_type,0/1 void 0=1 字节命令,1=2 字节命令
ll_sfc_is_two_byte_command(sfcx) — bool 查询是否 2 字节命令
ll_sfc_set_status_type(sfcx, status_type) uint8_t status_type,0..3 void 轮询状态 1/2/3/4 字节
ll_sfc_get_status_type(sfcx) — uint32_t 返回状态宽度编码
ll_sfc_set_ce_hold_time(sfcx, hold) uint8_t hold,0..3 void CE hold:0.5/1/1.5/2 cycle
ll_sfc_get_ce_hold_time(sfcx) — uint32_t 返回 CE hold 编码
ll_sfc_set_ce_setup_time(sfcx, setup) uint8_t setup,0..3 void CE setup:0.5/1/1.5/2 cycle
ll_sfc_get_ce_setup_time(sfcx) — uint32_t 返回 CE setup 编码
ll_sfc_set_ce_deselect_interval(sfcx, interval) uint8_t interval,0..15 void CE deselect:0.5..8 cycle
ll_sfc_get_ce_deselect_interval(sfcx) — uint32_t 返回 CE deselect 编码
ll_sfc_set_sample_edge(sfcx, edge) uint8_t edge,0/1 void 0=奇数边沿,1=偶数边沿
ll_sfc_is_sampled_on_even_edge(sfcx) — bool 查询是否偶数边沿采样
ll_sfc_set_clock_polarity(sfcx, polarity) uint8_t polarity,0/1 void 0=高有效,1=低有效
ll_sfc_is_clock_active_low(sfcx) — bool 查询 SCLK 是否低有效
ll_sfc_set_ce_inactive_level(sfcx, level) uint8_t level,0/1 void 设置 CE 无效电平低/高
ll_sfc_is_ce_inactive_high(sfcx) — bool 查询 CE 无效电平是否为高
ll_sfc_set_hold_inactive_level(sfcx, level) uint8_t level,0/1 void 设置 HOLD 无效电平低/高
ll_sfc_is_hold_inactive_high(sfcx) — bool 查询 HOLD 无效电平是否为高
ll_sfc_set_wp_inactive_level(sfcx, level) uint8_t level,0/1 void 设置 WP 无效电平低/高
ll_sfc_is_wp_inactive_high(sfcx) — bool 查询 WP 无效电平是否为高

4.6 设备状态 API

API 参数 返回 说明
ll_sfc_set_dev_status_expect(sfcx, value) uint32_t value void 覆盖写 DEV_STA_EXP
ll_sfc_get_dev_status_expect(sfcx) — uint32_t 读取 DEV_STA_EXP
ll_sfc_get_dev0_status(sfcx) — uint32_t 读取 DEV0_STA_RT 实时值
ll_sfc_set_dev_status_mask(sfcx, mask) uint32_t mask void 覆盖写 DEV_STA_MSK
ll_sfc_get_dev_status_mask(sfcx) — uint32_t 读取 DEV_STA_MSK

4.7 TRAN_CFG0 API

以下接口的 phase 范围均为 0..5。ll_sfc_set_tran_cfg0() 覆盖一个 phase 的完整寄存器,字段 setter 只修改目标字段。

API 参数 返回 说明
ll_sfc_get_tran_cfg0(sfcx, phase) uint8_t phase uint32_t 读取 TRAN_CFG0[phase]
ll_sfc_set_tran_cfg0(sfcx, phase, value) uint8_t phase,uint32_t value void 覆盖对应 phase 的完整配置
ll_sfc_set_tran_cfg0_clock_mode(sfcx, phase, clock_mode) uint8_t clock_mode void 仅 SDR 000 有定义,保持默认
ll_sfc_get_tran_cfg0_clock_mode(sfcx, phase) — uint32_t 读取 CLK_MODE
ll_sfc_set_tran_cfg0_address_width(sfcx, phase, address_width) uint8_t address_width,0..7 void 0 无地址;1..5 为 1..5 字节;6/7 为 6 字节
ll_sfc_get_tran_cfg0_address_width(sfcx, phase) — uint32_t 读取地址宽度编码
ll_sfc_enable_tran_cfg0_poll(sfcx, phase) — void 置 POLL_EN;优先于方向和 DATA_EN
ll_sfc_disable_tran_cfg0_poll(sfcx, phase) — void 清 POLL_EN
ll_sfc_is_tran_cfg0_poll_enabled(sfcx, phase) — bool 查询该 phase 轮询使能
ll_sfc_enable_tran_cfg0_command(sfcx, phase) — void 置 CMD_EN
ll_sfc_disable_tran_cfg0_command(sfcx, phase) — void 清 CMD_EN
ll_sfc_is_tran_cfg0_command_enabled(sfcx, phase) — bool 查询命令 phase 使能
ll_sfc_set_tran_cfg0_phase_format(sfcx, phase, format) uint8_t format,0/1 void 选择硬件 phase format 0/1
ll_sfc_is_tran_cfg0_format1(sfcx, phase) — bool 查询是否 format 1
ll_sfc_set_tran_cfg0_dummy_cycles(sfcx, phase, dummy_cycles) uint8_t dummy_cycles,0..63 void 写 DMY_BITS
ll_sfc_get_tran_cfg0_dummy_cycles(sfcx, phase) — uint32_t 读取 dummy cycle 数
ll_sfc_enable_tran_cfg0_data(sfcx, phase) — void 置 DATA_EN
ll_sfc_disable_tran_cfg0_data(sfcx, phase) — void 清 DATA_EN
ll_sfc_is_tran_cfg0_data_enabled(sfcx, phase) — bool 查询数据 phase 使能
ll_sfc_set_tran_cfg0_command(sfcx, phase, command) uint16_t command void 写 CMD[15:0];实际宽度由 CMD_TYPE 决定
ll_sfc_get_tran_cfg0_command(sfcx, phase) — uint32_t 读取命令值

4.8 传输长度和地址 API

API 参数 返回 说明
ll_sfc_set_transfer_length(sfcx, length) uint32_t length void 写 TRAN_LEN,单位为字节;0 表示无数据
ll_sfc_get_transfer_length(sfcx) — uint32_t 读取 TRAN_LEN
ll_sfc_set_device_address(sfcx, phase, address) uint8_t phase,uint32_t address void 写 Direct phase 地址低 32 位
ll_sfc_get_device_address(sfcx, phase) uint8_t phase uint32_t 读 Direct phase 地址低 32 位
ll_sfc_set_device_address_plus(sfcx, phase, address) uint8_t phase,uint32_t address,建议 0..0xFFFF void 写 Direct phase 地址高 16 位;寄存器只有低 16 位有效
ll_sfc_get_device_address_plus(sfcx, phase) uint8_t phase uint32_t 读取地址高位,只有低 16 位有意义
ll_sfc_set_memory_address(sfcx, address) uint32_t address void 写非 descriptor DMA 内存地址
ll_sfc_get_memory_address(sfcx) — uint32_t 读取非 descriptor DMA 内存地址

4.9 触发 API

TRIG 是只写寄存器;三个接口各写一个触发位,不返回执行结果,也不等待硬件状态变化。

API 参数 返回 说明
ll_sfc_trigger_start(sfcx) — void 写 TRIG.START=1 启动当前配置的事务;调用前应确认 WORKING=0
ll_sfc_trigger_stop(sfcx) — void 写 TRIG.STOP=1 终止传输;不会自动清 FIFO/状态
ll_sfc_trigger_fifo_flush(sfcx) — void 写 TRIG.FLUSH=1 复位 FIFO

4.10 SR 状态 API

SR 为只读。所有查询都直接反映采样时刻的硬件状态,不改变任何标志。

API 参数 返回 说明
ll_sfc_get_status(sfcx) — uint32_t 读取完整 SR
ll_sfc_get_fifo_word_count(sfcx) — uint32_t 读取 FIFONUM[22:16],单位 word
ll_sfc_is_working(sfcx) — bool 查询 WORKING
ll_sfc_get_busy_state(sfcx) — uint32_t 读取 BUSY[6:5] 编码
ll_sfc_is_end(sfcx) — bool 查询 END
ll_sfc_is_tran_req(sfcx) — bool 查询发送 FIFO 请求 TRAN_REQ
ll_sfc_is_rece_req(sfcx) — bool 查询接收 FIFO 请求 RECE_REQ
ll_sfc_is_over(sfcx) — bool 查询 OVER
ll_sfc_is_under(sfcx) — bool 查询 UNDER
ll_sfc_get_pending_status(sfcx) — uint32_t 返回 SR & SFC_SR_INT_MASK,只含五位可清除事件

4.11 SCR 状态清除 API

SCR 为只写、写 1 清零。除 ll_sfc_clear_status_bits() 外,其余接口每次只写一个清零位,不能通过返回值组合多个清零请求。

API 参数 返回 说明
ll_sfc_clear_status_bits(sfcx, mask) uint32_t mask,使用 SFC_SCR_CLR_* void 按 mask 同时清除多个状态/中断标志
ll_sfc_clear_end_status(sfcx) — void 清除 END
ll_sfc_clear_tran_req_status(sfcx) — void 清除 TRAN_REQ/TREQ
ll_sfc_clear_rece_req_status(sfcx) — void 清除 RECE_REQ/RREQ
ll_sfc_clear_over_status(sfcx) — void 清除 OVER
ll_sfc_clear_under_status(sfcx) — void 清除 UNDER
ll_sfc_clear_all_status_bits(sfcx) — void 写 SFC_SCR_CLR_ALL,清除全部可清除状态

4.12 INTC 中断控制 API

INTC 的 1 表示屏蔽、0 表示开放。字段 helper 只修改目标位;ll_sfc_set_interrupt_mask() 和两个 all 接口会覆盖整个寄存器。

API 参数 返回 说明
ll_sfc_set_interrupt_mask(sfcx, mask) uint32_t mask void 覆盖写完整 INTC,1=masked
ll_sfc_get_interrupt_mask(sfcx) — uint32_t 读取完整 INTC
ll_sfc_is_end_interrupt_masked(sfcx) — bool 查询 END 中断屏蔽位
ll_sfc_is_treq_interrupt_masked(sfcx) — bool 查询 TREQ 中断屏蔽位
ll_sfc_is_rreq_interrupt_masked(sfcx) — bool 查询 RREQ 中断屏蔽位
ll_sfc_is_over_interrupt_masked(sfcx) — bool 查询 OVER 中断屏蔽位
ll_sfc_is_undr_interrupt_masked(sfcx) — bool 查询 UNDER 中断屏蔽位
ll_sfc_mask_interrupts(sfcx, mask) uint32_t mask,使用 SFC_INTC_MASK_* void 按 mask 置位屏蔽
ll_sfc_mask_end_interrupt(sfcx) — void 屏蔽 END
ll_sfc_mask_treq_interrupt(sfcx) — void 屏蔽 TREQ
ll_sfc_mask_rreq_interrupt(sfcx) — void 屏蔽 RREQ
ll_sfc_mask_over_interrupt(sfcx) — void 屏蔽 OVER
ll_sfc_mask_undr_interrupt(sfcx) — void 屏蔽 UNDER
ll_sfc_unmask_interrupts(sfcx, mask) uint32_t mask,使用 SFC_INTC_MASK_* void 按 mask 清除屏蔽
ll_sfc_unmask_end_interrupt(sfcx) — void 开放 END
ll_sfc_unmask_treq_interrupt(sfcx) — void 开放 TREQ
ll_sfc_unmask_rreq_interrupt(sfcx) — void 开放 RREQ
ll_sfc_unmask_over_interrupt(sfcx) — void 开放 OVER
ll_sfc_unmask_undr_interrupt(sfcx) — void 开放 UNDER
ll_sfc_mask_all_interrupts(sfcx) — void 覆盖写 SFC_INTC_MASK_ALL
ll_sfc_unmask_all_interrupts(sfcx) — void 覆盖写 0,开放全部事件

4.13 FSM 调试 API

以下接口只读,不修改控制器状态。返回值是内部状态机编码,不应作为稳定软件 ABI 使用。

API 参数 返回 说明
ll_sfc_get_fsm(sfcx) — uint32_t 读取完整 FSM
ll_sfc_get_ahb_fsm(sfcx) — uint32_t 读取 FSM_AHB[19:16]
ll_sfc_get_spi_fsm(sfcx) — uint32_t 读取 FSM_SPI[15:8]
ll_sfc_get_dmac_fsm(sfcx) — uint32_t 读取 FSM_DMAC[7:4]
ll_sfc_get_rmc_fsm(sfcx) — uint32_t 读取 FSM_RMC[2:0]

4.14 CGE 时钟门控 API

“enable”表示开启门控功能,不是开启总线时钟。ll_sfc_set_clock_gate() 覆盖整个寄存器;写 0 会关闭全部门控功能。

API 参数 返回 说明
ll_sfc_get_clock_gate(sfcx) — uint32_t 读取完整 CGE
ll_sfc_set_clock_gate(sfcx, value) uint32_t value void 覆盖写 CGE;0=关闭全部门控功能
ll_sfc_enable_flow_clock_gate(sfcx) — void 置 FLOW_EN
ll_sfc_disable_flow_clock_gate(sfcx) — void 清 FLOW_EN
ll_sfc_is_flow_clock_gate_enabled(sfcx) — bool 查询 FLOW_EN
ll_sfc_enable_dma_clock_gate(sfcx) — void 置 DMA_EN
ll_sfc_disable_dma_clock_gate(sfcx) — void 清 DMA_EN
ll_sfc_is_dma_clock_gate_enabled(sfcx) — bool 查询 DMA_EN
ll_sfc_enable_rmc_clock_gate(sfcx) — void 置 RMC_EN
ll_sfc_disable_rmc_clock_gate(sfcx) — void 清 RMC_EN
ll_sfc_is_rmc_clock_gate_enabled(sfcx) — bool 查询 RMC_EN
ll_sfc_enable_spi_clock_gate(sfcx) — void 置 SPI_EN
ll_sfc_disable_spi_clock_gate(sfcx) — void 清 SPI_EN
ll_sfc_is_spi_clock_gate_enabled(sfcx) — bool 查询 SPI_EN
ll_sfc_enable_reg_clock_gate(sfcx) — void 置 REG_EN
ll_sfc_disable_reg_clock_gate(sfcx) — void 清 REG_EN
ll_sfc_is_reg_clock_gate_enabled(sfcx) — bool 查询 REG_EN

4.15 CMD_IDX API

以下接口控制下一次 CDT 执行。索引范围 0..63;入口有效性和链路长度由调用方保证。

API 参数 返回 说明
ll_sfc_get_cmd_idx_register(sfcx) — uint32_t 读取完整 CMD_IDX
ll_sfc_set_cmd_idx_register(sfcx, value) uint32_t value void 覆盖写完整 CMD_IDX
ll_sfc_enable_cmd_idx_cdt_data(sfcx) — void 置 CDT_DATA_EN
ll_sfc_disable_cmd_idx_cdt_data(sfcx) — void 清 CDT_DATA_EN
ll_sfc_is_cmd_idx_cdt_data_enabled(sfcx) — bool 查询 CDT 数据使能
ll_sfc_set_cmd_idx_transfer_direction(sfcx, direction) uint8_t direction,0/1 void 0=读 Flash,1=写 Flash
ll_sfc_is_cmd_idx_write_transfer(sfcx) — bool 查询 CDT 方向是否为写
ll_sfc_set_cmd_idx_index(sfcx, index) uint8_t index,0..63 void 写起始 CDT entry 索引
ll_sfc_get_cmd_idx_index(sfcx) — uint32_t 读取起始 entry 索引

4.16 CDT 和 DMA 地址 API

COL_ADDR、ROW_ADDR、STA_ADDR0/1 只在 CDT 模式使用;DES_ADDR 只在 DMA descriptor 模式使用。所有地址寄存器都是完整 32-bit 写,LL 层不做地址转换。

API 参数 返回 说明
ll_sfc_set_column_address(sfcx, address) uint32_t address void 写 CDT 列地址
ll_sfc_get_column_address(sfcx) — uint32_t 读 CDT 列地址
ll_sfc_set_row_address(sfcx, address) uint32_t address void 写 CDT 行地址
ll_sfc_get_row_address(sfcx) — uint32_t 读 CDT 行地址
ll_sfc_set_status_address0(sfcx, address) uint32_t address void 写 CDT 状态地址 0
ll_sfc_get_status_address0(sfcx) — uint32_t 读 CDT 状态地址 0
ll_sfc_set_status_address1(sfcx, address) uint32_t address void 写 CDT 状态地址 1
ll_sfc_get_status_address1(sfcx) — uint32_t 读 CDT 状态地址 1
ll_sfc_set_descriptor_address(sfcx, address) uint32_t address void 写 DMA descriptor 地址
ll_sfc_get_descriptor_address(sfcx) — uint32_t 读 DMA descriptor 地址

4.17 GLB1、XIP 和软复位 API

API 参数 返回 说明
ll_sfc_get_glb1(sfcx) — uint32_t 读取完整 GLB1
ll_sfc_set_glb1(sfcx, value) uint32_t value void 覆盖写完整 GLB1
ll_sfc_set_reset(sfcx, reset_cycles) uint8_t reset_cycles,0..63 void 写 RESET[20:15] 并启动软复位;0 为自动释放
ll_sfc_get_reset(sfcx) — uint32_t 读取当前复位倒计时
ll_sfc_set_tran_dir_select(sfcx, value) uint8_t value void 写 TRAN_DIR_SEL;应保持默认 0
ll_sfc_get_tran_dir_select(sfcx) — uint32_t 读取 TRAN_DIR_SEL
ll_sfc_set_mode_bit(sfcx, mode_bit) uint8_t mode_bit void 写 XIP 连续读 mode bits
ll_sfc_get_mode_bit(sfcx) — uint32_t 读取 XIP mode bits
ll_sfc_enable_xip(sfcx) — void 置 XIP_EN
ll_sfc_disable_xip(sfcx) — void 清 XIP_EN
ll_sfc_is_xip_enabled(sfcx) — bool 查询 XIP 状态
ll_sfc_enable_dqs(sfcx) — void 置 DQS_EN;规格要求保持默认
ll_sfc_disable_dqs(sfcx) — void 清 DQS_EN;规格要求保持默认
ll_sfc_is_dqs_enabled(sfcx) — bool 查询 DQS 状态
ll_sfc_set_chip_select(sfcx, chip_select) uint8_t chip_select void 写 CHIP_SEL;仅默认 00 有效,其它值保留
ll_sfc_get_chip_select(sfcx) — uint32_t 读取 CHIP_SEL

4.18 Device 1 和 TRAN_CFG1 API

phase 范围 0..5。TRAN_CFG1 的 transfer mode 编码为 0、1、2、3、5、6、7,4 保留。

API 参数 返回 说明
ll_sfc_get_dev1_status(sfcx) — uint32_t 读取 DEV1_STA_RT
ll_sfc_get_tran_cfg1(sfcx, phase) uint8_t phase uint32_t 读取 TRAN_CFG1[phase]
ll_sfc_set_tran_cfg1(sfcx, phase, value) uint8_t phase,uint32_t value void 覆盖对应 phase 的完整 TRAN_CFG1
ll_sfc_set_tran_cfg1_data_endian(sfcx, phase, endian) uint8_t endian,0/1 void 0=LSB-first,1=MSB-first
ll_sfc_is_tran_cfg1_data_msb_first(sfcx, phase) — bool 查询 phase 数据是否 MSB-first
ll_sfc_set_tran_cfg1_word_unit(sfcx, phase, word_unit) uint8_t word_unit,0..2 void 1/2/4 字节数据单元;3 保留
ll_sfc_get_tran_cfg1_word_unit(sfcx, phase) — uint32_t 读取 word unit
ll_sfc_set_tran_cfg1_transfer_mode(sfcx, phase, transfer_mode) uint8_t transfer_mode,0..3 或 5..7 void 标准/dual/quad 等 SPI line mode
ll_sfc_get_tran_cfg1_transfer_mode(sfcx, phase) — uint32_t 读取 transfer mode

4.19 CDT 和数据端口 API

API 参数 返回 说明
ll_sfc_get_cdt_word(sfcx, index) uint8_t index,0..255 uint32_t 读取一个 CDT word
ll_sfc_set_cdt_word(sfcx, index, value) uint8_t index,0..255;uint32_t value void 覆盖写一个 CDT word;index 不是 entry 编号
ll_sfc_read_data(sfcx) — uint32_t 从 DR 读取一个 CPU FIFO word
ll_sfc_write_data(sfcx, data) uint32_t data void 向 DR 写入一个 CPU FIFO word

ll_sfc_read_data() 不检查 FIFONUM,空 FIFO 读可能返回无效数据;ll_sfc_write_data() 不检查剩余空间,满 FIFO 写可能导致 overflow。批量访问时应先读取 FIFONUM,并自行实现超时和错误处理。


5. 典型完整示例

以下示例用于说明 LL 调用顺序,不是可直接量产的 Flash 驱动。时钟、pinmux、Flash 上电/复位、命令、地址宽度、dummy、采样、CE timing、XIP mode bits 和超时时间必须按实际器件与板卡修改。

5.1 Direct 模式 CPU FIFO 读取 JEDEC ID

示例使用标准 RDID(0x9F) 读取 3 字节 ID。TRAN_LEN=3,CPU FIFO 读取一个 32-bit word,再按 LSB-first 提取低 3 字节。这里假设 SFC 时钟、pinmux 和 Flash 已就绪,示例中的采样与 CE 参数只是占位值。

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "im_error.h"
#include "im_time.h"
#include "ll_sfc.h"

#define APP_SFC_TIMEOUT_MS   (100U)
#define APP_SFC_ERROR_MASK   (SFC_SR_OVER_MASK | SFC_SR_UNDER_MASK)

static void app_sfc_abort(void)
{
    ll_sfc_trigger_stop(SFC);
    ll_sfc_trigger_fifo_flush(SFC);
    ll_sfc_clear_all_status_bits(SFC);
}

static void app_sfc_common_config(void)
{
    ll_sfc_set_poll_time(SFC, 100U);
    ll_sfc_set_status_endian(SFC, 0U);
    ll_sfc_set_sample_delay(SFC, 0U);
    ll_sfc_set_command_type(SFC, 0U);
    ll_sfc_set_status_type(SFC, 0U);
    ll_sfc_set_ce_hold_time(SFC, 3U);
    ll_sfc_set_ce_setup_time(SFC, 3U);
    ll_sfc_set_ce_deselect_interval(SFC, 3U);
    ll_sfc_set_sample_edge(SFC, 0U);
    ll_sfc_set_clock_polarity(SFC, 0U);
    ll_sfc_set_ce_inactive_level(SFC, 1U);
    ll_sfc_set_hold_inactive_level(SFC, 1U);
    ll_sfc_set_wp_inactive_level(SFC, 1U);
}

static error_code_t app_sfc_read_jedec_id(uint8_t id[3])
{
    uint32_t word = 0U;
    uint32_t start_ms;
    bool got_word = false;

    if (id == NULL) {
        return IM_ERR_INVALID_PARAM;
    }

    if (!ll_sfc_is_instance(SFC) ||
        ll_sfc_is_working(SFC) ||
        ll_sfc_is_xip_enabled(SFC)) {
        return IM_ERR_BUSY;
    }

    app_sfc_common_config();

    ll_sfc_disable_cdt(SFC);
    ll_sfc_disable_dma_descriptor(SFC);
    ll_sfc_set_operation_mode(SFC, 0U);
    ll_sfc_set_transfer_direction(SFC, 0U);
    ll_sfc_set_phase_count(SFC, 1U);
    ll_sfc_set_fifo_threshold(SFC, 1U);

    ll_sfc_set_tran_cfg0(SFC, 0U, 0U);
    ll_sfc_set_tran_cfg1(SFC, 0U, 0U);
    ll_sfc_set_tran_cfg0_address_width(SFC, 0U, 0U);
    ll_sfc_disable_tran_cfg0_poll(SFC, 0U);
    ll_sfc_enable_tran_cfg0_command(SFC, 0U);
    ll_sfc_set_tran_cfg0_phase_format(SFC, 0U, 0U);
    ll_sfc_set_tran_cfg0_dummy_cycles(SFC, 0U, 0U);
    ll_sfc_enable_tran_cfg0_data(SFC, 0U);
    ll_sfc_set_tran_cfg0_command(SFC, 0U, 0x9FU);

    ll_sfc_set_tran_cfg1_data_endian(SFC, 0U, 0U);
    ll_sfc_set_tran_cfg1_word_unit(SFC, 0U, 0U);
    ll_sfc_set_tran_cfg1_transfer_mode(SFC, 0U, 0U);
    ll_sfc_set_transfer_length(SFC, 3U);

    ll_sfc_clear_all_status_bits(SFC);
    ll_sfc_trigger_fifo_flush(SFC);
    ll_sfc_trigger_start(SFC);

    start_ms = sys_time_get_ms();
    for (;;) {
        uint32_t pending = ll_sfc_get_pending_status(SFC);

        if ((pending & APP_SFC_ERROR_MASK) != 0U) {
            app_sfc_abort();
            return IM_ERR_HARDWARE;
        }

        if (!got_word && (ll_sfc_get_fifo_word_count(SFC) != 0U)) {
            word = ll_sfc_read_data(SFC);
            got_word = true;
            ll_sfc_clear_rece_req_status(SFC);
        }

        if ((pending & SFC_SR_END_MASK) != 0U) {
            if (!got_word && (ll_sfc_get_fifo_word_count(SFC) != 0U)) {
                word = ll_sfc_read_data(SFC);
                got_word = true;
            }

            if (!got_word) {
                app_sfc_abort();
                return IM_ERR_HARDWARE;
            }

            ll_sfc_clear_end_status(SFC);
            id[0] = (uint8_t)(word & 0xFFU);
            id[1] = (uint8_t)((word >> 8U) & 0xFFU);
            id[2] = (uint8_t)((word >> 16U) & 0xFFU);
            return IM_OK;
        }

        if ((uint32_t)(sys_time_get_ms() - start_ms) >= APP_SFC_TIMEOUT_MS) {
            app_sfc_abort();
            return IM_ERR_TIMEOUT;
        }
    }
}

说明:

  • 示例按轮询方式工作,没有配置或开放 SFC 中断。若使用 IRQ,必须同时处理请求清除和 END 清除,避免轮询路径与中断路径重复读 FIFO。
  • transfer_length=3 表示 Flash 数据总长度;CPU FIFO 只保证一个 32-bit word 可读。若实际 word unit、端序或 FIFO 打包方式不同,应按协议调整提取顺序。
  • ll_sfc_get_pending_status() 的读取值可能早于 FIFO 状态变化,因此 END 分支再次检查 FIFONUM。
  • 示例没有轮询 Flash 内部 busy;RDID 通常可直接执行,擦写命令仍需要 RDSR 或 CDT polling phase。
  • 采样延时 0 和 CE timing 3 只是示例值,必须在目标板卡上验证。

5.2 CDT 构建擦除命令链

下面的链由 WREN(0x06)、SECTOR_ERASE(0x20) 和 RDSR(0x05) 硬件轮询组成。轮询等待 WIP(状态 bit 0)清零;命令、地址和状态位必须按实际 NOR Flash 数据手册确认。示例沿用 5.1 的 app_sfc_common_config() 和 app_sfc_abort()。

#include <stdbool.h>

#include "im_error.h"
#include "im_time.h"
#include "ll_sfc.h"

#define APP_SFC_ERASE_TIMEOUT_MS   (3000U)
#define APP_SFC_ERROR_MASK         (SFC_SR_OVER_MASK | SFC_SR_UNDER_MASK)

static void app_sfc_cdt_entry(uint8_t entry,
                              bool link_next,
                              uint8_t address_kind,
                              uint8_t address_width,
                              uint16_t command,
                              bool data_enabled,
                              bool poll_enabled,
                              uint32_t status_expect,
                              uint32_t status_mask)
{
    uint8_t word_index = (uint8_t)(entry * SFC_CDT_WORDS_PER_ENTRY);
    uint32_t word0;
    uint32_t word1;

    word0 = (link_next ? SFC_CDT_WORD0_LINK_MASK : 0U) |
            (0U << SFC_CDT_WORD0_ENDIAN_SHIFT) |
            (0U << SFC_CDT_WORD0_WORD_UNIT_SHIFT) |
            (0U << SFC_CDT_WORD0_TRAN_MODE_SHIFT) |
            ((uint32_t)address_kind << SFC_CDT_WORD0_ADDR_KIND_SHIFT);

    word1 = (0U << SFC_CDT_WORD1_CLK_MODE_SHIFT) |
            ((uint32_t)address_width << SFC_CDT_WORD1_ADDR_WIDTH_SHIFT) |
            ((poll_enabled ? 1U : 0U) << SFC_CDT_WORD1_POLL_EN_SHIFT) |
            (1U << SFC_CDT_WORD1_CMD_EN_SHIFT) |
            (0U << SFC_CDT_WORD1_PHASE_FORMAT_SHIFT) |
            (0U << SFC_CDT_WORD1_DMY_BITS_SHIFT) |
            ((data_enabled ? 1U : 0U) << SFC_CDT_WORD1_DATA_EN_SHIFT) |
            ((uint32_t)command & 0xFFFFU);

    ll_sfc_set_cdt_word(SFC, word_index, word0);
    ll_sfc_set_cdt_word(SFC, (uint8_t)(word_index + 1U), word1);
    ll_sfc_set_cdt_word(SFC, (uint8_t)(word_index + 2U), status_expect);
    ll_sfc_set_cdt_word(SFC, (uint8_t)(word_index + 3U), status_mask);
}

static error_code_t app_sfc_wait_cdt_end(uint32_t timeout_ms)
{
    uint32_t start_ms = sys_time_get_ms();

    for (;;) {
        uint32_t pending = ll_sfc_get_pending_status(SFC);

        if ((pending & APP_SFC_ERROR_MASK) != 0U) {
            return IM_ERR_HARDWARE;
        }

        if ((pending & SFC_SR_END_MASK) != 0U) {
            ll_sfc_clear_end_status(SFC);
            return IM_OK;
        }

        if ((uint32_t)(sys_time_get_ms() - start_ms) >= timeout_ms) {
            return IM_ERR_TIMEOUT;
        }
    }
}

static error_code_t app_sfc_erase_sector(uint32_t flash_address)
{
    error_code_t ret;

    if (ll_sfc_is_working(SFC) || ll_sfc_is_xip_enabled(SFC)) {
        return IM_ERR_BUSY;
    }

    app_sfc_common_config();

    /* entry 0: WREN,无地址、无数据,继续下一项 */
    app_sfc_cdt_entry(0U, true, 0U, 0U, 0x06U, false, false, 0U, 0U);

    /* entry 1: SECTOR_ERASE,使用 ROW_ADDR 的 24-bit 地址,继续下一项 */
    app_sfc_cdt_entry(1U, true, 1U, 3U, 0x20U, false, false, 0U, 0U);

    /* entry 2: RDSR polling,等待 WIP(bit0)=0,命令链在此结束 */
    app_sfc_cdt_entry(2U, false, 0U, 0U, 0x05U, false, true, 0U, 0x01U);

    ll_sfc_set_column_address(SFC, 0U);
    ll_sfc_set_row_address(SFC, flash_address);
    ll_sfc_set_status_address0(SFC, 0U);
    ll_sfc_set_status_address1(SFC, 0U);

    ll_sfc_set_transfer_length(SFC, 0U);
    ll_sfc_disable_dma_descriptor(SFC);
    ll_sfc_set_operation_mode(SFC, 0U);

    ll_sfc_disable_cmd_idx_cdt_data(SFC);
    ll_sfc_set_cmd_idx_transfer_direction(SFC, 1U);
    ll_sfc_set_cmd_idx_index(SFC, 0U);
    ll_sfc_enable_cdt(SFC);

    ll_sfc_clear_all_status_bits(SFC);
    ll_sfc_trigger_fifo_flush(SFC);
    ll_sfc_trigger_start(SFC);

    ret = app_sfc_wait_cdt_end(APP_SFC_ERASE_TIMEOUT_MS);
    if (ret != IM_OK) {
        app_sfc_abort();
    }

    return ret;
}

说明:

  • ll_sfc_set_cdt_word() 的 index 是 word 编号,不是 entry 编号;entry 2 从 index 8 开始。
  • ROW_ADDR 保存 24-bit 擦除地址;不同 Flash 的地址 phase 数量和 ADDR_KIND 可能不同。
  • POLL_EN=1 的 RDSR entry 使用 status_expect=0、status_mask=0x01 等待 WIP 清零;其他器件可能使用 busy=1 或不同状态位。
  • 最后一项 LINK=0。索引 63 不能再链接下一项,整条链和数据 phase 唯一性由调用方保证。
  • 控制器 END 表示命令链执行完成;只有链中包含正确的 RDSR polling,才表示 Flash 内部擦除已经完成。
  • 示例会破坏目标扇区数据,擦除地址、保护位、供电和时序必须由 Flash 驱动校验。

5.3 XIP 和软复位边界

以下代码只展示控制器侧寄存器顺序,不包含 Flash 连续读预置、XIP 地址映射、cache 属性或退出帧:

static error_code_t app_sfc_xip_enter(uint8_t mode_bits)
{
    uint32_t glb1;

    if (ll_sfc_is_working(SFC) || ll_sfc_is_xip_enabled(SFC)) {
        return IM_ERR_BUSY;
    }

    /* 前置条件:连续读 phase、dummy 和采样已按 Flash 配置完成 */
    glb1 = ll_sfc_get_glb1(SFC);
    glb1 &= ~(SFC_GLB1_MODE_BIT_MASK |
              SFC_GLB1_XIP_EN_MASK |
              SFC_GLB1_CHIP_SEL_MASK);
    glb1 |= ((uint32_t)mode_bits << SFC_GLB1_MODE_BIT_SHIFT);
    ll_sfc_set_glb1(SFC, glb1);
    ll_sfc_enable_xip(SFC);

    return IM_OK;
}

static void app_sfc_xip_exit(void)
{
    ll_sfc_disable_xip(SFC);
    ll_sfc_set_mode_bit(SFC, 0U);
}

static error_code_t app_sfc_soft_reset(uint8_t reset_cycles)
{
    if (ll_sfc_is_working(SFC) || ll_sfc_is_xip_enabled(SFC)) {
        return IM_ERR_BUSY;
    }

    ll_sfc_set_reset(SFC, reset_cycles);
    return IM_OK;
}

说明:

  • XIP 映射地址由 SoC/板级决定,ll_sfc.h 不提供 XIP 窗口常量。进入 XIP 前必须由系统层建立可执行/可访问映射和 cache 策略。
  • ll_sfc_set_glb1() 是覆盖写,示例先清除 MODE_BIT、XIP_EN 和 CHIP_SEL 再写入 mode bits;实际代码应保留其它有效位。
  • 规格要求 CHIP_SEL=00、DQS_EN 和 TRAN_DIR_SEL 保持默认。除明确验证过的板级方案外,不要改变这些字段。
  • ll_sfc_set_reset() 启动后硬件自动释放,不需要轮询 RESET 字段。复位后必须重新初始化 DEV_CONF、phase、GLB0/GLB1、中断和地址;LL 层不会恢复旧配置。
  • 软件复位不处理外部 Flash、SFC 总线时钟或 pinmux,也不保证 Flash 内部状态已退出。

6. 使用约束和检查清单

6.1 关键约束

  • LL 层只支持 SFC 实例,所有地址按 SFC_IOBASE=0x13440000 计算。
  • LL 层不校验指针、phase、CDT、地址宽度、FIFO 阈值或保留字段;非法值会直接写入硬件。
  • LL 层没有 error_code_t、超时、重试、状态机或错误恢复,所有失败判断由调用方完成。
  • 字段 setter 的读改写不带锁;任务和 IRQ 并发访问同一寄存器会丢写。
  • 全寄存器 setter 会覆盖保留位和其它有效字段,除非调用方已经构造完整值。
  • 不应在 SR.WORKING=1 或 GLB1.XIP_EN=1 时修改 phase、CDT、传输长度、地址或 XIP 配置。
  • TRIG 是只写寄存器,SCR 是只写 W1C 寄存器,不能通过读回确认触发或清除是否完成。
  • SR.FIFONUM、SR.END 和 FIFO 请求是瞬时硬件状态;一次读取不能代表整个传输周期。
  • TRAN_LEN 是字节数,DR 和 FIFO 是 32-bit word;数据打包、端序和 word unit 必须一致。
  • XIP 地址映射、cache 维护、Flash 连续读命令和退出流程不属于 LL 层。
  • 软复位只复位 SFC 控制器,不处理 Flash、时钟或 pinmux;复位后必须重新配置。
  • FSM 编码属于内部调试信息,不能作为稳定产品协议。

6.2 首次集成检查

  • 已确认使用 SFC 实例,且不存在第二个寄存器块指针被传给 LL API。
  • SFC 总线时钟、门控时钟、pinmux 和 Flash 电源/复位已经由 BSP 正确配置。
  • 当前没有任务、IRQ 或 DMA 与本次 LL 配置并发访问同一控制器。
  • 每次启动前都确认 SR.WORKING=0,XIP 场景下确认已按流程关闭 XIP。
  • DEV_CONF 的命令宽度、状态宽度、端序、采样延时/边沿、CE timing 和无效电平已按器件手册配置。
  • PHASE_NUM、TRAN_CFG0、TRAN_CFG1、DEV_ADDR 和 DEV_ADDR_PLUS 与实际命令序列一致。
  • 所有 phase index 位于 0..5,FIFO 阈值位于 1..63,address width、dummy 和 word unit 未使用保留值。
  • TRAN_LEN 以字节为单位;无数据 phase 时为 0,CPU/DMA 数据通路与长度一致。
  • CPU FIFO 路径在读写前检查 FIFONUM,并为空读、满写、OVER 和 UNDER 定义错误处理。
  • DMA 路径已完成物理地址、对齐、连续性和 cache clean/invalidate;descriptor 地址符合硬件格式。
  • CDT 每个 entry 的 word index 按 entry_index * 4 计算,最后一项 LINK=0,索引 63 不再链接。
  • 启动前已清除旧状态和 FIFO,传输结束后按 END、OVER、UNDER 处理并清理状态。
  • XIP 使能前已建立连续读 phase、mode bits、地址映射和 cache 策略;关闭 XIP 后再重配控制器。
  • 软复位后执行了完整的重新初始化,没有复用复位前的 LL 配置假设。
  • 已通过逻辑分析仪、Flash 状态读取或回读测试验证真实时序,而不只依赖寄存器写入成功。