API GUIDE / JPEGD / HAL

G32S10 JPEGD HAL 接口

HAL 层接口:详见 code/include/drivers/g32s10/jpegd/hal_jpegd.h 和 code/drivers/g32s10/jpegd/hal_jpegd.c。

1. HAL JPEGD 接口

详见 code/include/drivers/g32s10/jpegd/hal_jpegd.h 和 code/drivers/g32s10/jpegd/hal_jpegd.c。

1.1 公开类型

1.1.1 hal_jpegd_pixel_format_t

值说明
HAL_JPEGD_PIX_FMT_NV12Semi-planar NV12/YUV420 输出
HAL_JPEGD_PIX_FMT_YUYVPacked YUYV/YUV422 输出
HAL_JPEGD_PIX_FMT_Y单 Y 平面输出

1.1.2 hal_jpegd_irq_event_t 和 hal_jpegd_callback_t

事件说明
HAL_JPEGD_IRQ_EVENT_NONE无事件;HAL 不会向已注册回调分发该值
HAL_JPEGD_IRQ_EVENT_FRAME_DONE当前帧完成
HAL_JPEGD_IRQ_EVENT_TIMEOUT当前帧发生硬件 timeout

hal_jpegd_callback_t 的原型为 void (*)(hal_jpegd_irq_event_t event)。回调由hal_jpegd_irq_handler() 在平台 IRQ 上下文调用,不能阻塞。

1.1.3 hal_jpegd_tables_t

字段类型说明
qt_yconst uint32_t *Y 分量量化表,16 word,不能为 NULL
qt_uconst uint32_t *U 分量量化表,16 word,不能为 NULL
qt_vconst uint32_t *V 分量量化表,16 word,不能为 NULL
huffminconst uint32_t *Huffman minimum 表,16 word,不能为 NULL
huffbaseconst uint32_t *Huffman base 表,64 word,不能为 NULL
huffsymbconst uint32_t *Huffman symbol 表,336 word,不能为 NULL

这些指针表示调用方准备好的硬件表 payload。hal_jpegd_sequence_configure() 会把表内容写入 JPEGD 寄存器数组;该函数返回后,HAL 后续单帧启动不再访问这些表指针。

1.1.4 hal_jpegd_param_t

字段类型说明
widthuint32_t输出图像宽度,当前 HAL 校验范围 8-1920
heightuint32_t输出图像高度,当前 HAL 校验范围 8-1440
out_formathal_jpegd_pixel_format_t输出像素格式
tableshal_jpegd_tables_t调用方准备好的 QT/Huffman 硬件表

1.1.5 hal_jpegd_restart_config_t

字段类型说明
restart_markers_enbool是否使能 restart marker 解码
restart_mcu_numuint32_trestart interval,启用时必须在 1..mcu_number

1.1.6 hal_jpegd_config_t

字段类型说明
rehal_jpegd_restart_config_tRestart marker 解码配置
timeout_enbooltrue 时打开硬件 timeout 检测和 timeout interrupt
timeout_thresholduint32_t硬件 timeout 阈值;建议由 hal_jpegd_struct_init() 填默认值
timeout_msuint32_t供上层等待策略使用的超时提示;HAL 不读取该字段,0 由上层解释
reset_cycleuint32_t软件 reset pulse field,范围 0-15;0 时 reset 流程使用默认值

1.1.7 hal_jpegd_frame_t

字段类型说明
bitstream_addrconst void *输入 entropy bitstream 地址,不能为 NULL
bitstream_sizeuint32_t输入 bitstream 字节数,必须非 0,用于 cache clean
dst_y_addrvoid *输出 Y 平面或 packed RAW 起始地址,必须 16 字节对齐
dst_uv_addrvoid *NV12 输出 UV 起始地址;为 NULL 时 HAL 使用紧凑布局推导
dst_buf_sizeuint32_t输出 buffer 总容量,必须覆盖 HAL 计算出的输出容量
output_sizeuint32_thal_jpegd_get_result() 回填的实际 RAW 输出字节数

1.2 公开函数

void hal_jpegd_struct_init(hal_jpegd_param_t *param,
                           hal_jpegd_config_t *config,
                           hal_jpegd_frame_t *frame);

