DSP

基站诊断工具——DSP开发小结

2019-07-13 16:21发布

本篇日志记录了我在爱立信实习中参与的一个项目:基站监测与调试工具的开发。 WoD工具分为两部分,一部分是在DSP侧,实现在基站freescale的板子上,提供基站端的流量信息/数据信息/signal状态/内存信息等;另一部分是PC侧,负责监视。控制基站与显示,记录数据结果。两部分之间通过socket通信,在自己设计的数据包传输协议基础上交换信息。(在外场测试中,没有PC侧的应用,DSP侧应将各种信息或数据记录在文件中存储,需要时用脚本解析)。 相关代码主要为om_message.c 在DSP0的app.c中,appstart()调用om_init(),这个函数创建了几个低优先级的进程(为了保证基站端物理层处理的实时性,WoD相关的进程优先级都会设为最低的):
  void om_init(void) { om_pool_id = s_create_pool(0, (OSADDRESS)&om_pool_buf[krn_get_core_id()][0], OM_POOL_BUF_SIZE, omPoolSizes); pid_wod_send = create_process(OS_PRI_PROC, "wodSendProc", wodSendProc, (OSADDRESS)4096, WOD_SEND_PRIORITY, 0, 0, 0, 0, 0); start(pid_wod_send); pid_wod_recv = create_process(OS_PRI_PROC, "wodRecvProc", wodRecvProc, (OSADDRESS)4096, WOD_RECV_PRIORITY, 0, 0, 0, 0, 0); start(pid_wod_recv); pid_log_to_file = create_process(OS_PRI_PROC,"logToFileProc",logToFileProc, (OSADDRESS)4096, WOD_LOG_TO_FILE_PRIORITY, 0,0,0,0,0); start(pid_log_to_file); om_debug_log_init(); }   下面以通信最基本的send和recv操作来记录进程键通信的用法: 简洁起见,以记录小数据和天线数据(大量)到文件这两个功能为例,涉及到的操作有文件的建立,关闭和写入。 send进程等待信号并判断sig的action,根据action的类别来封装message各个字段内容。除了包头(dspID,coreID,fileID,packetID等)外,payload通过 memcpy(p + 9, sig->send_req.sendbuf, sig->send_req.sendlength);
