API GUIDE / NEMC / HAL

NEMC HAL 接口

nemc hal api

本文面向 BSP、驱动和应用开发者,说明如何通过 G32S10 NEMC HAL 配置外部静态存储器控制器,并提供公开 API 参考。NEMC 寄存器位域细节、外部器件时序计算、总线电气特性和板级 pinmux 实现不在本文范围内。

NEMC 的数据访问使用固定内存映射窗口,不提供函数式读写接口。HAL 配置完成后,CPU 直接访问 0x1B000000 起始的静态存储窗口完成读写;HAL 只负责控制器配置、状态和复位控制。

API 头文件:

  • code/include/drivers/g32s10/nemc/hal_nemc.h

公开实现位于 code/drivers/g32s10/nemc/hal_nemc.c。ll_nemc.h 和 nemc_regs.h 提供寄存器级接口、NEMC 实例和静态窗口常量,属于 HAL 的内部依赖,不属于本文公开 API。

NEMC HAL 不负责 NEMC 时钟、GPIO pinmux、外部器件上下电/复位、WAIT# 外部逻辑或板级地址映射。调用方必须完成这些准备后才能配置 NEMC 并访问静态存储窗口。


1. 模块概览

普通应用只包含 <hal_nemc.h> 即可,不要自行定义 NEMC 寄存器结构或重复实现窗口地址。

  • 返回 error_code_t 的接口以 IM_OK 表示成功,其他值表示失败。
  • 当前只支持 NEMC 实例;传入其他寄存器块指针会返回 IM_ERR_INVALID_PARAM。
  • 所有需要 hal_nemc_handle_t 的接口都要求句柄中的 instance == NEMC。句柄由调用方分配,HAL 不负责释放。
  • hal_nemc_init() 只配置 SMC0R/SMC1R,不配置 SACR、时钟、GPIO、外部器件电源或复位。
  • 默认初始化保留硬件复位后的 SACR 窗口。只有板级映射不同于默认值时,才调用 hal_nemc_window_config()。
  • 静态存储数据通过内存映射窗口直接访问。默认窗口起始地址为 HAL_NEMC_STATIC_MEMORY_BASE(0x1B000000),最大访问范围为 HAL_NEMC_STATIC_MEMORY_MAX_SIZE(64 KiB)。
  • timing 字段是原始寄存器编码,不是统一的“周期数”数值。TAW_OE、TBP_DS、TRAS、TWAS 使用非线性编码,必须使用对应的 HAL_NEMC_*_CYCLE_* 枚举值。
  • HAL 不提供内部互斥或并发保护。多个任务同时修改配置、复位或访问同一 NEMC 窗口时,调用方必须自行串行化。
  • hal_nemc_soft_reset() 只启动软件复位,不等待完成。复位完成后需要重新调用 hal_nemc_init() 才能恢复 READY 状态和控制器配置。
  • hal_nemc_deinit() 只重置 HAL 软件句柄,不修改时钟、GPIO 或 NEMC 寄存器。
  • 调试接口 hal_nemc_regs_dump() 只有在全局和模块 debug helper 同时启用时才执行寄存器读取;否则保留符号并返回 IM_ERR_UNSUPPORTED。
  • 本文按 CONFIG_SDK_HAL_PARAM_CHECK=y 描述参数检查行为。关闭 SDK/HAL 参数检查后,空指针和非法字段不保证被拦截,调用方必须自行保证参数有效。

2. 典型流程

2.1 Kconfig 配置

NEMC 驱动配置位于 code/drivers/g32s10/nemc/Kconfig,菜单路径为 Drivers -> G32S10 Drivers -> NEMC driver。

配置项 默认值 说明
CONFIG_DRIVER_NEMC n 编译 NEMC HAL,即 hal_nemc.c
CONFIG_NEMC_ENABLE_DEBUG_HELPERS n 编译 NEMC 调试辅助代码;需要同时启用全局 debug helper

仅使用 HAL 时启用:

CONFIG_DRIVER_NEMC=y

需要使用 hal_nemc_regs_dump() 时,再同时启用:

CONFIG_SDK_ENABLE_DEBUG_HELPERS=y
CONFIG_NEMC_ENABLE_DEBUG_HELPERS=y

启用 CONFIG_MIDDLEWARE_SHELL_CMD_NEMC=y 还会注册 nemc_dump_regs Shell 命令。该命令依赖 CONFIG_DRIVER_NEMC,且只有 debug helper 同时启用时才能真正输出寄存器。

参考示例位于 code/examples/hal/nemc_test_example_app/,其 defconfig 已启用 CONFIG_DRIVER_NEMC、CPM、GPIO 和 NEMC 调试功能。示例应用额外依赖 CONFIG_DRIVER_CPM 和 CONFIG_DRIVER_GPIO,但这两个选项不是 HAL 本身编译所必需的。

2.2 板级准备

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

  • 使能 NEMC 模块时钟,例如 hal_cpm_clk_enable(HAL_CPM_ID_GATE_NEMC)。
  • 按板卡原理图配置 NEMC 引脚复用。16-bit 总线通常需要 CS#、AVD、RD#、WE#、WAIT# 和 AD0..AD15;8-bit 总线使用 AD0..AD7 和相应控制信号。
  • 完成外部器件上电、复位释放和总线空闲电平设置。WAIT# 只在实际使用时接入。
  • 确认静态存储窗口与系统中其他外设不重叠;默认窗口为 0x1B000000 起始的 64 KiB。
  • 根据外部器件数据手册计算 TCH、TAW_OE、TBP_DS、TRAS、TWAS、STRV、TRDRV、TWRRV、TAVDH、TAVDP 和 TAVDS。

