OCAP平台XC4210侧demo用例学习

2023/02/04 自组网 OCAP 通信 共 13202 字,约 38 分钟

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函数,它主要有如下几个功能:

  1. 它对测试用例的状态,一些全局变量进行了初始定义
  2. 通过self_test_callback执行XC4210上的公共函数demo
  3. 在执行完相关算法函数后对整个运行过程进行统计,包括测试个数,通过个数,失败个数

变量具体说明可见相关章节

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

此用例依次执行以下功能:

  1. 调用函数 osa_dbg_profiler_start 打开 PROFILE 功能
  2. 调用函数 osa_dbg_profiler_end 关闭 PROFILE 功能
  3. 调用函数 osa_dbg_profiler_register_save 将信息从寄存器中导出到记录 BUFFER 中
  4. 调用函数 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)

图 5
图 6

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)

参数如下: 图 7

成功则返回线程 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

图 8

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)

文档信息

Search

    Table of Contents