来具体赋值,及sig中传入了sendbuf中有效信息,如文件名,天线数据等。PS:天线数据因为过大,是从bufpointer开始copy过来的。之后通过socket发送   err = ipcom_sendto(sockcli, sendbuf, sendsize, 0, (struct Ip_sockaddr *)&serveraddr, sizeof(serveraddr)); free_buf(&sig); OS_PROCESS(wodSendProc) { S32 err; union SIGNAL *sig; static SIGSELECT sigsel[] = {1,SIG_SEND_REQ}; U32 * p; struct msgHead* msg_p; U8 sendbuf[MAX_WOD_SEND_LENGTH]; #pragma align sendbuf 8 U32 sendsize; Ip_fd_set readset; Ip_fd sockcli; sockcli = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0); IP_FD_ZERO(&readset); IP_FD_SET(sockcli, &readset); for(;;) { sig = receive(sigsel); switch(sig->sig_no) { case SIG_SEND_REQ: switch(sig->send_req.Action) { case ATL_CMD_READ_MEMORY_RSP: msg_p = (struct msgHead*)&sendbuf[0]; p = (U32 *)&sendbuf[0]; msg_p->FLIType = om_swap32(pfm_get_dsp_id()); // device id msg_p->FLIMode = om_swap32(krn_get_core_id()); msg_p->Action = om_swap32(sig->send_req.Action); msg_p->Size = om_swap32(9 + 1 + sig->send_req.sendlength/sizeof(U32) - 2); *(p+9+sig->send_req.sendlength/sizeof(U32)) = om_swap32(END_TOKEN); memcpy(p+9, sig->send_req.sendbuf, sig->send_req.sendlength); sendsize = sig->send_req.sendlength + sizeof(struct msgHead) + sizeof(U32); break; case ATL_CMD_FILE_OPEN: case ATL_CMD_FILE_CLOSE: msg_p = (struct msgHead*)&sendbuf[0]; p = (U32 *)&sendbuf[0]; msg_p->FLIType = om_swap32(pfm_get_dsp_id()); // device id msg_p->FLIMode = om_swap32(krn_get_core_id()); msg_p->Action = om_swap32(sig->send_req.Action); msg_p->FLIdx = om_swap16(sig->send_req.fileIdx); msg_p->Size = om_swap32(9 + 1 + sig->send_req.sendlength/sizeof(U32) -2 ); *(p+9+sig->send_req.sendlength/sizeof(U32)) = om_swap32(END_TOKEN); memcpy(p + 9, sig->send_req.sendbuf, sig->send_req.sendlength); sendsize = sig->send_req.sendlength + sizeof(struct msgHead) + sizeof(U32); break; case ATL_CMD_DATA_WRITE: msg_p = (struct msgHead*)&sendbuf[0]; p = (U32 *)&sendbuf[0]; msg_p->FLIType = om_swap32(pfm_get_dsp_id()); // device id msg_p->FLIMode = om_swap32(krn_get_core_id()); msg_p->Action = om_swap32(sig->send_req.Action); msg_p->FLIdx = om_swap16(sig->send_req.fileIdx); msg_p->PacketIdx = om_swap16(sig->send_req.packetIdx); msg_p->Size = om_swap32(9 + 1 + sig->send_req.sendlength/sizeof(U32) -2 ); *(p+9+sig->send_req.sendlength/sizeof(U32)) = om_swap32(END_TOKEN); memcpy(p+9, sig->send_req.sendbufpointer, sig->send_req.sendlength); sendsize = sig->send_req.sendlength + sizeof(struct msgHead) + sizeof(U32); break; default: break; } break; default: break; } err = ipcom_sendto(sockcli, sendbuf, sendsize, 0, (struct Ip_sockaddr *)&serveraddr, sizeof(serveraddr)); free_buf(&sig); } ipcom_socketclose(sockcli); }
  recv进程从socket读出请求(recvfrom),然后根据包头中的action字段实施具体操作,如果请求的是从内存中绝对地址读出N个字节的数据,
1.动态分配一个sigsigsend = s_alloc(om_pool_id,sizeof(struct SigSendReq), SIG_SEND_REQ);
2.拷贝内存数据到sigsend的buf中去memcpy(&sigsend->send_req.sendbuf[0], (char*) p, size);
3.将信号传递给send进程send(&sigsend,pid_wod_send);
错误处理要注意,如果分配了sig但是由于异常导致没有send出去,一定要判断异常并free掉这个sendsig,如果send成功,在send进程中处理完这个sig后也要free掉这块内存。
OS_PROCESS(wodRecvProc) { S32 err; union SIGNAL *sig; Ip_fd sockcli; struct Ip_sockaddr_in clisockaddr; struct Ip_sockaddr_in fromsockaddr; S32 socklen = sizeof(fromsockaddr); Ip_fd_set readset; U32 * p; struct msgHead* msg_p; U8 recvbuf[MAX_WOD_SEND_LENGTH]; U32 memoryAddr; U32 readSize; /* create socket */ sockcli = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0); /* bind address */ clisockaddr.sin_family = IP_AF_INET; clisockaddr.sin_port = IP_PORT; clisockaddr.sin_addr.s_addr = 0; err = ipcom_bind(sockcli, (struct Ip_sockaddr*)&clisockaddr, sizeof(clisockaddr)); IP_FD_ZERO(&readset); IP_FD_SET(sockcli, &readset); for(;;) { /* receive data */ err = ipcom_recvfrom(sockcli, (U8*)recvbuf, sizeof(recvbuf), 0, (struct Ip_sockaddr *)&fromsockaddr, &socklen); if (err >0) { msg_p = (struct msgHead*)&recvbuf[0]; switch(msg_p->Action) { case ATL_CMD_READ_MEMORY_REQ: // read memory p = (U32 * )&recvbuf[0]; memoryAddr = *(p + 9); readSize = *(p + 10); om_read_memory(memoryAddr,readSize); break; } } } ipcom_socketclose(sockcli); } void om_read_memory(U32 addr, U32 len) { union SIGNAL* sigsend; U32 p; U32 size; p = addr; size = len * sizeof(U32); if (size > MAX_SIG_SEND_REQ_LENGTH) { return; } sigsend = s_alloc(om_pool_id,sizeof(struct SigSendReq), SIG_SEND_REQ); sigsend->send_req.Action = ATL_CMD_READ_MEMORY_RSP; sigsend->send_req.sendlength = size; if (sigsend->send_req.sendlength > MAX_SIG_SEND_REQ_LENGTH) { free_buf(&sigsend); return; } memcpy(&sigsend->send_req.sendbuf[0], (char*) p, size); send(&sigsend,pid_wod_send); return; }
  目前看来,逻辑比较简单,问题在于处理天线端口上大量数据的时候,常常一次数据要分成不同的packet来发送,而且数据传输比较麻烦,单独开辟一个进程并利用DMA搬数首先,创建两个sem semWodDMA = create_sem(0); 在DMA开始之前和之后,查看定时状态,确定数据的有效性
//get current subframe and radio frame , timing check
deadline.sfn = deadlineRFC;
deadline.subframe = deadlineSFC;
status = l1IfTimingCheck(l1GetTiming(), &deadline);   if(status == SUCCESS) //data is valid { outputFlag = FAILURE; if((sig->send_data_req.dataType == PUS_CRCERR_ANT_FREQ_DATA) || (sig->send_data_req.dataType == PUS_AN_DTX_ANT_FREQ_DATA) || (sig->send_data_req.dataType == PUC_AN_DTX_ANT_FREQ_DATA) || (sig->send_data_req.dataType == DL_ANT_FREQ_DATA) || (sig->send_data_req.dataType == SRS_ERR_ANT_FREQ_DATA)) //data, config, scale { data_p = gWodDataFileLogOut[sig->send_data_req.dataType][log_file_para.SFC].addr; data_size = gWodDataFileLogOut[sig->send_data_req.dataType][log_file_para.SFC].len; cfg_p = gWodDataCfgAddr[sig->send_data_req.dataType][log_file_para.RFC & 1][log_file_para.SFC].addr; cfg_size = gWodDataCfgAddr[sig->send_data_req.dataType][log_file_para.RFC & 1][log_file_para.SFC].len; scale_p = gWodDataScaleAddr[sig->send_data_req.dataType][log_file_para.SFC].addr; scale_size = gWodDataScaleAddr[sig->send_data_req.dataType][log_file_para.SFC].len; /* cache invalidate */ DCACHE_INVALIDATE((INT8*)data_p, ALIGN_SIZE(data_size, POOL_ALIGNED_256)); outputFlag = carryWodData(data_p,data_size,cfg_p,cfg_size,scale_p,scale_size,&WodObifDataBuffer[0],WOD_DATA_DMA_REF_INFO,DmaWodDataCallback); } //启动DMA后,等待sem //wait semaphore if(outputFlag == SUCCESS) { wait_sem(semWodDMA); } DCACHE_FLUSH(&WodObifDataBuffer[0], ALIGN_SIZE(sizeof(WodObifDataBuffer), POOL_ALIGNED_256)); //若确定数据timing和size有效,则进行open,write,close status = omFileIdFind(&file_id); status = om_msg_file_open(file_id + dsp_file_id_offset,0,&log_file_para); //... }   考虑到多个DSP可能同时调用logdatatofile,所以需要在建立文件ID时加锁   INT32 omFileIdFind(U32* file_id) { static U32 fileIdx = 0; LOCK(); *file_id = fileIdx; fileIdx++; if(fileIdx == TOTAL_FILE_ID) { fileIdx = 0; } UNLOCK(); return SUCCESS; }
    注意,由于天线数据数量过大,memcpy到sig的sendbuf中不太现实,可以利用一个全局的buf暂存DMA转移过来的数据   S8 WodObifDataBuffer[MAX_TRACE_DATA_LEN + MAX_TRACE_CFG_LEN + MAX_TRACE_SCALE_LEN]; S32 om_msg_data_write(U32 FLIdx, U32 packetIdx, L1_Log* log_parm, U32 len, void* buffer) { union SIGNAL *sig; sig = s_alloc(om_pool_id,sizeof(struct SigSendReq),SIG_SEND_REQ); sig->send_req.Action = ATL_CMD_DATA_WRITE; sig->send_req.fileIdx = (U16)FLIdx; sig->send_req.packetIdx = (U16)packetIdx; sig->send_req.sendbufpointer = (U8*)buffer; sig->send_req.sendlength = len; if(sig->send_req.sendlength > MAX_SIG_SEND_REQ_LENGTH) { free_buf(&sig); return FAILURE; } send(&sig,pid_wod_send); return SUCCESS; }   这里直接将sendbufpointer赋值为这个全局地址即可。p = (S8*)&WodObifDataBuffer[0];
status = om_msg_data_write(file_id + dsp_file_id_offset,packet_idx[file_id],&log_file_para,MAX_SIG_SEND_REQ_LENGTH,p);
假如sendbufpointer为局部变量地址,则执行相关代码后会直接导致DSP崩溃。。。。