时钟使能示例:

#include "hal_cpm.h"

error_code_t board_nemc_clock_enable(void)
{
    return hal_cpm_clk_enable(HAL_CPM_ID_GATE_NEMC);
}

2.3 初始化与直接访问

下面的代码只展示 HAL 调用顺序。timing 数值来自现有 FPGA 测试示例,实际产品必须按外部器件手册替换:

#include <stdint.h>

#include "hal_nemc.h"
#include "im_error.h"

#define APP_NEMC_WORD_COUNT (8U)

static hal_nemc_handle_t s_nemc;

static error_code_t app_nemc_init(void)
{
    hal_nemc_config_t config;
    error_code_t ret;

    ret = hal_nemc_config_default(&config);
    if (ret != IM_OK) {
        return ret;
    }

    config.bus_width = (uint8_t)HAL_NEMC_BUS_WIDTH_16;
    config.wait_pin_enable = 0U;

    config.timing.tch = HAL_NEMC_TCH_CYCLE_5;
    config.timing.taw_oe = HAL_NEMC_TAW_OE_CYCLE_10;
    config.timing.tbp_ds = HAL_NEMC_TBP_DS_CYCLE_10;
    config.timing.tras = HAL_NEMC_TRAS_CYCLE_5;
    config.timing.twas = HAL_NEMC_TWAS_CYCLE_5;
    config.timing.strv = 15U;
    config.timing.trdrv = HAL_NEMC_TRDRV_CYCLE_1;
    config.timing.twrrv = HAL_NEMC_TWRRV_CYCLE_0;
    config.timing.tavdh = HAL_NEMC_TAVDH_CYCLE_10;
    config.timing.tavdp = HAL_NEMC_TAVDP_CYCLE_10;
    config.timing.tavds = HAL_NEMC_TAVDS_CYCLE_10;
    config.timing.double_enable = 0U;

    return hal_nemc_init(&s_nemc, NEMC, &config);
}

static error_code_t app_nemc_write_read(void)
{
    volatile uint16_t *window =
        (volatile uint16_t *)(uintptr_t)HAL_NEMC_STATIC_MEMORY_BASE;
    const uint32_t access_size = APP_NEMC_WORD_COUNT * sizeof(uint16_t);
    uint32_t index;

    if (access_size > HAL_NEMC_STATIC_MEMORY_MAX_SIZE) {
        return IM_ERR_INVALID_PARAM;
    }

    for (index = 0U; index < APP_NEMC_WORD_COUNT; index++) {
        window[index] = (uint16_t)(0x1000U + index);
    }

    for (index = 0U; index < APP_NEMC_WORD_COUNT; index++) {
        if (window[index] != (uint16_t)(0x1000U + index)) {
            return IM_ERR_IO;
        }
    }

    return IM_OK;
}

说明:

  • 示例假定 NEMC 时钟、pinmux 和外部器件已经就绪,并假定外部器件允许连续写后读回。
  • 访问宽度必须与 bus_width 匹配;16-bit 总线使用 uint16_t,8-bit 总线使用 uint8_t。
  • 所有访问都必须位于实际映射窗口内。默认窗口大小为 64 KiB,不能跨窗口访问。
  • hal_nemc_init() 成功只表示寄存器配置已写入,不表示外部器件已经正确响应。首次集成应使用示波器、逻辑分析仪或回读校验确认总线波形和数据。

2.4 软件复位流程

hal_nemc_soft_reset() 是异步接口。复位完成后 HAL 仍保持 HAL_NEMC_STATE_RESET,必须重新初始化控制器:

#include "im_time.h"

#define APP_NEMC_RESET_TIMEOUT_MS (100U)

static error_code_t app_nemc_soft_reset_and_reinit(
    const hal_nemc_config_t *config)
{
    uint32_t start_ms;
    uint32_t status;
    error_code_t ret;

    ret = hal_nemc_soft_reset(&s_nemc);
    if (ret != IM_OK) {
        return ret;
    }

    start_ms = sys_time_get_ms();
    for (;;) {
        ret = hal_nemc_soft_reset_status_get(&s_nemc, &status);
        if (ret != IM_OK) {
            return ret;
        }

        if ((status & HAL_NEMC_SOFT_RESET_DONE_FLAG) != 0U) {
            break;
        }

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

        sys_time_delay_ms(1U);
    }

    (void)hal_nemc_soft_reset_status_clear(&s_nemc);

    return hal_nemc_init(&s_nemc, NEMC, config);
}

注意:

  • 复位期间不要访问静态存储窗口,也不要同时修改 SMC0R/SMC1R/SACR。
  • 软件复位只复位 NEMC 控制器,不复位外部存储器、NEMC 时钟或 GPIO pinmux。
  • 复位会恢复 NEMC 寄存器默认值,因此之前通过 hal_nemc_init() 或 hal_nemc_window_config() 写入的配置需要重新应用。
  • hal_nemc_soft_reset_status_get() 在复位未完成时返回 IM_OK 且 status == 0;只有状态读取失败才返回错误。
  • 超时由调用方定义;HAL 本身不提供超时参数,也不会返回 IM_ERR_TIMEOUT。