error_code_t hal_jpegd_reset(const hal_jpegd_config_t *config);
error_code_t hal_jpegd_init(const hal_jpegd_config_t *config);
error_code_t hal_jpegd_callback_register(hal_jpegd_callback_t callback);
void hal_jpegd_irq_handler(void);
error_code_t hal_jpegd_sequence_configure(const hal_jpegd_param_t *param,
                                          const hal_jpegd_config_t *config);
error_code_t hal_jpegd_start_frame(const hal_jpegd_frame_t *frame);
error_code_t hal_jpegd_get_result(hal_jpegd_frame_t *frame);
error_code_t hal_jpegd_deinit(void);
error_code_t hal_jpegd_regs_dump(void);

推荐调用顺序:

STEP 1  hal_jpegd_struct_init
↓
STEP 2  填写尺寸、格式、量化表、Huffman 表、restart 与帧缓冲
↓
STEP 3  hal_jpegd_init
↓
STEP 4  hal_jpegd_callback_register
↓
STEP 5  hal_jpegd_sequence_configure
↓
STEP 6  hal_jpegd_start_frame
↓
STEP 7  platform IRQ → hal_jpegd_irq_handler → registered callback
↓
STEP 8  hal_jpegd_get_result
↓
STEP 9  hal_jpegd_deinit

1.3 默认值

hal_jpegd_struct_init() 会清零 param/config/frame,然后设置以下默认值:

字段默认值
param.out_formatHAL_JPEGD_PIX_FMT_NV12
config.timeout_threshold0x00ffffff
config.timeout_ms1000
config.reset_cycle10

width、height、tables、bitstream_addr、bitstream_size、dst_y_addr、dst_buf_size 等字段仍需由调用方填写。

1.4 初始化、复位和反初始化

error_code_t hal_jpegd_reset(const hal_jpegd_config_t *config);
error_code_t hal_jpegd_init(const hal_jpegd_config_t *config);
error_code_t hal_jpegd_deinit(void);

hal_jpegd_reset() 内部流程:

  1. 校验 config != NULL 和 reset_cycle <= 15

  2. 若 reset_cycle == 0,使用 HAL 默认 reset cycle

  3. 写 SW_RESET_CYCLE

  4. 使能 sequence reset 覆盖 sequence-level registers

  5. 触发 SW_SEQ_RESET

  6. 按 SPEC 轮询 JPEG_DEC_TRIG[31] RESET_END,为 1 后才继续;超时返回 IM_ERR_TIMEOUT

hal_jpegd_init() 内部流程:

  1. 校验 runtime config

  2. 清 HAL 内部 context 并保存 config

  3. 检查 systick 是否初始化

  4. 执行 hal_jpegd_reset()

  5. 关闭所有 JPEGD 中断并清 pending

  6. 设置内部 initialized 状态

hal_jpegd_deinit() 会关闭所有中断、清 pending、执行 reset,并清除 HAL 内部 context 和 initialized 状态。反初始化后回调恢复为 HAL 内部 no-op 回调。每次 hal_jpegd_init() 也会先恢复该 no-op 回调,因此需要在 init 成功后重新注册业务回调。

1.5 序列配置

error_code_t hal_jpegd_sequence_configure(const hal_jpegd_param_t *param,
                                          const hal_jpegd_config_t *config);

该函数用于配置一组解码序列的固定参数,包括图像尺寸、输出格式、stride、MCU 数、QT/Huffman 表、restart marker 和 timeout。

参数校验:

参数条件
param/config不能为 NULL
width8-1920
height8-1440
out_format必须为 NV12/YUV420、YUYV/YUV422 或单 Y
tables.qt_y/qt_u/qt_v不能为 NULL
tables.huffmin/huffbase/huffsymb不能为 NULL
reset_cycle0-15
restart_mcu_numrestart_markers_en=true 时必须在 1..mcu_number
HAL 状态必须已经 hal_jpegd_init()

寄存器配置:

  1. 根据输出格式计算对齐宽高、stride、MCU 数和输出容量

  2. 固定写 table selector:Y 使用 table 0,U/V 使用 table 1

  3. 写 qt_tab_y[16]、qt_tab_u[16]、qt_tab_v[16]

  4. 写 huffmin[16]、huffbase[64]、huffsymb[336]

  5. 根据 config.re 配置 restart marker

  6. 根据 config.timeout_en 配置硬件 timeout 和 timeout interrupt

  7. 使能 frame-end interrupt status

  8. 写对齐宽高、stride、输出 RAW 字节总数、MCU 数和输出格式

1.6 单帧启动、中断和取结果

