4.3.29.3. SSF中间件开发指南

基本概念

SSF= SGF(Smart Graph Framework)+ SMF(Smart Message Framework,其中包含SIPC

代码路径:adsp/ssf

SGF

代码路径:adsp/ssf/sgf

SGF音频子系统(HiFi5 DSP)的数据路由框架,实现以下功能:

1、音频下行链路,每一条链路满足配置节点,可包含音效模块、驱动模块、通信模块等。

2、音频上行链路,每一条链路满足配置节点,可包含音效模块、驱动模块、通信模块等。

SGF包含以下多个模块:

Module是dsp数据处理基本模块。一个算法、一个驱动,只要数据链路中处理音频数据,都可以算作一个Module。

定义代码路径:adsp/ssf/sgf/ar/src/module.c

实现代码路径:adsp/ssf/sgf/modules

Component包含了Module,额外添加一系列用于处理数据路由组件。Component是SGF通路数据路由节点,能够接收多路数据,输出多路数据。

头文件路径:adsp/ssf/sgf/ar/include/comp.h

Data Port是Component数据输入输出接口,分为InputOutput。一个Component可以有0个或者多个Input/Output Port,根据配置定义。

头文件路径:adsp/ssf/sgf/ar/include/comp.h

Component1

Component2

Connector用于连接Component,有传递数据、控制通路通断功能,是SGF数据连接器。每两个Component之间需要一个Connector连接。 包含Connector BaseConnector RB两种,前者基本的Connector,仅传递数据指针;后者带有Ringbuffer的Connector,能够缓冲一段数据。常用多路数据输入输出、不同节奏业务之间数据交互。

头文件路径:adsp/ssf/sgf/ar/include/conn.h

Graph所有Component、以及相互之间连接关系。

头文件路径:adsp/ssf/sgf/ar/include/graph_factory.h

代码路径:adsp/ssf/sgf/conf/src/config_ar_x5.c

Trigger周期性触发,以运行Graph。 通常Trigger源可以来自硬件:Timer InterruptI2S InterruptDMA Interrupt。 也可以来自通讯中间件,由其他核来触发运行。

Zone用来划分不同业务的Component,可以通过多线程进行处理一些数据节奏一致并发业务场景。 理论上,假设有n个component,理论可以拥有n个zone,即每个component都一个单独的zone中,但这样做会消耗大量系统资源。 实际上,比较合理方案是,”Zone”根据period_size、trigger源进行划分,同样的period size、同样的trigger应当放到一个zone中。 实际使用场景: 下行链路通过”I2S0 TX”输出,数据处理的period size一致,那么所有下行链路放在”Zone 1”中。 上行链路通过”I2S1 RX”输入,数据处理的period size一致,那么所有上行链路放在”Zone 2”中。

zone

整个Graph的构建遵循”使用尽量少的zone”的原则。

定义comp_param结构体中zone_id段。

SMF

SMF

代码路径:adsp/ssf/smf

整体解决方案分为3个层次:通讯层、消息分发层、应用服务层。

  1. 通讯框架(SIPC):

  • 封装 SIPC Instance实例用来表示传输实例,下面可以多个传输通道(Channel)。

  • 一个可以多个通讯实例Instance。

  • 封装SIPC Channel实例用来表示传输通讯通道。

  1. 消息分发框架(dispatcher): 为services提供服务,不负责解析msg的内容,时序上线运行dispatcher运行services,services产生相应待处理命令后,dispatcher再处理。

  • 消息分发Dispatcher维护一个消息队列一个分发线程。

  • 接收SIPC消息,找到对应的services进行解析。 接受消息时候轮训services->category和msg->category通过category_id区分哪个服务消息。

  • 常驻线程运行disp->msg_queue中指令。

  1. 应用服务框架(services)

  • 一个应用服务Service对接其他应用Application模块。

  • 支持定制应用服务Service。

  • 应用服务Service用来接收消息分发层Dispatcher发送过来消息。

  • 应用服务Service可以申请多个通讯通道Channel。 应用服务Service支持借助通讯通道Channel向不同发送消息。 可以注册多个service,挂在g_disp链上。

service解析进一步操作如下两种:

1.control信息:service负责解析消息通过smf_send_msg_async发送给dispatcher执行。

2.pipeline信息:直接执行pipeline动作,如open/start/triggerterminate/set_buf/set_param 不同服务通过category_id区分。

SIPC

SIPC(Super Inter-Processor Communication) 是 GUA 芯片音频框架——智能通讯框架 SMF(Smart Msg Framework)的一个组成部分,用来封装不同IPC (比如 hb ipc)传输层,提供标准统一接口上层外部使用,使得软件框架可以底层诸多IPC实现接口脱离出来。 因为不同方案或者厂商会使用不同的IPC方式,也可能定制IPC传输,这样使得核间通讯接口尤为发散,从软件代码层面会令软件框架比较难以兼容。

IPC

Sipc 分为三层,

  • Sipc interface - 对外提供一个板级方案,用来控制创建多少个instance和channel 用来进行通讯,具体实现由sipc driver 实现。

  • Sipc driver - 用来实现 sipc 接口,并调用 sipc channel和 sipc intance 的接口,创建instance和需要的channel。

  • Sipc transport - 用来实现 sipc channel 和 sipc instance 的接口,HB-IPC需要实现一层。

路由配置

路由配置文件/adsp/ssf/sgf/conf/src/config_ar_x5.c文件中。 以现有语音唤醒路由图为例。当前路由拓扑上面zone中例所示。

Component配置描述

struct comp_param {
    uint32_t cuid;
    char name[16];
    uint32_t zone_id;
    uint32_t rate;
    uint32_t channel;
    uint32_t bitdepth;
    uint32_t period;
    ele_attr_t attr;
    uint32_t muid;
    trigger_source_t trigger;
    data_port_cfg_t in_port;
    data_port_cfg_t out_port;
};

1.uint32_t cuid; Component Unique ID,使用辅助定义GEN_COMP_UID生成。

2.char name[16]; Component Name,注意最大size为16,字符串超过15个字符。

3.uint32_t zone_id; 表明一个内,每个一个线程顺序处理。

4.uint32_t rate; Component能够处理采样率。

5.uint32_t channel; Component能够处理声道数。

6.uint32_t bitdepth; Component能够处理位深。

7.uint32_t period; Component每帧处理的Periods。注:以sample点为单位。

8.ele_attr_t attr; Component的属性

/**
 * element attribute enum
 * which shows element belong to downlink or uplink
 * and shows whether has thread
 */
typedef enum {
        ELE_ATTR_DL = BIT(0),
        ELE_ATTR_UL = BIT(1),
        ELE_ATTR_THREAD = BIT(2),
        ELE_ATTR_IL = BIT(5),   /* interleaved */
        ELE_ATTR_NIL = BIT(6),  /* noninterleaved */
} ele_attr_t;

9.uint32_t muid; Module Unique ID,通过 GEN_MOD_UID 宏定义生成。

10.trigger_source_t trigger; 表明当前的Component可哪个trigger触发。

typedef enum trigger_source {
        TRIGGER_I2S0_DMA_TX,
        TRIGGER_I2S0_DMA_RX,
        TRIGGER_I2S1_DMA_TX,
        TRIGGER_I2S1_DMA_RX,

        TRIGGER_APP_START,
        TRIGGER_APP_WRITE = TRIGGER_APP_START,

        TRIGGER_SOURCE_BUFFER, /* Use source block ringbuffer to trigger process*/
} trigger_source_t;

10.data_port_cfg_t in_port;

11.data_port_cfg_t out_port; Component的输入/输出端口,使用辅助定义:

DEF_NONE_PORT       // 没有端口
DEF_SINGLE_PORT(2)  // 定义一个使用2个连续channel的端口

// 定义多个端口,每个端口使用 PORT(index, offset, cnt) 宏定义进行描述,连续的通道数
// PORT的第一个参数是port id,第二个是起始通道,第三个是通道数。
// (0,0,2)代表id为1的输出,会输出从0开始计数的两个通道(数组从0开始计数)。即第一、第二通道。
DEF_MULTI_PORTS(
            PORT(0, 0, 2),
            PORT(1, 2, 4)
        )
// PORT_MASK定义离散的通道数,如71 = 1000111  代表输出1237四个通道
DEF_MULTI_PORTS(
			PORT_MASK(0, 0, 71),
			PORT_MASK(1, 0, 71),
			PORT_MASK(2, 0, 71)
		)

具体示例

录音原始音频pipeline0为例。根据拓扑图,pipeline如下所示:

static link_param_t capture_links[] = {
	{
		.source_id = CUID_I2S_RX,
		.sink_id = CUID_CAPTURE_DEVICE_SPLITTER,
		.conn_param = {
			.conn_attr = CONN_ATTR_BASE,
			.default_status = CONN_STATUS_LINKED | CONN_STATUS_SWITCH_ON,
			.source_port_id = PORT_ID(0),
			.sink_port_id = PORT_ID(0),
		},
	},
	{
		.source_id = CUID_CAPTURE_DEVICE_SPLITTER,
		.sink_id = CUID_FE_NORMAL_RECORD,
		.conn_param = {
			.conn_attr = CONN_ATTR_RB,
			.default_status = CONN_STATUS_LINKED,
			.source_port_id = PORT_ID(1), // 从源的port1输入
			.sink_port_id = PORT_ID(0), // 送入
		},
	},
};

涉及Component如下所示:

	{
		.cuid = CUID_I2S_RX,
		.name = "i2s-rx",
		.zone_id = 1,
		.rate = 16000,
		.channel = 8, // 1100 1111 从低到高,四路mic 2路ref,当前音频板为8通道
		.bitdepth = PLATFORM_BIT_DEPTH,
		.period = 256,
		.attr = ELE_ATTR_UL | ELE_ATTR_IL | ELE_ATTR_THREAD,
		.muid = MUID_I2S_RX,
		.trigger = TRIGGER_I2S1_DMA_RX,
		.in_port = DEF_NONE_PORT,
		.out_port = DEF_SINGLE_PORT(8),
	},
	{
		.cuid = CUID_CAPTURE_DEVICE_SPLITTER,
		.name = "cp_splitter",
		.zone_id = 1,
		.rate = 16000,
		.channel = 8,
		.bitdepth = PLATFORM_BIT_DEPTH,
		.period = 256,
		.attr = ELE_ATTR_UL | ELE_ATTR_NIL,
		.muid = MUID_SPLITTER_2,
		.trigger = TRIGGER_SOURCE_BUFFER,
		.in_port = DEF_SINGLE_PORT(8),
		.out_port = DEF_MULTI_PORTS(
			PORT_MASK(0, 0, 71), // 0100 0111 3mic + 1ref port0
			PORT_MASK(1, 0, 79), // 0100 1111 4mic + 1ref port1
			PORT_MASK(2, 0, 71) // 暂无使用
		),
	},
	{
		.cuid = CUID_FE_NORMAL_RECORD,
		.name = "Normal_Record",
		.zone_id = 1,
		.rate = 16000,
		.channel = 5, // 5路音频
		.bitdepth = PLATFORM_BIT_DEPTH,
		.period = 256,
		.attr = ELE_ATTR_UL | ELE_ATTR_IL,
		.muid = MUID_FE_UL_1,
		.trigger = TRIGGER_I2S1_DMA_RX,
		.in_port = DEF_SINGLE_PORT(5), // 5路音频
		.out_port = DEF_NONE_PORT,
	}

算法移植指南

中间件集成低功耗正常功耗地瓜智能语音前端(HISF)算法。通过两个module将算法分别集成,并接入中间件路由通路。

编写module

在SGF框架中,所有数据处理模块需要实现Module的接口,才能配合框架使用。因此算法移植第一步需要依照Module接口说明,实现数据处理module。

static struct module_desc mod_hisf_lp = {
        .mtid = MTID_HISF_LP,
        .name = "HISF_LP",
        .ops = {
                .cmd = hisf_cmd,
                .process = hisf_process,
                .stack_size = hisf_get_stackreq,
                .create = hisf_create,
        },
};

common_module_register(mod_hisf_lp);

主要实现module的 cmd/process 接口,其他接口根据module说明实现。

module放在 adsp\ssf\sgf\plugin\src目录下。 算法自身实现放在adsp\ssf\algo目录下。

定义MTID (Module Type ID)

MTID是用来区分不同Module的标识符,使用7bit来表示(0 ~ 127)。 MTID定义位置放在adsp\ssf\sgf\conf\include\config_common.h内,也自行存放,需要保证config和module看到的ID统一。

#define MTID_HISF_LP     (0x45)
#define MTID_HISF_NORMAL (0x46)

定义的MTID填充到Module Description结构体中。

添加文件到Makefile

将module以及相关文件,添加到DSP SSF的Makefile中。 SSF Makefile位于adsp\ssf\Makefile:

MOD_LIB_SRC += mod_hisf_lp.c

定义CUID/MUID

算法移植后,需要放到Component中,并配置到Graph中才调用。 在 adsp\ssf\sgf\conf\include\config_ar_x5.h定义:

#define CUID_HISF_LP       GEN_COMP_UID(CTID_BASE, MTID_HISF_LP, 0x1)
#define CUID_HISF_NORMAL   GEN_COMP_UID(CTID_BASE, MTID_HISF_NORMAL, 0x1)

#define MUID_HISF_LP       GEN_MOD_UID(MTID_HISF_LP, 0x1)
#define MUID_HISF_NORMAL   GEN_MOD_UID(MTID_HISF_NORMAL, 0x1)

配置Graph

路由配置章节。

A核与DSP数据通信

四路alsa pcm device介绍:

  1. device0:SMF FE raw —— mic原始音频,包含回采。

  2. device1:SMF FE ASR —— 一级唤醒的ASR音频,连续的,有1秒的buffer。

  3. device2:SMF FE history ——一级唤醒,4秒唤醒音频,有四秒缓冲。

  4. device4:SMF FE hisf normal —— 正常功耗预处理音频,包含唤醒sdk的enhanced音频送vad sdk的asr音频。

调试指南

Log

基本log打印,log level可adsp/ssf/sysdeps/xos/include/sys_log_cfg.h设置。 修改SYS_LOG_OUTPUT_LVL即可。

#define SYS_LOG_LVL_ASSERT 0
#define SYS_LOG_LVL_ERROR 1
#define SYS_LOG_LVL_WARN 2
#define SYS_LOG_LVL_INFO 3
#define SYS_LOG_LVL_DEBUG 4
#define SYS_LOG_LVL_VERBOSE 5
#define SYS_LOG_LVL_TOTAL_NUM 6

Backtrace

xt-addr2line 工具。 xt-addr2line 是一个用于地址转换源代码位置命令行工具。通常情况下,它用于分析程序崩溃核心转储文件堆栈跟踪信息,以便找到崩溃位置对应源代码文件名。这软件调试故障排除非常有用。

adsp目录使用source env_hf5.sh即可使用xt-addr2line工具分析代码。

例如出现以下报错时:

trace

可以使用:

xt-addr2line -e output/adsp -fpiC [地址]

获取报错堆栈信息。