2.5 可选地址窗口配置

默认映射由硬件复位值决定,hal_nemc_init() 不会写 SACR。只有板级必须改变地址窗口时,才在初始化成功后调用:

hal_nemc_window_config_t window;
error_code_t ret;

window.base = 0x1BU;
window.mask = 0xFFU;

ret = hal_nemc_window_config(&s_nemc, &window);
if (ret != IM_OK) {
    return ret;
}

base 和 mask 是 SACR 的原始 8-bit 字段,不是 32-bit 物理地址或字节长度。上面的 0x1B/0xFF 对应默认 CS1# 窗口;修改时必须依据 NEMC 规格和板级地址规划,不能只按软件计算的地址范围填写。


3. 配置和关键类型

3.1 HAL 状态

hal_nemc_state_t 用于描述句柄所处的软件生命周期阶段:

typedef enum {
    HAL_NEMC_STATE_RESET = 0,
    HAL_NEMC_STATE_READY,
} hal_nemc_state_t;
状态 说明
HAL_NEMC_STATE_RESET 句柄尚未初始化,或已 deinit/soft_reset,不能执行依赖 READY 的配置操作
HAL_NEMC_STATE_READY hal_nemc_init() 成功,允许修改 WAIT#、DOUBLE 和 SACR 窗口

HAL 没有独立的 BUSY 状态。软件复位进行中由调用方通过 hal_nemc_soft_reset_status_get() 判断,期间不要访问 NEMC。

state 是 hal_nemc_handle_t 的字段;本节只定义状态取值,句柄对象及其生命周期约束见 3.2。

3.2 HAL 句柄

HAL 句柄是调用方分配并持有的 HAL 上下文,用于保存 NEMC 实例、当前状态和用户数据。本节描述句柄对象本身及其生命周期。

typedef struct hal_nemc_handle hal_nemc_handle_t;

struct hal_nemc_handle {
    nemc_regs_t *instance;
    hal_nemc_state_t state;
    void *user_data;
};
字段 说明
instance NEMC 寄存器块指针,当前必须为 NEMC
state HAL 软件状态,类型为 hal_nemc_state_t;取值见 3.1
user_data 调用方上下文指针,HAL 只保存不解释;init 会清零,deinit 会置为 NULL

句柄由调用方分配并保证生命周期覆盖所有 HAL 调用。不要复制正在使用的句柄,也不要在多个任务中共享同一个句柄进行并发配置。

3.3 总线位宽

typedef enum {
    HAL_NEMC_BUS_WIDTH_8 = NEMC_SMC0_BW_8BIT,
    HAL_NEMC_BUS_WIDTH_16 = NEMC_SMC0_BW_16BIT,
} hal_nemc_bus_width_t;

hal_nemc_config_t.bus_width 只接受 0 或 1,对应 8-bit 和 16-bit 外部数据总线。位宽必须与 pinmux 和外部器件连接一致。

3.4 时序配置

typedef struct {
    uint8_t tch;
    uint8_t taw_oe;
    uint8_t tbp_ds;
    uint8_t tras;
    uint8_t twas;
    uint8_t strv;
    uint8_t trdrv;
    uint8_t twrrv;
    uint8_t tavdh;
    uint8_t tavdp;
    uint8_t tavds;
    uint8_t double_enable;
} hal_nemc_timing_config_t;
字段 寄存器位域 编码说明
tch SMC0R.TCH 0~15 个周期,使用 HAL_NEMC_TCH_CYCLE_*
taw_oe SMC0R.TAW_OE 0~10、12、15、20、25、31 个周期,使用 HAL_NEMC_TAW_OE_CYCLE_*
tbp_ds SMC0R.TBP_DS 0~10、12、15、20、25、31 个周期,使用 HAL_NEMC_TBP_DS_CYCLE_*
tras SMC0R.TRAS 0~10、12、15、20、25、31 个周期,使用 HAL_NEMC_TRAS_CYCLE_*
twas SMC0R.TWAS 0~10、12、15、20、25、31 个周期,使用 HAL_NEMC_TWAS_CYCLE_*
strv SMC1R.STRV 0~63 个周期,原始字段范围 0x00..0x3F
trdrv SMC1R.TRDRV 0~15 个周期,使用 HAL_NEMC_TRDRV_CYCLE_*
twrrv SMC1R.TWRRV 0~15 个周期,使用 HAL_NEMC_TWRRV_CYCLE_*
tavdh SMC1R.TAVDH 0~15 个周期,使用 HAL_NEMC_TAVDH_CYCLE_*
tavdp SMC1R.TAVDP 0~15 个周期,使用 HAL_NEMC_TAVDP_CYCLE_*
tavds SMC1R.TAVDS 0~15 个周期,使用 HAL_NEMC_TAVDS_CYCLE_*
double_enable SMC1R.DOUBLE 0 关闭,1 开启 DOUBLE timing;HAL 只接受 0 或 1

TAW_OE、TBP_DS、TRAS 和 TWAS 的原始字段不是线性周期值。例如原始值 0xB 表示 12 个周期,而不是 11 个周期。不要手写裸数值,优先使用对应枚举。

