OCAP XC4210侧代码结构理解与分析
以下为阅读 OCAP 相关源码或修改相关代码过程中的一些理解和心得
结构体及变量
消息结构体
消息头
typedef struct osa_msg_tag
{
/* 第 1 段:消息头 */
osa_prim_id_t prim_id; /* 消息 ID */
osa_sap_id_t sap_id; /* 用于过滤 */
UINT16 u16_src_module_id; /* 目标模块 ID */
UINT16 u16_dest_module_id; /* 源模块 ID */
UINT16 u16_msg_type; /* 用于区分消息类型,确定内存的组成方式 */
BOOLEAN b_mbuf_used ; /* 指示 PEER 部分是否使用 MBUF,见内存管理*/
UINT32 u32_fn; /* 无线帧号 */
UINT16 u16_offset ; /* 无线帧内偏移时间 */
UINT16 u16_waveid; /* 波形 ID */
UINT32 u32_timestamp; /* 系统时间戳 */
osa_msg_para_t st_msg_para; /* The parameter structure */
osa_msg_peer_t st_msg_peer; /* 消息体*/
/* 第 1 段:消息头 */
} osa_msg_t;
消息参数
typedef struct osa_msg_para_tag
{
UINT16 u16_para_len;
OSA_FILLER2
VOID_PTR vp_para;
} osa_msg_para_t;
消息体
typedef struct osa_msg_peer_tag
{
UINT16 u16_peer_len;
UINT16 u16_free_header_space_len;
VOID_PTR vp_peer;
} osa_msg_peer_t;
消息参数和消息体相关宏
相关宏定义定义于 osa_pub.h 文件中
取出消息长度: #define FSM_PARAM_SIZE(stp_msg) ((stp_msg)->st_msg_para.u16_para_len)
取出具体消息参数: #define FSM_PARAM_PTR(stp_msg) ((stp_msg)->st_msg_para.vp_para)
取出消息ID: #define FSM_PRIMITIVE_ID(stp_msg) ((stp_msg)->prim_id)
用例结构体
存放在 demo_xc4210_comm.h
typedef struct demo_case_tag
{
char type_name[20]; /* */
UINT16 u16_core_id; /*核号 */
UINT16 u16_type_id; /*用例号 */
UINT16 u16_para_num; /* 用例需要的参数个数 */
UINT32 u32_parameter[32];
} demo_case_t;
用例状态结构体
定义在 demo_common.h 中
enum类型,typedef 将枚举类型定义成别名,利用该别名进行变量声明
typedef enum TEST_CASE_RESULT {
TEST_CASE_RESULT_NOT_RUN,
TEST_CASE_RESULT_UNKNOWN,
TEST_CASE_RESULT_PASS,
TEST_CASE_RESULT_FAIL,
} TEST_CASE_RESULT;
用例状态变量命名为 g_self_test_result,类型为 TEST_CASE_RESULT
存放在DDR中,申明如下:UCASE_DRAM_ZERO_INIT_DATA_SECTION TEST_CASE_RESULT g_self_test_result;
重要的全局变量
以下变量都定义在 demo_common.c 中
g_self_test_begin_num: g_self_test_begin_num 存放开始的用例个数,只要运行,无论是否通过都计入统计
存放在DDR中,申明如下:UCASE_DRAM_ZERO_INIT_DATA_SECTION UINT32 g_self_test_begin_num;
g_self_test_pass_num: g_self_test_pass_num 存放通过的用例数,只有用例状态为TEST_CASE_RESULT_PASS时,该值才会自加
存放在DDR中,申明如下:UCASE_DRAM_ZERO_INIT_DATA_SECTION UINT32 g_self_test_pass_num;
g_self_test_case_name: g_self_test_case_name 存放用例名,比如 ‘testcase_vstdw_v32’ 就是一个用例名,一般认为命名长度不会超过100
存放在DDR中,申明如下:UCASE_DRAM_ZERO_INIT_DATA_SECTION CHAR g_self_test_case_name[100];
用例名如何传递:通过宏定义中的 #x 传递给函数中的局部变量 ‘name’,再通过 strcpy 函数传递给 g_self_test_case_name
用例相关函数
testcase_pass
定义在 demo_common.c
测试用例通过时调用。如果是TEST_CASE_RESULT_UNKNOWN状态,则变为TEST_CASE_RESULT_PASS
void testcase_pass(void)
{
if(g_self_test_result == TEST_CASE_RESULT_UNKNOWN)
{
g_self_test_result = TEST_CASE_RESULT_PASS;
}
}
testcase_fail
定义在 demo_common.h
该函数不直接定义,而是通过宏定义定义,其等价与函数testcase_fail_impl
#define testcase_fail() testcase_fail_impl(__FILE__, __LINE__)
testcase_fail_impl
定义在 demo_common.c
由写法来看可以用来定位出问题的文件名和行号
测试用例失败时调用。如果调用该函数,该用例失败,忽略self_test_pass(testcase_pass)的调用
void testcase_fail_impl(char const* file, int line)
{
g_self_test_result = TEST_CASE_RESULT_FAIL;
OSA_PRINT(OSA_PRINT_INFO,
"test case %d FAIL %d: %s, line %d, file: %s",
g_self_test_begin_num,
g_self_test_begin_num - g_self_test_pass_num,
g_self_test_case_name,
line,
file);
}
testcase_begin
定义在 demo_common.c
每个用例的开始执行该函数,test_result被置为TEST_CASE_RESULT_UNKNOWN状态。
若运行函数时发现状态不为TEST_CASE_RESULT_NOT_RUN,则说明上一个用例未跑完
void testcase_begin(CHAR CONST* name) //测试用例开始时调用。
{
if(g_self_test_result != TEST_CASE_RESULT_NOT_RUN)
{
OSA_PRINT(OSA_PRINT_INFO,
"test case %d not end: %s, before run %s",
g_self_test_begin_num,
g_self_test_case_name,
name);
++g_self_test_begin_num; //add one fail case.
}
g_self_test_result = TEST_CASE_RESULT_UNKNOWN;
++g_self_test_begin_num;
strcpy(g_self_test_case_name, name);
OSA_PRINT(OSA_PRINT_INFO,
"test case %d begin: %s",
g_self_test_begin_num,
g_self_test_case_name);
}
testcase_end
定义在 demo_common.c
用例运行完执行该函数,此时检测test_result的状态,仅为TEST_CASE_RESULT_PASS表示通过了用例
void testcase_end(char const* file, int line)
{
//如果不调用self_test_pass,直接作为失败。
if(g_self_test_result == TEST_CASE_RESULT_NOT_RUN)
{
++g_self_test_begin_num; //add one fail case.
OSA_PRINT(OSA_PRINT_INFO,
"testcase_end error: test case is not running, %s, line %d",
file,
line);
}
else if(g_self_test_result == TEST_CASE_RESULT_PASS)
{
++g_self_test_pass_num;
OSA_PRINT(OSA_PRINT_INFO,
"test case %d PASS: %s",
g_self_test_begin_num,
g_self_test_case_name);
}
else if(g_self_test_result == TEST_CASE_RESULT_UNKNOWN)
{
OSA_PRINT(OSA_PRINT_INFO,
"test case %d result UNKNOWN: %s",
g_self_test_begin_num,
g_self_test_case_name);
}
else
{
OSA_PRINT(OSA_PRINT_INFO,
"test case %d FAIL num = %d: %s",
g_self_test_begin_num,
g_self_test_begin_num - g_self_test_pass_num,
g_self_test_case_name);
}
g_self_test_result = TEST_CASE_RESULT_NOT_RUN;
}
testcase_ver
定义在 xc4210_self_test.c
打印显示当前时间,分别于例测试开始前和结束后调用
void testcase_ver(void)
{
OSA_PRINT(OSA_PRINT_INFO, "%s, %s", __DATE__, __TIME__);
}
self_test_callback
该函数结构非常简单,就是一个一个调用单个用例的函数。同时在整个函数的开始时和结束后分别打印当前时间
定义在 xc4210_self_test.c
void self_test_callback(void)
{
testcase_ver();
TEST_CASE(testcase_vstdw_v32);
TEST_CASE(testcase_vstdw_v32_offset);
TEST_CASE(testcase_vstdw_v32_offset_2);
TEST_CASE(testcase_vadd32_v32_v32_v32);
testcase_ver();
}
单个用例运行过程
定义在 demo_common.h
此处以宏定义方式展开
tips:此处 do while(0)用来规避编译错误,无实际意义
#define TEST_CASE(X) \
do \
{ \
testcase_begin(#X); \
X(); \
testcase_end(__FILE__, __LINE__); \
} while(0)
测试用例开始前调用 testcase_begin,测试结束调用 testcase_end
X()会被替换成具体的测试函数进行调用
4210算法库(demo = 0.3001.0.0)验证入口
定义在 demo_common.c
在 OCAP Demo Utility 的指令框中输入命令“demo=0,3001,0,0” 即可进入ucase_algo_start_self_test_req函数,它主要有如下几个功能:
- 它对测试用例的状态,一些全局变量进行了初始定义
- 通过self_test_callback执行XC4210上的公共函数demo
- 在执行完相关算法函数后对整个运行过程进行统计,包括测试个数,通过个数,失败个数
变量具体说明可见相关章节
void ucase_algo_start_self_test_req(void)
{
OSA_PRINT(OSA_PRINT_INFO, "self test begin...");
g_self_test_begin_num = 0;
g_self_test_pass_num = 0;
g_self_test_result = TEST_CASE_RESULT_NOT_RUN;
self_test_callback();
if(g_self_test_result != TEST_CASE_RESULT_NOT_RUN)
{
OSA_PRINT(OSA_PRINT_INFO,
"test case %d not end: %s",
g_self_test_begin_num,
g_self_test_case_name);
++g_self_test_begin_num; //add one fail case.
}
OSA_PRINT(OSA_PRINT_INFO, "self test end");
OSA_PRINT(OSA_PRINT_INFO, "self test case num = %d", g_self_test_begin_num);
OSA_PRINT(OSA_PRINT_INFO, "self test case PASS num = %d", g_self_test_pass_num);
OSA_PRINT(OSA_PRINT_INFO,
"self test case FAIL num = %d",
g_self_test_begin_num - g_self_test_pass_num);
}
核间通信函数
demo_intercore_msg_send
定义在 demo_main_xc4210.c 中: WAVEFORM_CODE_DRAM_SECTION OSA_STATUS demo_intercore_msg_send(osa_msg_t *stp_msg,UINT32 u32_msg_id,UINT16 u16_core_id);
phy_intercore_msg_send
定义在 demo_main_xc4210.c 中
这几个函数没有完全看懂,需要弄懂 OSA 接口的消息管理机制和相关的函数
4210算法功能分支函数
根据输入的用例号选择相应的分支进行测试,定义在 xc4210_self_test.c
其中 fsm_msg_ptr 类型为 osa_msg_t,为消息结构体。
u16_demo_cmd 为用例号,是一个 16 位无符号整型变量
ALGO_DEMO_TEST_START_XC4210_SELF_TEST 对应的值为3001,它定义在 demo_common.h 中
整个函数运行逻辑如下:用户输入 demo=0.3001.0.0 用例后,最终会进入该函数的分支,执行 ucase_algo_start_self_test_req 函数。
执行完 xc4210 后通过核间通信回复 x1643
VOID ucase_demo_module_algo_branch(osa_msg_t *fsm_msg_ptr,UINT16 u16_demo_cmd)
{
switch(u16_demo_cmd)
{
case ALGO_DEMO_TEST_START_XC4210_SELF_TEST:
{
OSA_PRINT(OSA_PRINT_WARNING, "XC4210_TASK0: u16_demo_cmd = %d", u16_demo_cmd);
ucase_algo_start_self_test_req();
demo_intercore_msg_send(fsm_msg_ptr, MSG_XC4210_2_X1643_PARAM_CNF, DEMO_CORE_ID_X1643);
break;
}
default:
{
break;
}
}
}
4210平台功能分支函数
定义在 demo_osa.c 下:
ucase_xc4210_demo_module_osa_branch
其大体与 ucase_demo_module_algo_branch
类似,不过多赘述。
死机dump
用例号: OSA_DEMO_DUMP_COMMAND_XC4210 = 2001
触发 OSA_ASSERT
函数,此处直接填入参数 OSA_FALSE
触发死机dump
XC4210 创建线程
用例号:OSA_DEMO_CREATE_TASK_XC4210 = 2003
该函数定义在 demo_osa.c 中
VOID osa_xc4210_demo_create_task_test(VOID)
{
osa_msg_t * test_msg_ptr = NULL_PTR;
g_demo_xc4210_queue_id = osa_msg_queue_create(10);
/*allocate stack memory for task i*/
g_demo_xc4210_task_id = osa_task_create("TASK2",
0x23,
0,
NULL_PTR,
(osa_task_main_t)demo_xc4210_task_entry,
NULL_PTR,
DEMO_TASK_SPOOL_ID_1,
2048);
test_msg_ptr = OSA_MSG_CREATE(DEMO_MSG_POOL_ID, 2);
FSM_PRIMITIVE_ID(test_msg_ptr) = 0x76543210;
OSA_MSG_SEND(g_demo_xc4210_queue_id, test_msg_ptr, OSA_WAIT_FOREVER);
return;
}
此函数中线程扩展参数,线程初始化函数,线程删除函数都为空
线程入口函数为 demo_xc4210_task_entry
static VOID demo_xc4210_task_entry(VOID)
{
osa_msg_t *fsm_msg_ptr;
osa_prim_id_t prim_id[2] = {1, OSA_MSG_ANY_ID};
UINT32 u32_msg_id;
OSA_PRINT(OSA_PRINT_WARNING, "XC4210_DEMO create_task_entry ");
while (1)
{
OSA_PRINT(OSA_PRINT_INFO, "XC4210_DEMO _queue_id:%x ", g_demo_xc4210_queue_id);
fsm_msg_ptr = osa_msg_receive(g_demo_xc4210_queue_id, prim_id, OSA_WAIT_FOREVER);
if (NULL_PTR != fsm_msg_ptr)
{
u32_msg_id = FSM_PRIMITIVE_ID(fsm_msg_ptr);
OSA_PRINT(OSA_PRINT_WARNING, "XC4210_DEMO receive msg id: 0x%x",u32_msg_id);
}
OSA_MSG_RELEASE(fsm_msg_ptr);
}
return;
}
PROFILE 功能
用例号:OSA_DEMO_PROFILER_FUNCTION_TEST = 2007
该函数定义在 demo_osa.c 中
需要在 TT 中提前打开 PROFILE 开关,点击连接 -> Cp Debug
此用例依次执行以下功能:
- 调用函数
osa_dbg_profiler_start
打开 PROFILE 功能 - 调用函数
osa_dbg_profiler_end
关闭 PROFILE 功能 - 调用函数
osa_dbg_profiler_register_save
将信息从寄存器中导出到记录 BUFFER 中 - 调用函数
osa_dbg_profiler_flow_trace
将记录 BUFFER 中的数据通过 TT 工具导出
VOID demo_xc4210_profiler_statistic_function(VOID)
{
UINT32 u32_profiler_test_data = 0;
UINT32 u32_idx = 0;
g_u32_xc4210_profiler_demo_cnt++;
osa_dbg_profiler_start();
OSA_PRINT(OSA_PRINT_WARNING, "osa_xc4210_dbg_profiler_start");
for(u32_idx = 0; u32_idx < 100; u32_idx++)
{
u32_profiler_test_data++;
}
osa_dbg_profiler_end();
OSA_PRINT(OSA_PRINT_WARNING, "osa_xc4210_dbg_profiler_end");
osa_dbg_profiler_register_save(1);
OSA_PRINT(OSA_PRINT_WARNING, "osa_xc4210_dbg_profiler_register_save");
osa_dbg_profiler_flow_trace(64);
}
用例分支选择函数
fw_test_case_main
函数定义在 demo_test_case_xc4210.c
其中 stp_msb_body 为消息体中的具体参数,涉及的结构体或参数有:osa_msg_t->osa_msg_para_t->(demo_case_t*)vp_para
u16_demo_cmd 为用例号,具体可详见 demo_case_t 结构体
根据用例号的数值进行分支选择:如果用例号为 2000-3000,则进入4210的平台功能分支函数。如果用例号为 3000-4000,则进入4210的算法功能分支函数。
OSA_STATUS fw_test_case_main(osa_msg_t *stp_msg)
{
demo_case_t *stp_msb_body;
UINT16 u16_demo_cmd;
UINT16 u16_mod_id;
stp_msb_body = (demo_case_t*)FSM_PARAM_PTR(stp_msg);
u16_demo_cmd = (stp_msb_body->u16_type_id ) ;/*demo_cmd_id*/
OSA_PRINT(OSA_PRINT_WARNING, "XC4210: receive MSG: MSG_A72DSP_PARAM_REQ, u16_demo_cmd = %d" , u16_demo_cmd);
if((2000 <= u16_demo_cmd) && (u16_demo_cmd < 3000))
{
OSA_PRINT(OSA_PRINT_WARNING,"DEMO PARA to MODULE: OSA");
ucase_xc4210_demo_module_osa_branch(stp_msg,u16_demo_cmd);
}
else if((3000 <= u16_demo_cmd) && (u16_demo_cmd < 4000))
{
OSA_PRINT(OSA_PRINT_WARNING,"DEMO PARA to MODULE: ALGO");
ucase_demo_module_algo_branch(stp_msg,u16_demo_cmd);
else
{
OSA_PRINT(OSA_PRINT_WARNING, "the AT command is wrong !");
OSA_ASSERT(OSA_FALSE);
}
return OSA_SUCCESS;
}
demo_xc4210_case_req_handler
定义在 demo_xc4210_task.c
由代码来看,感觉就是对fw_test_case_main
进行了进一步的封装
OSA_STATUS demo_xc4210_case_req_handler(osa_msg_t *stp_msg)
{
UINT16 u16_core_id;
u16_core_id = ((demo_case_t*)FSM_PARAM_PTR(stp_msg))->u16_core_id;
//demo_print(0,"u16_core_id_xc4210=0x%lx",u16_core_id);
fw_test_case_main(stp_msg);
return OSA_SUCCESS;
}
xc4210任务主函数
demo_xc4210_task_main
定义在 demo_xc4210_task.c
搜索消息ID,如果有匹配的,进入相应的分支
其中 1643 发给 4210 的消息 ID 为 MSG_X1643_2_XC4210_PARAM_REQ
4210接收任务消息函数
定义在 demo_xc4210_task.c
任务消息的接收是在一个死循环中进行,如下所示:
st_wait_msg_list 为接收指定消息ID的数组,其数据结构为 {消息个数,消息ID0,···} 消息 ID 个数由结构的第一个变量消息个数决定。
OSA_TASK_SAFE_SET
在 osa_pub.h 中被宏定义: #define OSA_TASK_SAFE_SET() osa_task_safe()
OSA_TASK_UNSAFE_SET
在 osa_pub.h 中被宏定义: #define OSA_TASK_UNSAFE_SET() osa_task_unsafe()
OSA_MSG_RELEASE
在 osa_pub.h 中被宏定义:
#define OSA_MSG_RELEASE(stp_msg) \
osa_msg_release(stp_msg )
OSA_STATUS demo_xc4210_diag_task(VOID)
{
/* Variable */
osa_msg_t *stp_msg;/* message pointer */
/* Msg num , Msg ID */
osa_prim_id_t CONST st_wait_msg_list[2] = {(UINT32)1, (UINT32)OSA_MSG_ANY_ID};/* parameter for receive message */
/* The task will not end after setup */
while (1)
{
/* Set flag to inform task stack is safe to be released
when dynamic task stack is enabled*/
OSA_TASK_SAFE_SET();
/* Wait for one message forever */
stp_msg = osa_msg_receive(DEMO_XC4210_DIAG_TASK_MSG_Q, st_wait_msg_list, (UINT16)OSA_WAIT_FOREVER);
/* Set flag to inform task stack can not be released
when dynamic task stack is enabled*/
OSA_TASK_UNSAFE_SET();
/* message driven task */
if (NULL_PTR != stp_msg)
{
demo_xc4210_diag_task_main(stp_msg);
/* Release the received message */
OSA_MSG_RELEASE(stp_msg);
}
}
return OSA_SUCCESS;
}
波形入口函数
wf_init
定义在 demo_main_xc4210.c 中
wf_init
函数在哪被调用的呢:
答:在 demo_init.asm 中:
.CSECT CODE_WF_A_INIT_FUNC_TBL
__demo_xc4210_init:
.func_start 2 __demo_xc4210_init
brr{t} _wf_init
.func_end 2 __demo_xc4210_init
.CSECT CODE_WF_A_EXIT_FUNC_TBL
__demo_xc4210_exit:
.func_start 2 __demo_xc4210_exit
brr{t} 0
.func_end 2 __demo_xc4210_exit
平台相关函数
OSA接口函数
osa_msg_queue_create
UINT16 osa_msg_queue_create(IN CONST_UINT16 u16_mb_size)
消息在发送之前,要创建消息队列,用于接收其他线程发送过来的消息。
成功则返回消息队列 ID 号,失败则返回 0xFFFF
u16_mb_size:申请的 mb 块大小,单位 byte
osa_msg_release
用于释放消息
VOID osa_msg_release(IN osa_msg_t *stp_msg)
stp_msg 为消息首地址
osa_msg_create
创建仅含有 PARAM 部分的整体式消息
u16_pool_id:消息使用的内存池 ID u16_msg_len:申请消息的长度
osa_msg_t *osa_msg_create(IN CONST_UINT16 u16_pool_id,
IN CONST_UINT16 u16_msg_len)
osa_msg_receive
获取指定消息队列的消息
如果接收成功,返回消息首地址。如果超时返回 NULL_PTR
osa_msg_t *osa_msg_receive(IN CONST_UINT16 u16_queue_id,
IN CONST osa_prim_id_t *stp_prim_id,
IN CONST_UINT32 u32_timeout)
osa_task_create
用于创建一个线程
UINT16 osa_task_create(CONST_CHAR_PTR_CONST p_task_name,
CONST_UINT16 u16_priority,
CONST_UINT32 u32_task_param,
CONST osa_task_init_t fp_task_init,
CONST osa_task_main_t fp_task_main,
CONST osa_task_delete_t fp_task_delete,
CONST_UINT16 u16_stack_pool_id,
CONST_UINT16 u16_stack_size)
参数如下:
成功则返回线程 ID,失败返回 0xFFFF
osa_task_unsafe
可设置当前线程进入不可动态删除/创建的状态
osa_task_safe
设置当前线程进入可动态删除/创建的状态
Framework 接口
调试功能
Log
osa_print:
将内容输出到调试工具(TT)上
VOID osa_print(IN CONST_UINT16 u16_level,
IN CONST_CHAR_PTR u8p_format,
IN ...)
Debug
OSA_ASSERT:
VOID OSA_ASSERT(BOOLEAN expr)
提供检查程序是否正常运行的功能,可在调试程序时使用。
expr:条件表达式,可以填入TRUE 或 FALSE
osa_dbg_profiler_start:
用于启动 profiler,同时对寄存器中残留的结果进行清 0,并启动 profiler 开始计数
VOID osa_dbg_profiler_start (VOID)
osa_dbg_profiler_end:
用于停止 profiler 计数
VOID osa_dbg_profiler_end (VOID)
osa_dbg_profiler_register_save:
用于读取 profiler 寄存器中计数结果,并保存到全局变量中
u32_process_id 为本次记录保存的 process_id
VOID osa_dbg_profiler_register_save(UINT32 u32_process_id)
osa_dbg_profiler_flow_trace:
统计 u16_profiler_cnt 次后输出 profiler 统计的消息,并达到后实时输出
u16_profiler_cnt:需要统计几次结果后输出消息
此函数感觉有问题,使用后发现和描述存在较大差异。
VOID osa_dbg_profiler_flow_trace(CONST_UINT16 u16_profiler_cnt)
文档信息
- 本文作者:Ziyue Qi
- 本文链接:https://www.qiziyue.cn/2023/02/04/demo%E7%94%A8%E4%BE%8B/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)