error_code_t hal_jpegd_callback_register(hal_jpegd_callback_t callback);
void hal_jpegd_irq_handler(void);
error_code_t hal_jpegd_start_frame(const hal_jpegd_frame_t *frame);
error_code_t hal_jpegd_get_result(hal_jpegd_frame_t *frame);

hal_jpegd_start_frame():

  1. 校验 HAL 已初始化、sequence config 已配置

  2. 保存 frame 到 HAL 内部 context

  3. 清除历史中断状态

  4. 校验 bitstream_addr != NULL、bitstream_size != 0

  5. 校验 dst_buf_size >= HAL 计算出的输出容量

  6. 校验 dst_y_addr 16 字节对齐

  7. 写 BS_ADDR 和 RAWY_ADDR

  8. 对 input bitstream 执行 cache clean

  9. 对输出 Y/RAWY 范围执行 clean+invalidate

  10. NV12/YUV420 输出下配置 UV 地址;若 dst_uv_addr == NULL,按紧凑布局推导

  11. 触发 START

hal_jpegd_callback_register():

  • 只能在 hal_jpegd_init() 成功后调用

  • 传入非空回调后,JPEGD 中断事件由该回调接收

  • 传入 NULL 后恢复 HAL 内部 no-op 回调

  • 回调在平台 IRQ 上下文执行,不能阻塞;事件为HAL_JPEGD_IRQ_EVENT_FRAME_DONE 或 HAL_JPEGD_IRQ_EVENT_TIMEOUT

hal_jpegd_irq_handler():

  • HAL 不注册 CPU IRQ handler;平台 IRQ 入口必须在收到 JPEGD IRQ 后调用本函数

  • 函数读取 INTR_STAT,清除当前 pending 状态,再向已注册回调分发事件

  • 未初始化、无 pending 状态或未处于已启动帧状态时直接返回

  • 同时出现 timeout 和 frame-end 时优先分发 timeout 事件

hal_jpegd_get_result():

  1. 校验 frame != NULL 和 frame->dst_y_addr != NULL

  2. 校验 HAL 已初始化、sequence 已配置,且已启动帧已由 hal_jpegd_irq_handler() 收尾

  3. 读取 GLB_STAT.RAW_LEN

  4. 若输出大小超过 dst_buf_size,返回 IM_ERR_NO_SPACE

  5. 对输出 buffer 执行 cache invalidate

  6. 回填 frame->output_size

  7. 触发 SW_FRM_RESET

调用方必须在收到 HAL_JPEGD_IRQ_EVENT_FRAME_DONE 后再调用本函数,并传入与hal_jpegd_start_frame() 相同的输出 buffer 字段。帧仍在运行时该函数返回IM_ERR_NOT_READY。

1.7 Cache 管理

HAL 当前在以下位置进行 cache 维护:

阶段地址操作目的
start_framebitstream_addr,长度 bitstream_sizeclean确保硬件读到 CPU 已准备好的 entropy bitstream
start_framedst_y_addr,长度 rawy_sizeclean+invalidate避免 CPU 脏 cache line 在硬件写结果后覆盖 Y/RAW 输出
start_framedst_uv_addr 或推导 UV 地址,长度 rawc_sizeclean+invalidateNV12/YUV420 下避免 CPU 脏 cache line 覆盖 UV 输出
get_resultdst_y_addr,长度 HAL 计算出的输出容量invalidate让 CPU 读取硬件写入后的 RAW 输出

调用方在 hal_jpegd_start_frame() 到 hal_jpegd_get_result() 之间不应修改 input bitstream 或 output buffer。

1.8 线程安全与中断策略

JPEGD HAL 当前通过平台 IRQ 入口调用 hal_jpegd_irq_handler(),再由注册回调接收帧结束或硬件 timeout 事件。HAL 不等待、不轮询 INTR_STAT,也不负责注册或释放 SoC IRQ。

当前 HAL 使用模块级静态 context,不支持多实例并发或无锁多线程访问。回调运行于 IRQ 上下文,不能执行阻塞操作;若上层需要在多任务环境使用 JPEGD,应在 HAL 调用外侧加锁,并使用 ISR 安全的通知机制。

1.9 Debug Register Dump

error_code_t hal_jpegd_regs_dump(void);

hal_jpegd_regs_dump() 用 INFO log 输出 JPEGD 可读寄存器,便于和 spec 或异常现场对照。该接口由两个 Kconfig 开关控制:

开关说明
CONFIG_SDK_ENABLE_DEBUG_HELPERSSDK 全局 debug helper 开关
CONFIG_JPEGD_ENABLE_DEBUG_HELPERSJPEGD driver debug helper 开关

未同时打开这两个开关时,接口仍保留,但直接返回 IM_ERR_UNSUPPORTED。

默认 dump 内容包括普通控制寄存器、状态寄存器和 0x108-0x16C 的 JPEGD debug 寄存器。INTR_CLR 是 write-only 寄存器,dump 时只打印 <skip>,不会读取。

QT/Huffman 表寄存器默认跳过,避免一次 dump 打出过长日志。需要确认表内容时,可在编译参数中打开:

#define HAL_JPEGD_DUMP_READ_TABLES 1U

打开后会额外打印:

表数量
QT_TAB_Y16 word
QT_TAB_U16 word
QT_TAB_V16 word
HUFFMIN16 word
HUFFBASE64 word
HUFFSYMB336 word

2. HAL 调用代码骨架

下面代码只展示 HAL 层入参和调用顺序。qt_*、huff*、entropy_bs 和输出 buffer 均由调用方提前准备。

#include "hal_jpegd.h"

#define JPEGD_DST_ALIGN 16U

extern const uint32_t qt_y[16];
extern const uint32_t qt_u[16];
extern const uint32_t qt_v[16];
extern const uint32_t huffmin[16];
extern const uint32_t huffbase[64];
extern const uint32_t huffsymb[336];
extern const void *entropy_bs;
extern uint32_t entropy_bs_size;

static volatile hal_jpegd_irq_event_t jpegd_event;

static void jpegd_irq_callback(hal_jpegd_irq_event_t event)
{
    jpegd_event = event;
}

void board_jpegd_irq_handler(void)
{
    hal_jpegd_irq_handler();
}

/* Platform-specific task/ISR synchronization, implemented by the caller. */
extern error_code_t wait_for_jpegd_event(volatile hal_jpegd_irq_event_t *event);

static uint8_t raw_dst[1024U * 1024U] __attribute__((aligned(JPEGD_DST_ALIGN)));

error_code_t run_jpegd_hal_decode(void)
{
    hal_jpegd_param_t param;
    hal_jpegd_config_t config;
    hal_jpegd_frame_t frame;
    error_code_t ret;
    error_code_t deinit_ret;

    hal_jpegd_struct_init(&param, &config, &frame);

    param.width = 640U;
    param.height = 480U;
    param.out_format = HAL_JPEGD_PIX_FMT_NV12;
    param.tables.qt_y = qt_y;
    param.tables.qt_u = qt_u;
    param.tables.qt_v = qt_v;
    param.tables.huffmin = huffmin;
    param.tables.huffbase = huffbase;
    param.tables.huffsymb = huffsymb;

    config.re.restart_markers_en = true;
    config.re.restart_mcu_num = 21U;

    frame.bitstream_addr = entropy_bs;
    frame.bitstream_size = entropy_bs_size;
    frame.dst_y_addr = raw_dst;
    frame.dst_uv_addr = NULL;
    frame.dst_buf_size = sizeof(raw_dst);

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

    ret = hal_jpegd_callback_register(jpegd_irq_callback);
    if (ret != IM_OK) {
        goto cleanup;
    }

    ret = hal_jpegd_sequence_configure(&param, &config);
    if (ret != IM_OK) {
        goto cleanup;
    }

    jpegd_event = HAL_JPEGD_IRQ_EVENT_NONE;
    ret = hal_jpegd_start_frame(&frame);
    if (ret != IM_OK) {
        goto cleanup;
    }

    /* The platform JPEGD IRQ entry must call hal_jpegd_irq_handler().
     * The registered callback should notify the task that the frame ended. */
    ret = wait_for_jpegd_event(&jpegd_event);
    if ((ret != IM_OK) || (jpegd_event != HAL_JPEGD_IRQ_EVENT_FRAME_DONE)) {
        ret = (ret == IM_OK) ? IM_ERR_TIMEOUT : ret;
        goto cleanup;
    }

    ret = hal_jpegd_get_result(&frame);

cleanup:
    deinit_ret = hal_jpegd_deinit();
    if ((ret == IM_OK) && (deinit_ret != IM_OK)) {
        return deinit_ret;
    }
    return ret;
}