3.5 NEMC 配置和默认值

typedef struct {
    uint8_t bus_width;
    uint8_t wait_pin_enable;
    hal_nemc_timing_config_t timing;
} hal_nemc_config_t;
字段 说明
bus_width HAL_NEMC_BUS_WIDTH_8 或 HAL_NEMC_BUS_WIDTH_16
wait_pin_enable 0 关闭 WAIT# 动态访问控制,1 开启;HAL 只接受 0 或 1
timing SMC0R/SMC1R timing 原始字段集合

hal_nemc_config_default() 填充以下默认值:

字段 默认值
bus_width HAL_NEMC_BUS_WIDTH_8
wait_pin_enable 1
timing.tch HAL_NEMC_TCH_CYCLE_7
timing.taw_oe HAL_NEMC_TAW_OE_CYCLE_31
timing.tbp_ds HAL_NEMC_TBP_DS_CYCLE_31
timing.tras HAL_NEMC_TRAS_CYCLE_7
timing.twas HAL_NEMC_TWAS_CYCLE_7
timing.strv HAL_NEMC_STRV_CYCLE_DEFAULT(15)
timing.trdrv HAL_NEMC_TRDRV_CYCLE_0
timing.twrrv HAL_NEMC_TWRRV_CYCLE_0
timing.tavdh HAL_NEMC_TAVDH_CYCLE_1
timing.tavdp HAL_NEMC_TAVDP_CYCLE_2
timing.tavds HAL_NEMC_TAVDS_CYCLE_1
timing.double_enable 0

这些默认值对应 NEMC 复位字段值,但 hal_nemc_config_default() 只填充软件结构,不访问硬件。默认配置不代表任何外部存储器的最优或可用时序,实际使用时仍需按器件手册修改。

3.6 地址窗口配置

typedef struct {
    uint8_t base;
    uint8_t mask;
} hal_nemc_window_config_t;
字段 说明
base SACR.BASE 原始字段值,范围 0x00..0xFF
mask SACR.MASK 原始字段值,范围 0x00..0xFF

默认硬件窗口为 0x1B00_0000 起始的 64 KiB,对应 base = 0x1B、mask = 0xFF。头文件说明的可配置物理地址范围是 0x1B00_0000 到 0x1C00_0000;base/mask 与最终地址窗口的精确对应关系以 NEMC 规格为准。HAL 只检查配置指针非空,不会校验字段组合、地址重叠或映射合法性。

3.7 常用常量和公开类型索引

常量 值 说明
HAL_NEMC_TIMING_4BIT_MAX 0x0F 4-bit timing 字段最大值
HAL_NEMC_STRV_CYCLE_MAX / HAL_NEMC_STRV_MAX 0x3F SMC1R.STRV 最大值
HAL_NEMC_STRV_CYCLE_DEFAULT 0x0F SMC1R.STRV 复位默认值
HAL_NEMC_SACR_FIELD_MAX 0xFF SACR.BASE/MASK 字段最大值
HAL_NEMC_STATIC_MEMORY_BASE 0x1B000000 固定静态存储窗口起始地址
HAL_NEMC_STATIC_MEMORY_MAX_SIZE 0x00010000 窗口最大访问大小,64 KiB
HAL_NEMC_SOFT_RESET_DONE_FLAG 0x00000004 软件复位完成标志(SWRST bit 2)
类型 用途
hal_nemc_state_t HAL 软件状态
hal_nemc_bus_width_t 8-bit/16-bit 总线位宽
hal_nemc_timing_config_t SMC0R/SMC1R timing 配置
hal_nemc_config_t NEMC 初始化配置
hal_nemc_window_config_t SACR 地址窗口配置
hal_nemc_handle_t HAL 句柄
HAL_NEMC_*_CYCLE_* 各 timing 字段的编码值枚举

3.8 常见错误码

错误码 常见原因
IM_OK 操作成功
IM_ERR_INVALID_PARAM 空指针、实例不是 NEMC、bus_width 非 8/16、布尔字段不是 0/1、timing 超出字段范围、strv 大于 63
IM_ERR_NOT_READY 在 init 前、deinit 后或 soft_reset 后调用 WAIT#、DOUBLE 或 SACR 配置接口
IM_ERR_UNSUPPORTED hal_nemc_regs_dump() 编译时未启用全局或模块 debug helper
IM_ERR_TIMEOUT 不由 HAL 返回;调用方轮询软件复位超时时自行产生
IM_ERR_IO 不由 HAL 返回;调用方对静态窗口回读校验失败时自行产生

3.8.1 常见问题定位

现象 优先检查
hal_nemc_init() 返回 IM_ERR_INVALID_PARAM 是否先调用 hal_nemc_config_default()、配置结构是否未初始化、实例是否为 NEMC、布尔字段和 timing 是否越界
WAIT#/DOUBLE/SACR 配置返回 IM_ERR_NOT_READY hal_nemc_init() 是否成功,是否在 deinit 或 soft_reset 后忘记重新初始化
所有外部窗口访问数据异常 时钟、pinmux、片选、AVD/RD#/WE#、位宽和 timing 是否正确;外部器件是否上电并释放复位
读回数据全为固定值或总线挂起 bus_width 与硬件连接是否一致,WAIT# 是否应开启,窗口地址是否与 SACR 匹配
软件复位状态一直为 0 NEMC 时钟是否已使能、复位是否被外部条件阻塞、是否在复位期间清除了状态位
软件复位完成后访问仍异常 是否重新调用 hal_nemc_init(),是否需要重新配置 SACR,外部器件是否也被复位
hal_nemc_regs_dump() 返回 IM_ERR_UNSUPPORTED 是否同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_NEMC_ENABLE_DEBUG_HELPERS
访问超出 64 KiB 后系统异常 检查窗口偏移和访问长度,确保不越过 HAL_NEMC_STATIC_MEMORY_MAX_SIZE

