API GUIDE / AON / MIDDLEWARE
AON Middleware 接口
1. 接口规范
1.1 配置与初始化
1.1.1 im_pm_config_default
void im_pm_config_default(im_pm_config_t *config);
将配置结构清零,并把 resume_from_retained_record 初始化为 false。
config == NULL时函数安全返回,不写入任何内容。- 该函数只初始化配置值,不访问 HAL,也不改变 PM 状态。
1.1.2 im_pm_init
error_code_t im_pm_init(const im_pm_config_t *config);
初始化和启动顺序:
- 校验配置和静态客户端链表,失败返回
IM_ERR_INVALID_PARAM。 - 加锁;状态不是
RESET或READY时返回IM_ERR_BUSY。 - 调用
hal_pm_init(),失败则记录fault并进入FAULT。 - 调用
hal_pm_get_capabilities()保存能力快照,失败同样进入FAULT。 - 若
resume_from_retained_record=true,校验恢复记录与当前镜像是否匹配。 - 匹配则恢复到
SLEEPING并重建挂起客户端栈,否则回到READY。
注意:
- 第 4 步失败时当前实现不会调用
hal_pm_deinit()回滚第 3 步的初始化,状态直接进入FAULT。 FAULT没有公共退出路径,im_pm_deinit()也要求READY。im_pm_init()在READY状态下可再次调用,会重新执行上述流程。- 配置、链表节点和回调表必须在
im_pm_deinit()前保持有效。
1.1.3 im_pm_deinit
error_code_t im_pm_deinit(void);
- 只允许在
READY状态调用;成功回到RESET。 - 状态不是
READY时返回IM_ERR_BUSY。 - HAL 取消失败时进入
FAULT。 - 不会免费或回收调用方提供的客户端链表。
1.2 模式请求
1.2.1 im_pm_request
error_code_t im_pm_request(im_pm_mode_t mode,
const hal_pm_request_config_t *request_config);
执行顺序:
- 校验
mode在ACTIVE与COUNT之间,校验唤醒源及V1_TIMER/IDLE组合。 - 加锁,状态必须为
READY,否则返回IM_ERR_BUSY。 - 检查能力掩码,不支持时返回
IM_ERR_UNSUPPORTED。 - 将状态置为
PREPARING,按链表正序对匹配客户端调用prepare。 - 将状态置为
SUSPENDING,按链表正序对已 prepare 客户端调用suspend。 - 提交
ARMED恢复记录,状态置为ARMED。 - 调用对应 HAL
enter接口。 - 成功后提交
SLEEPING恢复记录并进入SLEEPING,返回IM_OK。
失败处理:
| 失败点 | 行为 | 最终状态 |
|---|---|---|
| prepare 失败 | 对此前 prepare 成功的客户端按正序调用 complete(prepare_error) |
READY |
| suspend 失败 | 按逆序恢复已 suspend 客户端,恢复成功的客户端收到 complete(suspend_error);再对剩余已 prepare 客户端正序 complete |
READY,恢复失败则 FAULT |
| HAL 进入失败且 HAL 自身回滚成功 | 按逆序恢复客户端,恢复成功的客户端收到 complete(enter_error);正序 complete 剩余客户端;清除恢复记录 |
READY,恢复失败则 FAULT |
| HAL 进入失败且 HAL 自身回滚也失败 | 直接进入 FAULT,当前实现不恢复客户端、不失效记录 |
FAULT |
im_pm_request() 成功返回 SLEEPING 后不会自行休眠 CPU。平台路径需在返回后完成 WFI、V1 上下文保存、V0/V1 握手等动作,并在唤醒后调用 im_pm_resume()。
1.3 唤醒恢复
1.3.1 im_pm_resume
error_code_t im_pm_resume(void);
执行顺序:
- 加锁,状态必须为
SLEEPING,否则返回IM_ERR_BUSY。 - 重新校验恢复记录(含 owner、客户端数量、挂起掩码、模式、序列号)。
- 状态置为
WAKING,调用对应 HALexit接口。 - 提交
RESUMING记录,状态置为RESUMING。 - 按相反顺序调用所有已 suspend 客户端的
resume_early。 - 按相反顺序对
resume_early成功的客户端调用resume,并逐个调用complete(IM_OK);resume失败的客户端收到complete(resume_error)。 - 清除恢复记录,
mode=ACTIVE、fault=IM_OK、状态回到READY。
失败处理:
- 第 2 步记录不一致:失效记录,
fault=IM_ERR_IO,进入FAULT。 - HAL 退出失败:失效记录,
fault为 HAL 返回码,进入FAULT。 - 客户端恢复失败:其余客户端仍会继续恢复并收到
complete,之后失效记录、进入FAULT;im_pm_resume()返回第一个恢复错误。
im_pm_resume() 必须与成功的 im_pm_request() 配对,且只调用一次。
1.4 状态与能力读取
1.4.1 im_pm_get_status
error_code_t im_pm_get_status(im_pm_status_t *status_out);
加锁后返回一致性快照,输出指针为空时返回 IM_ERR_INVALID_PARAM。
1.4.2 im_pm_get_capabilities
error_code_t im_pm_get_capabilities(im_pm_capabilities_t *capabilities_out);
返回 im_pm_init() 时保存的能力快照:
typedef struct {
uint32_t supported_mode_mask;
bool power_off_supported;
} im_pm_capabilities_t;
- 输出指针为空时返回
IM_ERR_INVALID_PARAM。 - 初始化前快照为全零;该方法不会重新查询 HAL。
- 若需要当前编译配置的实时能力,可直接调用
hal_pm_get_capabilities(),该 HAL 接口不要求先初始化。
2. 行为约束与实现说明
2.1 客户端回调顺序与回滚
正常进入和恢复顺序:
prepare: client 0 -> client 1 -> ... -> client N
suspend: client 0 -> client 1 -> ... -> client N
resume_early: client N -> ... -> client 1 -> client 0
resume: client N -> ... -> client 1 -> client 0
complete用于完成 prepare 阶段的簿记,成功或回滚都会按规则调用。- 回调在 MID 递归互斥锁内执行,不能阻塞等待另一个线程调用
im_pm_*()。 - 回调应快速完成,并把耗时等待放到事务之外,避免拉长进入/退出窗口。
resume_early用于恢复依赖,resume用于恢复正常工作;两者都应按逆序执行。
2.2 恢复记录与跨启动校验
恢复记录保存在当前核 .pm_ctx 段,包含以下字段:
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t mode;
uint32_t sequence;
uint32_t client_count;
uint32_t suspended_mask;
uint32_t wake_source;
uint32_t phase;
uint32_t owner;
uint32_t crc;
} im_pm_resume_record_t;
两层校验:
im_pm_resume_record_is_valid()只检查记录自身:magic、version、模式范围、client_count <= 16、wake_source范围、phase == SLEEPING、owner为 1(V1)或 2(V0)、CRC。im_pm_init(resume_from_retained_record=true)还会检查当前 owner、当前客户端数量、挂起掩码范围、当前模式和序列号,并确认记录模式在当前能力范围内。im_pm_resume()还会检查当前 owner、当前客户端数量、挂起掩码范围、当前模式(s_mode)和序列号。
行为:
resume_from_retained_record=true但记录不匹配:失效记录,返回IM_ERR_IO,状态FAULT。im_pm_resume()发现记录不一致:失效记录,返回IM_ERR_IO,状态FAULT。- 成功恢复后清除记录,
mode=ACTIVE、fault=IM_OK、状态READY。 - 记录阶段依次为
ARMED -> SLEEPING -> RESUMING,任何失败路径都会失效记录,但“HAL 进入失败且 HAL 退出也失败”这一分支是例外。
2.3 V0/V1 能力与所有权
- V1 支持
IDLE、LIGHT_NOR、LIGHT_PD。 - V0-only 构建且
CONFIG_G32S10_V0_RUN_IN_SRAM_ONLY=y时支持LIGHT_NOR、LIGHT_PD、RETENTION、DEEP_SLEEP、HIBERNATE。 - V0 未运行在 SRAM-only 区域时不支持任何低功耗模式,避免 HP 断电后从 PSRAM 取指失败。
- LIGHT-PD 可由 V1 发起,也可由收到 V1
CTX_SAVE确认的 V0 发起;两种路径都必须保存 V1 CPU 上下文。 - RETENTION/DEEP_SLEEP/HIBERNATE 由 V0 控制;V1 在这些模式下断电,唤醒后按
AON_FLAG选择恢复或冷启动路径。 IM_PM_MODE_POWER_OFF需要板级 PMIC/外部电源控制,当前 HAL 返回不支持。
2.4 中断、WFI 与平台职责
im_pm_request()只完成事务准备和 HAL 硬件进入,不执行 WFI。- V1 控制的 IDLE/LIGHT-NOR 由 V1 中断路径自行唤醒;V1 控制的 LIGHT-PD 需要保存上下文后执行 WFI,并通过 retained resume entry 恢复。
- V0 控制的 LIGHT-PD/RETENTION/DEEP_SLEEP/HIBERNATE 由 V0 在 LP 域继续运行或执行 WFI,具体唤醒源由板级路径配置。
- 唤醒源选择、GPIO 路由、中断 pending 清理和定时器重新装载必须由应用/板级代码完成。
- 进入前应停止或挂起 DMA、cache 和对外部存储的访问,避免 HP 断电或时钟停止期间产生未完成事务。
2.5 超时、错误与恢复
- HAL 硬件等待超时以
IM_ERR_TIMEOUT透传。 - 参数、状态和记录校验错误分别使用
IM_ERR_INVALID_PARAM、IM_ERR_BUSY、IM_ERR_INVALID_STATE和IM_ERR_IO。 - 当前核不支持的模式返回
IM_ERR_UNSUPPORTED。 - 进入失败时 MID 会尽量按逆序回滚已 suspend 的客户端;回滚成功回到
READY,回滚失败进入FAULT。 - HAL 进入失败且 HAL 退出也失败时直接进入
FAULT,不再恢复客户端。 FAULT没有公共恢复接口,必须复位后重新初始化。
2.6 已知限制与注意事项
IM_PM_STATE_ENTERING是瞬时内部状态,公开状态查询通常看不到。im_pm_get_capabilities()返回初始化快照,不是实时硬件探测结果。resume_from_retained_record=true需要与当前镜像、owner、客户端数量和模式匹配的记录,否则初始化失败。- LIGHT-PD/RETENTION 的上下文恢复依赖
pm_context.S和 retained resume entry,普通业务代码不应直接调用pm_v1_ctx_save()/pm_v1_ctx_restore()。 - 文中的流程是硬件参考流程,MID 的公开可请求模式只以
hal_pm_get_capabilities()返回值为准。
3. 典型用法
3.1 初始化与请求 IDLE
im_pm_config_t config;
im_pm_status_t status;
hal_pm_request_config_t request = {
.wake_source = HAL_PM_WAKE_V1_TIMER,
};
im_pm_config_default(&config);
if (im_pm_init(&config) != IM_OK) {
/* 记录初始化错误。 */
}
if (im_pm_request(IM_PM_MODE_IDLE, &request) == IM_OK) {
/* 平台路径:配置唤醒源、进入 WFI。 */
/* 唤醒后完成硬件退出,再调用 im_pm_resume()。 */
}
if (im_pm_get_status(&status) != IM_OK) {
/* 状态读取失败。 */
}
HAL_PM_WAKE_V1_TIMER 只允许用于 IDLE;其它模式应使用 HAL_PM_WAKE_NONE 或与应用实际唤醒源匹配的值。
3.2 Light-pd 双核流程
示例模式:
light_pd_v1
示例使用 im_pm_config_default() 的默认配置,client_count=0;因此它验证 LIGHT-PD 请求、双核握手和恢复流程,不验证客户端回调顺序或失败回滚。本节只描述 light_pd_v1,不能代表其他模式均已通过验证。
控制路径:
- V1 发起 LIGHT-PD;
- V0 作为唤醒伴随;
- retained resume 入口恢复 V1 上下文。
V1 流程:
im_pm_config_default()与im_pm_init()- 连续打印 5 次计数
- 打印
[V1] enter light-pd im_pm_request(IM_PM_MODE_LIGHT_PD, ...)- 关闭本地中断,设置
HAL_AON_CONTEXT_RESTORE_ENTRY为pm_v1_ctx_restore pm_v1_ctx_save()保存 V1 上下文sys_cache_data_clean_all()清理数据 cache- 若 AON 已恢复为
ACTIVE,直接调用im_pm_resume()完成事务 - 否则发布
HAL_AON_PM_WAKE_REQUEST,执行 fence 后进入 WFI - 唤醒后等待 AON 进入
ACTIVE;必要时发布HAL_AON_PM_HANDSHAKE_RESUME - 调用
im_pm_resume(),打印[V1] light-pd resume complete
V0 流程:
hal_pm_init()- 每秒计数并轮询 AON 握手区
- 看到
HAL_AON_PM_WAKE_REQUEST后清除请求,打印[V0] light-pd wake requested - LP 域继续计数约 5 秒
- 写
0x1210000C = 1触发 V1 恢复,并等待HAL_AON_PM_HANDSHAKE_RESUME - 清除触发寄存器,将 AON 模式切回
HAL_AON_MODE_ACTIVE,清除握手控制 - 打印
[V0] light-pd resume complete
3.3 客户端注册骨架
static error_code_t sensor_prepare(void *ctx, im_pm_mode_t mode)
{
(void)ctx;
(void)mode;
return IM_OK;
}
static error_code_t sensor_suspend(void *ctx, im_pm_mode_t mode)
{
(void)ctx;
(void)mode;
/* 停止新请求、保存寄存器状态。 */
return IM_OK;
}
static const im_pm_device_ops_t s_sensor_pm_ops = {
.prepare = sensor_prepare,
.suspend = sensor_suspend,
};
static const im_pm_client_t s_sensor_pm_client = {
.name = "sensor",
.ops = &s_sensor_pm_ops,
.supported_mode_mask = IM_PM_MODE_MASK(IM_PM_MODE_LIGHT_PD),
};
- 链表按“消费者在前、提供者在后”的顺序静态组织。
- 回调表、客户端节点和
context必须在整个 PM 生命周期内有效。 - 可选回调未实现时填
NULL;不要用未初始化的函数指针占位。
3.4 状态诊断
im_pm_status_t status;
if (im_pm_get_status(&status) == IM_OK) {
/* status.state、status.mode、status.fault */
}
调试建议:
- 进入前确认
status.state == IM_PM_STATE_READY。 - 请求成功后应看到
status.state == IM_PM_STATE_SLEEPING且status.mode为目标模式。 - 唤醒并恢复后应回到
READY、mode == ACTIVE、fault == IM_OK。 - 出现
FAULT时先保存fault和日志,再复位重新初始化;当前没有公共 API 清除FAULT。
4. 示例程序与使用方式
示例入口位于 code/middleware/pm/example/。示例支持以下 8 种模式:
PM_EXAMPLE_MODE |
控制路径 |
|---|---|
idle |
V1 控制 IDLE |
light_nor_v1 |
V1 控制 LIGHT-NOR |
light_nor_v0 |
V0 控制 LIGHT-NOR |
light_pd_v1 |
V1 发起 LIGHT-PD,V0 负责唤醒伴随 |
light_pd_v0 |
V0 发起 LIGHT-PD,并负责 AON 休眠/唤醒 |
retention |
V0 控制 RETENTION,V1 保存并恢复上下文 |
deep_sleep |
V0 控制 DEEP_SLEEP,V1 冷启动 |
hibernate |
V0 控制 HIBERNATE,GPIO 唤醒 |
推荐使用两段式构建:
./code/middleware/pm/example/build.sh --mode light_pd_v1
脚本先用 pm_example_slt_v0_defconfig 编译 V0,再把 V0 镜像嵌入 V1
pm_example_slt_v1_defconfig 构建,最终产物位于 build/artifacts/。单配置路线可使用:
./code/middleware/pm/example/build.sh --single --mode light_pd_v1
正常 light_pd_v1 串口输出至少应包含:
[V1] enter light-pd
[V0] light-pd wake requested
[V1] light-pd resume complete
[V0] light-pd resume complete
恢复后 V1/V0 计数继续增长,说明 V1 上下文保存/恢复、V0 在 LP 域继续运行、V0/V1 跨核握手和 AON 模式恢复均成功。示例自身的跨核握手超时由示例代码返回 IM_ERR_TIMEOUT 或打印失败日志,不属于 im_pm_*() 的直接返回路径。
5. MID 错误码
im_pm_*() 返回 error_code_t。除参数和状态检查外,MID 还会透传客户端或 HAL 回调返回的错误。
| 返回值 | 含义 |
|---|---|
IM_OK |
操作成功。 |
IM_ERR_INVALID_PARAM |
模式、客户端列表、回调表、唤醒源或请求配置非法。 |
IM_ERR_NOT_READY |
HAL 尚未初始化或底层硬件未就绪;通常由 HAL 透传。 |
IM_ERR_BUSY |
当前状态不允许初始化、发起新事务或执行 resume。 |
IM_ERR_UNSUPPORTED |
当前核、构建配置或 G32S10 PM HAL 不支持目标模式。 |
IM_ERR_TIMEOUT |
HAL 硬件等待或客户端回调超时,经 MID 透传。 |
IM_ERR_IO |
MID 检测到无效恢复记录或事务元数据不一致。 |
IM_ERR_INVALID_STATE |
硬件/事务状态不满足进入条件,例如 V0 进入 LIGHT-PD 前未收到 V1 上下文保存确认。 |