4. API 参考

4.1 API 总览

hal_nemc.h

API 用途
hal_nemc_config_default() 填充默认配置
hal_nemc_init() 初始化句柄并配置控制器
hal_nemc_deinit() 反初始化 HAL 软件句柄
hal_nemc_window_config() 配置 SACR 地址窗口(需要 READY)
hal_nemc_wait_pin_enable() 开启 WAIT#(需要 READY)
hal_nemc_wait_pin_disable() 关闭 WAIT#(需要 READY)
hal_nemc_double_timing_enable() 开启 DOUBLE timing(需要 READY)
hal_nemc_double_timing_disable() 关闭 DOUBLE timing(需要 READY)
hal_nemc_soft_reset() 启动软件复位
hal_nemc_soft_reset_status_get() 读取复位完成状态
hal_nemc_soft_reset_status_clear() 清除复位完成状态
hal_nemc_state_get() 读取 HAL 状态
hal_nemc_regs_dump() 打印寄存器快照

4.2 通用约定

  • 返回 error_code_t 的接口以 IM_OK 表示成功,其他值表示失败。
  • 输出参数必须指向有效内存;接口允许 NULL 时会单独说明。
  • hal_nemc_init()、hal_nemc_deinit() 和配置/复位流程建议只在任务上下文调用。HAL 不提供并发保护,调用方必须串行化所有 NEMC 配置和访问。
  • 以下接口要求句柄处于 HAL_NEMC_STATE_READY:hal_nemc_window_config()、hal_nemc_wait_pin_enable()、hal_nemc_wait_pin_disable()、hal_nemc_double_timing_enable()、hal_nemc_double_timing_disable()。
  • hal_nemc_soft_reset_status_get()、hal_nemc_soft_reset_status_clear() 和 hal_nemc_state_get() 不要求 READY,可用于复位流程和状态诊断。
  • NEMC 没有 HAL 读/写函数。配置完成后直接访问静态存储窗口;HAL 无法捕获事务级错误。
  • 参数检查依赖 CONFIG_SDK_HAL_PARAM_CHECK。关闭检查后,调用方仍需保证指针、实例和字段合法。

4.3 生命周期和初始化 API

hal_nemc_config_default()

error_code_t hal_nemc_config_default(hal_nemc_config_t *config);

把配置结构填充为 SDK 默认值。函数只写软件结构,不访问 NEMC 寄存器,也不初始化句柄。

参数 说明
config 待填充的配置结构,不能为 NULL

返回:

  • IM_OK:填充成功。
  • IM_ERR_INVALID_PARAM:config == NULL。

调用方应在 hal_nemc_config_default() 之后覆盖与板级和外部器件相关的字段,再调用 hal_nemc_init()。

hal_nemc_init()

error_code_t hal_nemc_init(hal_nemc_handle_t *hnemc,
                           nemc_regs_t *instance,
                           const hal_nemc_config_t *config);

初始化 HAL 句柄并编程一组 NEMC 静态存储配置。函数会先清零句柄,再设置 instance、写入 SMC0R/SMC1R 相关字段,并把状态置为 READY。成功时 SMC0R.SMT 固定为地址/数据复用模式 NEMC_SMC0_SMT_ADM。

参数 说明
hnemc 待初始化的 HAL 句柄,不能为 NULL
instance NEMC 寄存器块指针,当前必须为 NEMC
config NEMC 配置,不能为 NULL

返回:

  • IM_OK:配置写入成功,句柄进入 HAL_NEMC_STATE_READY。
  • IM_ERR_INVALID_PARAM:句柄/配置为空、实例不是 NEMC,或配置字段非法。

配置字段非法包括:bus_width 不是 8/16、wait_pin_enable 或 double_enable 不是 0/1、任一 4-bit timing 字段大于 0x0F,或 strv 大于 0x3F。

说明:

  • 本接口不配置 NEMC 时钟、GPIO pinmux、外部器件电源/复位或 SACR 地址窗口。
  • 本接口不执行软件复位,也不等待外部器件就绪。
  • 句柄中的 user_data 会被清零。需要保存调用方上下文时,应在 hal_nemc_init() 成功后自行赋值。
  • 可以在已经初始化的句柄上再次调用本接口重新配置控制器,不需要先 deinit();旧配置会被覆盖。
  • 如果板级地址映射不同于默认值,应在 hal_nemc_init() 成功后调用 hal_nemc_window_config()。

hal_nemc_deinit()

error_code_t hal_nemc_deinit(hal_nemc_handle_t *hnemc);

把 HAL 软件句柄恢复为未初始化状态,即清除 user_data 并把 state 置为 HAL_NEMC_STATE_RESET。函数不修改 NEMC 硬件寄存器。

参数 说明
hnemc HAL 句柄,不能为 NULL,且 instance 必须为 NEMC

返回:

  • IM_OK:软件句柄已反初始化。
  • IM_ERR_INVALID_PARAM:句柄为空或 instance 不是 NEMC。

说明:

  • 不关闭 NEMC 时钟,不恢复 GPIO pinmux,也不恢复 SMC0R/SMC1R/SACR。
  • 需要对控制器执行硬件复位时,使用 hal_nemc_soft_reset()。
  • deinit 后,依赖 READY 的接口返回 IM_ERR_NOT_READY;状态查询和复位状态读取仍可使用原句柄。
  • 如需再次使用 NEMC,重新调用 hal_nemc_init()。

4.4 地址窗口 API

hal_nemc_window_config()

error_code_t hal_nemc_window_config(
    hal_nemc_handle_t *hnemc,
    const hal_nemc_window_config_t *config);

写入 SACR.BASE 和 SACR.MASK,修改静态存储地址窗口。只有板级映射必须改变时才调用。

参数 说明
hnemc 已初始化的 HAL 句柄
config 窗口配置,不能为 NULL

返回:

  • IM_OK:SACR 字段已写入。
  • IM_ERR_INVALID_PARAM:句柄或配置为空,或 instance 不是 NEMC。
  • IM_ERR_NOT_READY:句柄尚未初始化,或已经 deinit/soft_reset。

说明:

  • hal_nemc_init() 不写 SACR;不调用本接口时保留硬件复位窗口。
  • base 和 mask 是原始字段值,HAL 只检查指针,不校验映射范围、重叠或合法性。
  • 默认窗口为 0x1B00_0000 起始的 64 KiB;头文件给出的可配置物理地址范围是 0x1B00_0000 到 0x1C00_0000。
  • 修改窗口后,静态存储访问地址必须与新的硬件映射一致,不能继续假定 HAL_NEMC_STATIC_MEMORY_BASE 仍可用。
  • 软件复位会恢复 SACR 默认值,复位完成后需要重新配置窗口。

4.5 WAIT# 控制 API

hal_nemc_wait_pin_enable()

error_code_t hal_nemc_wait_pin_enable(hal_nemc_handle_t *hnemc);

置位 SMC1R.WP_EN,开启 WAIT# 动态访问控制。WAIT# 引脚必须已由板级 pinmux 配置并正确连接。

参数 说明
hnemc 已初始化的 HAL 句柄

返回:

  • IM_OK:WAIT# 已开启。
  • IM_ERR_INVALID_PARAM:句柄为空或 instance 不是 NEMC。
  • IM_ERR_NOT_READY:句柄未初始化或当前不是 READY。

hal_nemc_init() 会根据 config.wait_pin_enable 设置初始状态;本接口用于初始化后动态开启 WAIT#。

hal_nemc_wait_pin_disable()

error_code_t hal_nemc_wait_pin_disable(hal_nemc_handle_t *hnemc);

清除 SMC1R.WP_EN,关闭 WAIT# 动态访问控制。

参数 说明
hnemc 已初始化的 HAL 句柄

返回:

  • IM_OK:WAIT# 已关闭。
  • IM_ERR_INVALID_PARAM:句柄为空或 instance 不是 NEMC。
  • IM_ERR_NOT_READY:句柄未初始化或当前不是 READY。

4.6 DOUBLE timing 控制 API

hal_nemc_double_timing_enable()

error_code_t hal_nemc_double_timing_enable(hal_nemc_handle_t *hnemc);

置位 SMC1R.DOUBLE,开启加倍 timing 等待。该功能适用于需要更长等待周期的外部器件或低速板级环境。

参数 说明
hnemc 已初始化的 HAL 句柄

返回:

  • IM_OK:DOUBLE timing 已开启。
  • IM_ERR_INVALID_PARAM:句柄为空或 instance 不是 NEMC。
  • IM_ERR_NOT_READY:句柄未初始化或当前不是 READY。

hal_nemc_init() 会根据 config.timing.double_enable 设置初始状态;本接口用于初始化后动态开启。

hal_nemc_double_timing_disable()

error_code_t hal_nemc_double_timing_disable(hal_nemc_handle_t *hnemc);

清除 SMC1R.DOUBLE,关闭加倍 timing 等待。

参数 说明
hnemc 已初始化的 HAL 句柄

返回:

  • IM_OK:DOUBLE timing 已关闭。
  • IM_ERR_INVALID_PARAM:句柄为空或 instance 不是 NEMC。
  • IM_ERR_NOT_READY:句柄未初始化或当前不是 READY。

4.7 软件复位 API

hal_nemc_soft_reset()

error_code_t hal_nemc_soft_reset(hal_nemc_handle_t *hnemc);

清除软件复位完成状态,触发 NEMC 软件复位,并把 HAL 状态置为 HAL_NEMC_STATE_RESET。函数在启动复位后立即返回,不等待复位完成。

参数 说明
hnemc HAL 句柄,不能为 NULL,且 instance 必须为 NEMC

返回:

  • IM_OK:软件复位已启动。
  • IM_ERR_INVALID_PARAM:句柄为空或 instance 不是 NEMC。

说明:

  • 本接口不要求 READY,但句柄的 instance 必须有效。
  • 启动复位后,调用 hal_nemc_soft_reset_status_get() 轮询 HAL_NEMC_SOFT_RESET_DONE_FLAG。
  • 复位完成前不要读写静态存储窗口,也不要修改 NEMC 配置寄存器。
  • 复位会恢复 NEMC 寄存器默认值;完成后应重新调用 hal_nemc_init(),并按需重新配置 SACR。
  • 本接口不复位外部存储器、时钟或 GPIO,也不会自动恢复之前的配置。

hal_nemc_soft_reset_status_get()

error_code_t hal_nemc_soft_reset_status_get(
    const hal_nemc_handle_t *hnemc,
    uint32_t *status_out);

读取 SWRST 的复位完成位,并以掩码形式输出。

参数 说明
hnemc HAL 句柄,不能为 NULL,且 instance 必须为 NEMC
status_out 输出状态,不能为 NULL;完成时为 HAL_NEMC_SOFT_RESET_DONE_FLAG,否则为 0

返回:

  • IM_OK:状态读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针为空,或 instance 不是 NEMC。

说明:

  • 不要求句柄处于 READY,因此可在 hal_nemc_soft_reset() 后立即轮询。
  • 复位未完成不是错误,此时返回 IM_OK 且 *status_out == 0。
  • 建议调用方设置超时并避免忙等。

hal_nemc_soft_reset_status_clear()

error_code_t hal_nemc_soft_reset_status_clear(hal_nemc_handle_t *hnemc);

清除 SWRST 的复位完成状态。hal_nemc_soft_reset() 在启动复位前已经执行过一次清除;通常在确认完成并处理结果后调用。

参数 说明
hnemc HAL 句柄,不能为 NULL,且 instance 必须为 NEMC

返回:

  • IM_OK:清除操作已发出。
  • IM_ERR_INVALID_PARAM:句柄为空或 instance 不是 NEMC。

说明:

  • 不要求 READY。
  • 复位进行中不要清除完成状态,否则可能丢失完成指示。
  • 本接口只清除状态位,不启动或停止复位。

4.8 状态和调试 API

hal_nemc_state_get()

error_code_t hal_nemc_state_get(const hal_nemc_handle_t *hnemc,
                                hal_nemc_state_t *state_out);

读取 HAL 软件状态快照,不访问 NEMC 寄存器。

参数 说明
hnemc HAL 句柄,不能为 NULL,且 instance 必须为 NEMC
state_out 输出当前 HAL 状态,不能为 NULL

返回:

  • IM_OK:状态读取成功。
  • IM_ERR_INVALID_PARAM:句柄或输出指针为空,或 instance 不是 NEMC。

说明:

  • 可在 init、deinit 或 soft_reset 后查询。
  • HAL 只有 RESET 和 READY 两个软件状态;复位正在进行也表现为 RESET,需结合复位状态位判断。

hal_nemc_regs_dump()

error_code_t hal_nemc_regs_dump(const hal_nemc_handle_t *hnemc);

以 INFO 日志输出 SMC0R、SACR、SMC1R 和 SWRST 的当前值以及 NEMC 基地址。

参数 说明
hnemc HAL 句柄,不能为 NULL,且 instance 必须为 NEMC

返回:

  • IM_OK:寄存器已读取并输出日志(需要编译调试辅助代码)。
  • IM_ERR_INVALID_PARAM:调试功能已编译,但句柄为空或 instance 不是 NEMC。
  • IM_ERR_UNSUPPORTED:未同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 与 CONFIG_NEMC_ENABLE_DEBUG_HELPERS;此分支不检查句柄。

说明:

  • 不要求 READY,可用于初始化前、复位后或故障现场诊断。
  • 该接口只读取寄存器,不修改配置。
  • Shell 命令 nemc_dump_regs 使用本接口,需启用 CONFIG_MIDDLEWARE_SHELL_CMD_NEMC 和上述两个 debug helper。

5. 典型完整示例

5.1 初始化、访问和清理

以下示例假设已完成 NEMC 时钟、pinmux 和外部器件准备。示例中的 timing 枚举值必须替换为按器件手册计算并经硬件验证的值:

#include <stdint.h>

#include "hal_nemc.h"
#include "im_error.h"

#define DEMO_NEMC_WORD_COUNT (8U)

static hal_nemc_handle_t s_demo_nemc;

static error_code_t demo_nemc_init(void)
{
    hal_nemc_config_t config;
    error_code_t ret;

    ret = hal_nemc_config_default(&config);
    if (ret != IM_OK) {
        return ret;
    }

    config.bus_width = (uint8_t)HAL_NEMC_BUS_WIDTH_16;
    config.wait_pin_enable = 0U;
    config.timing.tch = HAL_NEMC_TCH_CYCLE_5;
    config.timing.taw_oe = HAL_NEMC_TAW_OE_CYCLE_10;
    config.timing.tbp_ds = HAL_NEMC_TBP_DS_CYCLE_10;
    config.timing.tras = HAL_NEMC_TRAS_CYCLE_5;
    config.timing.twas = HAL_NEMC_TWAS_CYCLE_5;
    config.timing.strv = 15U;
    config.timing.trdrv = HAL_NEMC_TRDRV_CYCLE_1;
    config.timing.twrrv = HAL_NEMC_TWRRV_CYCLE_0;
    config.timing.tavdh = HAL_NEMC_TAVDH_CYCLE_10;
    config.timing.tavdp = HAL_NEMC_TAVDP_CYCLE_10;
    config.timing.tavds = HAL_NEMC_TAVDS_CYCLE_10;
    config.timing.double_enable = 0U;

    return hal_nemc_init(&s_demo_nemc, NEMC, &config);
}

static error_code_t demo_nemc_write_read(void)
{
    volatile uint16_t *window =
        (volatile uint16_t *)(uintptr_t)HAL_NEMC_STATIC_MEMORY_BASE;
    uint32_t index;

    if ((DEMO_NEMC_WORD_COUNT * sizeof(uint16_t)) >
        HAL_NEMC_STATIC_MEMORY_MAX_SIZE) {
        return IM_ERR_INVALID_PARAM;
    }

    for (index = 0U; index < DEMO_NEMC_WORD_COUNT; index++) {
        window[index] = (uint16_t)(0x2000U + index);
    }

    for (index = 0U; index < DEMO_NEMC_WORD_COUNT; index++) {
        if (window[index] != (uint16_t)(0x2000U + index)) {
            return IM_ERR_IO;
        }
    }

    return IM_OK;
}

error_code_t demo_nemc_run(void)
{
    error_code_t ret;

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

    ret = demo_nemc_write_read();

    (void)hal_nemc_deinit(&s_demo_nemc);
    return ret;
}

说明:

  • 该示例不包含时钟、pinmux 或外部器件控制代码,不能直接用于未完成板级准备的系统。
  • 写后读回能够发现大部分接线和 timing 问题,但不能替代对总线时序的硬件测量。
  • 如果调用 hal_nemc_deinit() 后仍要继续访问窗口,必须重新调用 hal_nemc_init()。
  • 如果启用了文件系统、RTOS 或其他外部总线使用者,必须由系统层保证 NEMC 窗口访问互斥。

5.2 调试寄存器快照

启用两个 debug helper 后,可以不经过 hal_nemc_init(),直接调用 hal_nemc_regs_dump() 读取寄存器用于诊断:

#include "hal_nemc.h"
#include "im_log.h"

static void demo_nemc_dump(void)
{
    hal_nemc_handle_t hnemc = {0};

    hnemc.instance = NEMC;
    (void)hal_nemc_regs_dump(&hnemc);
}

如果希望通过 Shell 直接使用该接口,还需要额外启用 CONFIG_MIDDLEWARE_SHELL_CMD_NEMC=y:

nemc_dump_regs

直接使用 nemc_dump_regs 命令需要同时启用以下配置:

CONFIG_SDK_ENABLE_DEBUG_HELPERS=y
CONFIG_NEMC_ENABLE_DEBUG_HELPERS=y
CONFIG_MIDDLEWARE_SHELL_CMD_NEMC=y

前两项使能寄存器读取和日志输出代码,第三项注册 Shell 命令;仅调用 C API 时不需要第三项。只启用 Shell 命令而未同时启用两个 debug helper 时,命令会报告 IM_ERR_UNSUPPORTED。运行该命令还需要 Shell 中间件及可用的控制台后端。


6. 使用约束和检查清单

6.1 关键约束

  • HAL 只支持 NEMC 实例和地址/数据复用静态存储模式。
  • HAL 不配置时钟、GPIO pinmux、外部器件电源/复位或板级地址映射。
  • hal_nemc_init() 不写 SACR;默认窗口由硬件复位值决定。
  • NEMC 数据访问只能通过内存映射窗口完成,HAL 不提供事务级读写 API,也不返回总线错误。
  • 默认窗口为 0x1B000000 起始的 64 KiB,访问不得越界。
  • timing 字段必须使用对应编码枚举;非线性字段不能按裸值推算周期。
  • HAL 不提供并发保护,所有配置、复位和窗口访问都应串行化。
  • hal_nemc_soft_reset() 是异步的;复位完成后必须重新初始化和配置。
  • hal_nemc_deinit() 只清理软件句柄,不恢复硬件。
  • 调试寄存器 dump 需要全局和模块 debug helper 同时启用。

6.2 首次集成检查

  • 已启用 CONFIG_DRIVER_NEMC。
  • 已使能 NEMC 时钟,并完成 CS#、AVD、RD#、WE#、WAIT#、AD 总线的 pinmux 配置。
  • 外部器件已上电、复位已释放,WAIT# 状态符合器件要求。
  • 总线位宽与硬件连接一致,8-bit 使用 AD0..AD7,16-bit 使用 AD0..AD15。
  • 已根据外部器件数据手册配置全部 timing 字段,未仅依赖 SDK 默认值。
  • hal_nemc_init() 成功,并通过 hal_nemc_state_get() 确认状态为 READY。
  • 如修改了 SACR,已按板级地址规划完成 hal_nemc_window_config() 并验证新映射。
  • 所有直接内存访问都位于实际窗口内,且不超过 HAL_NEMC_STATIC_MEMORY_MAX_SIZE。
  • 已通过回读或逻辑分析仪验证写、读、WAIT#、AVD、RD#、WE# 和片选时序。
  • 软件复位流程包含超时、复位完成轮询和重新初始化。
  • 如需寄存器诊断,已同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 与 CONFIG_NEMC_ENABLE_DEBUG_HELPERS。
  • 产品流程在停止或释放 NEMC 前,不再有任务访问静态存储窗口。