DSP,从入门到入土

DSP,从入门到入土本文是本人关于 ti 国产 6678DSP 学习的一些心得 仅代表本人的理解 内容较为基础 部分内容来源于本人的硕士学位论文及官方提供的技术手册

大家好,欢迎来到IT知识分享网。


前言

下面是本人关于ti/国产 6678DSP学习的一些心得,仅代表本人的理解,内容较为基础,部分内容来源于本人的硕士学位论文[论文地址https://kns.cnki.net/kcms2/article/abstract?v=DNPf6DgFiJsOXTyu-reGlppIitylD9L94buycBItj11aY72sEPNUmpv4FTS0iRc5kShgldM_5MJstvRhZaki1pQqc4-FuiOxaUR5Vsmiav8zf5tsrjL2Pi0SrLIQTmubE3-tedZeh7w=&uniplatform=NZKPT&flag=copy论文DOI:10.27389/d.cnki.gxadu.2022.000259]及官方提供的技术手册。

一、DSP架构

本章删除,具体可见前言中的论文

二、内存管理

本章删除,具体可见前言中的论文

三、 多核并行处理

目前主流的多核并行任务处理模式主要有主从模式和数据流模式,接下来将对这两种处理模式进行分析。

1.1、主从模式

1.2、数据流模式

四、多核同步

在主从模式下,需要对数据进行分块操作,八个核采用同样的算法对数据进行处理,在有些情况下,只有八个核都处理完后,才能进行下一步的算法处理,如本步对矩阵的行进行处理,下一步要对矩阵的列进行处理,只有八个核都完成对矩阵的行处理后,才能进行列处理。由于各种因素的限制,八个核不可能做到严格同时处理完,为保证下一步的正常进行,需要做一个同步处理,以提升程序的稳定性和保证程序的正确性。FT-M6678在工作时,可以支持实时操作系统SYS/BIOS,也可以在裸核状态下工作。对于操作系统和裸核,FT-M6678均提供了核间通信方式以完成多核同步:对于操作系统,可以采用消息中间件(Notify)同步、消息队列(Message Queue,MessageQ)、核间中断机制(Inter-Processor Communication,IPC)等方法;对于裸核,可以采用共享存储区变量、硬件信号量(Semaphore)等方式同步。考虑到SYS/BIOS操作系统的复杂性和不稳定性,在遇到问题时不易排查,一般常选择裸核的工作模式。接下来本节将主要对裸核模式下的共享存储区变量和硬件信号量两种同步方式做重点分析。

4.1、共享存储区变量

通过共享存储区变量实现多核同步的原理较简单。由本章3.2节可知,在FT-M6678的存储系统中,MSMC和DDR是两段公共的区域,八个核都可以进行访问。在进行多核通信之前,需要首先编写cmd文件,按照第1节的方法,通过MEMORY在MSM或DDR中开辟一段公共区域,然后在SECTIONS中完成段的映射,最后将结构体定义在该段上

typedef struct _Radar_FLAG_{ 
     char sync_flag[8]; }RadarFlag_t; extern RadarFlag_t RadarFlag; #pragma DATA_SECTION(RadarFlag,".data_msmc"); #pragma DATA_ALIGN(data_msmc, 8); RadarFlag_t RadarFlag; 

我们在共享存储区的.datda_msmc段上定义了一个结构体,其中sync_flag是一个拥有8个元素的数组,每个核拥有一个元素,作为同步的标志位。具体的使用如下所示。其中CoreNum为0-7核的核号,初始化阶段,将sync_flag数组里的元素都置为0,执行完多核任务后,将sync_flag[CoreNum] 置为1,当检测到sync_flag数组里的所有元素都为1时,则将sync_flag[CoreNum]重新置为0,并开始进入下一步的多核处理任务,否则在while循环里等待,直到其它核的任务处理完。

multi_task0(); RadarFlag.sync_flag[CoreNum]=1while(!(RadarFlag.sync_flag[0]&RadarFlag.sync_flag[1]&RadarFlag.sync_flag[2]& RadarFlag.sync_flag[3]&RadarFlag.sync_flag[4]&RadarFlag.sync_flag[5]& RadarFlag.sync_flag[6]&RadarFlag.sync_flag[7])); RadarFlag.sync_flag[CoreNum]=0multi_task1(); 

该方式虽然实现简单,但可能存在存储高速缓存一致性问题,关于这个问题,将在第5节进行详细分析,多个核对同一片区域读写可能会造成数据的读取失败而不能完成多核的同步。可以通过配置相关的MAR寄存器将某片区域配置不可Cache,并将结构体定义在这片区域上以解决这一问题。

4.2、硬件信号量

multi_task0(); CSL_semAcquireDirect(CoreNum+1); while(CSL_semIsFree(1)|CSL_semIsFree(2)|CSL_semIsFree(3)|CSL_semIsFree(4)| CSL_semIsFree(5)|CSL_semIsFree(6)|CSL_semIsFree(7)|CSL_semIsFree(8)); multi_task1(); CSL_semReleaseSemaphore(CoreNum+1); while(!(CSL_semIsFree(1)&CSL_semIsFree(2)&CSL_semIsFree(3)&CSL_semIsFree(4)& CSL_semIsFree(5)&CSL_semIsFree(6)&CSL_semIsFree(7)&CSL_semIsFree(8))); multi_task2(); 

硬件信号量的实现也较为简单,可以直接调用官方的函数实现,相比于共享存储区变量,不会出现高速缓存一致性问题,准确率更高,具有显著的优势。因此本文采用硬件信号量并使用相对简单的直接方式来实现多核同步。

五、Cache的使用

在这里插入图片描述
以核0为例,在某个时刻,Cache中保存了存储器中数据0x11223344的副本,在下一时刻,核0需要将存储器中的数据修改为0x55667788。由于Cache中存在存储器中数据的副本,因此省去了CPU去外部存储器寻找该数据的过程,直接将Cache中的数据修改为了0x55667788,而存储器中的数据并未更新。之后核1去存储器中读取数据,此时读取到的数据为0x11223344,数据读取错误。
在具体使用时,一般会用到两级缓存,一级缓存速度较快,但容量较小,二级缓存速度为一级缓存速度的一半,但是容量较大。如果目标数据在Cache中,每次CPU都可以直接在Cache中取数,那么Cache的命中率就会提高,程序的运行速率也会相应地提高。为了提高Cache的命中率,需要合理地配置二级缓存的大小。L1、L2均可全部配置为Cache,但是Cache空间并非越高越好。如果Cache的比例较高,会影响SRAM的空间,反而会降低程序的运算速率。因此应该根据计算的规模,合理地对Cache大小进行配置。当CPU要访问外部存储器中的数据时,会首先在L1中去寻找相应的数据拷贝,如果找到,则命中,否则去L2中寻找,如果还是无法命中,则接着去外部存储器中去找。在找到相应的数据后,先把数据读取到L2中,再读取到L1中,最后读取到内核中。考虑到L1的速度较快,空间也可以足以满足计算规模的需要,因此本设计将L1全部配置为Cache,L2将Cache大小配置为64KB,为计算预留一定的空间,L2其余的448KB的空间为SRAM,存放程序段以及其它相关段。采用官方提供的配置函数,具体配置过程下所示。

CACHE_setL1PSize (CACHE_L1_32KCACHE); CACHE_setL1DSize (CACHE_L1_32KCACHE); CACHE_setL2Size (CACHE_64KCACHE); 

M66x提供了两种方式进行一致性维护,一种是硬件自动维护,另一种是手动软件维护。对于核内L2 SRAM与L1D Cache数据直接的一致性,硬件会自动维护。但是对于L2 SRAM与L1P Cache之间的一致性,外存(包括MSM和DDR3)与L1 Cache、L2 Cache之间的一致性,需要手动软件维护。对于手动软件维护,官方提供了三种操作:作废、写回、写回作废。下面对这三种操作进行简单介绍。

L1 Cache和L2 Cache均支持上述三种操作,既可进行块操作(即在某个范围内进行操作),又可进行全局操作(即在整个区域内进行操作)。对于具体的操作,官方提供了相应的函数,具体介绍如下所示。

CACHE_invL1p L1P块作废 块操作
CACHE_invL1d L1D块作废
CACHE_invL2 L2块作废
CACHE_wbL1d L1D块写回
CACHE_wbL2 L2块写回
CACHE_wbInvL1d L1D块写回作废
CACHE_wbInvL2 L2块写回作废
CACHE_invAllL1p L1P全局作废 全局操作
CACHE_invAllL1d L1D全局作废
CACHE_invAllL2 L2全局作废
CACHE_wbAllL1d L1D全局写回
CACHE_wbAllL2 L2全局写回
CACHE_wbInvAllL1d L1D全局写回作废
CACHE_wbInvAllL2 L2全局写回作废












注:由于L1P不存放数据,所以不存在数据写回操作。对于块操作,函数的有效输入参数有数据所在存储器的起始地址、数据长度;对于全局操作,函数无有效输入参数。

六、DMA的使用

void *memcpy(void *dst, const void *src, size_t _n) 

DMA(Direct Memory Access),即直接存储器访问,它的实现需要额外的硬件参与。FT-M6678在内部集成了DMA控制器,通过CrossNet总线,对内连接八个核内部各自的L1P、L1D和L2等存储器,对外连接了MSMC、DDR、SRIO、EMIF等存储器和外设。可以通过对DMA控制器的配置,可以完成对DSP不同存储器之间的数据交互。
DMA三维搬移示意图

DMA最多可以在三个维度上进行数据搬移,即acnt、bcnt和ccnt,但一般只需要用到两个维度的acnt、bcnt即可完成搬移,具体使用如下所示,其中ChannelNum为使用的DMA通道号,src为源地址,dst为目的地址,acnt为一次搬移的数据长度,bcnt为搬移重复的次数,srcBidx为每次搬移源地址跳变的长度,disBidx为此搬移目的地址跳变的长度。DMA对数据的搬移操作比较灵活,可以顺序搬移,也可以跳跃搬移。通过跳跃搬移,可以实现矩阵的转置等功能。DMA的操作相对独立,不需要CPU的参与,速度较快,适合大数据量搬移或地址有跳变的场合。

void DMA_transport_ab_region(int ChannelNum,Uint32 src , Uint32 dst, int acnt, int bcnt, int srcBidx, int dstBidx) 

完成矩阵转置关键的步骤为通过地址的跳跃取数将矩阵每行的数据取出,并按顺序存储于新地址。考虑到直接通过循环赋值的方式较为繁琐,且效率较低,因此可以采用DMA方式完成矩阵的转置。假设矩阵为浮点型实数矩阵,距离向点数为nrn,方位向点数为nan ,将第n行数据转置至第n列时,DMA搬移的源地址为起始地址加上n4个字节的偏移量,目的地址为起始地址加上nnrn4个字节,每次源地址跳变nrn4个字节,目的地址跳变4个字节,重复nan次。考虑到八核的并行,假设每个核的核号为CoreNum ,则在同一时刻各核处理的数据行号为CoreNum*nrn/8+n ,其中0<n<nrn/8 。对于复数矩阵的转置,原理与其相同,这里不做过多介绍。
在使用DMA搬移数据前,要首先使用DMA_Init_region(1)打开相关通道,在搬移数据后,要使用waitDMAover_region(1)等待搬移完成中断,在DMA使用完毕后,使用DMA_Close_region(1)进行关闭。当然,DMA的打开和关闭也可以置于程序的最开始和结尾,但个人更倾向于置于使用前和使用后,个人习惯罢了。

七、一些编程tips

7.1 关键字

volatile
使用volatile关键字后,使用-o3编译选项,编译器也不会对该变量的访问做任何优化,用来声明一些 全局变量。
(1)凡是2个线程共享的全局变量就需要使用volatile关键字。
(2)凡是某个内存地址的内容随时可能被外部硬件环境改变就需要使用volatile关键字。
restrict
使用restrict关键字,程序员告诉编译器确保在指针定义范围内对象指针只能被这个指针访问。




void floatVectorExp_2(float * restrict arrayIn, float *restrict array) { 
     } 
float RealVectorSum(const float *restrict x,const int nx) { 
     } 

UNROLL
UNROLL编译指示用于指定编译器循环需要被展开多少次。除了MUST_ITERATE和PROB_ITERATE外,在UNROLL编译指示与for,while,do-while循环之间不能有其他声明。
例: #pragma UNROLL(n)
编译器展开循环,导致存在原来循环的n份拷贝。配合MUST_ITERATE编译指示告诉循环最小可能循环迭代数、最大可能循环迭代数、循环倍数。
MUST_ITERATE
使用MUST_ITERATE编译指示,可以保证循环执行特定的次数。
例1:告诉编译器循环并执行正好10次,





#pragma MUST_ITERATE(10,10); for(i=0; i<conut;i++{ 
     } 

例2:告诉编译器循环并执行8—48之间,且循环次数变量为8 的倍数(8,16,24,32,40,48)

#pragma MUST_ITERATE(8,48,8); for(i=0; i<conut;i++{ 
     } 

7.2 struct定义

typedef struct { 
     float month; float day; float year; }Date; extern volatile Date birthday; 

若将该结构体放在DDR3中

#pragma DATA_SECTION(birthday, ".dataDDR"); #pragma DATA_ALIGN(birthday, 8); volatile Date birthday; 

说明:

关键字 volatile 必须加上

.dataDDR 定义为DDR3中的段

7.3 存储

char sss[64]; sss[0] = *((char*)0x); // 第一个字节 sss[1] = *((char*)0x+1); // 第二个字节 float aaa[8]; aaa[0] = *((float*)0x); // 取 0x这个地址开始的四个字节 aaa[1] = *((float*)0x+1*4); //取 0x这个地址开始的四个字节 

小端格式:数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中;
例:32bit宽的数0x,放在内存(0x4000)中
内存地址 0x4000 0x4001 0x4002 0x4003
数据 0x78 0x56 0x34 0x12


大端格式:数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中;
例:32bit宽的数0x,放在内存(0x4000)中,
内存地址 0x4000 0x4001 0x4002 0x4003
数据 0x12 0x34 0x56 0x78


7.4 动态内存

动态内存位于堆区,一般将堆区映射至L2空间上。在计算过程中,考虑到L2的带宽较大,因此常江一些变量置于L2中。可采用malloc/free进行动态内存的申请与释放,具体使用格式如下:

float *temp1 = (float *)malloc(nrn_m*2*size_float); free(temp1); 

在上述语句中,使用malloc申请了一个nrn_m*2大小的存放浮点型数的动态数组,在使用完毕后通过free进行了释放,在使用完毕后,请务必要进行释放,否则有可能会导致内存泄露。

7.5 编译器选项

-O0:属于寄存器级的优化,它的优化功能包括精简控制流和表达式,为程序中声明的变量分配寄存器,将程序中未用到的代码删除,调用程序中的内联函数(Inline
function)。
-O1属于局部级的优化,它的优化功能在-O0级的基础上增加了对本地拷贝或局部变量的使用,将未使用到的变量赋值和局部相同的表达式删除。
-O2::属于函数级的优化,它的优化功能在-O1级的基础上增加了循环优化和软件流水,在循环中使用指针增加的形式代替对数组的引用,删除了全局相同的表达式及未使用到的变量赋值。
-O3:属于文件级的优化,它的优化功能在-O2级的基础上增加了对带有返回值但未使用返回值的函数的精简,删掉了对文件中所有未使用的函数,并对小函数进行内敛优化和进行SIMD操作。



在程序优化前,要选择上述几个选项之一。在选择-O2、-O3时,程序不是顺序执行的,程序在进行流水线操作和循环优化时,编译器会把自认为无用语句去掉,但实际使用中,这些可能是有用的(-O0和-O1选项无此问题)。针对此问题,可以同时使用-g选项避免此问题,但这样会造成程序运行效率的下降,降低了优化效果。另外,也可以改变程序的编写方式,如使用volatile关键字修饰相关变量,限制其对相关语句的优化。首先需要通过调试确定哪些关键变量被优化掉了,之后使用volatile关键字修饰这些关键变量。使用volatile关键字配合-O2/-O3的方式对程序的优化效果相比于未进行软件流水的-O0/-O1方式更为高效,此外还有以下几个常用的编译器选项。

八、八核固化

Core0 L2 SRAM 0x Core1 L2 SRAM 0x Core2 L2 SRAM 0x Core3 L2 SRAM 0x Core4 L2 SRAM 0x Core5 L2 SRAM 0x Core6 L2 SRAM 0x Core7 L2 SRAM 0x 

之后运行spiboot_multi_8cores_noddr.bat,生存bootimage_nor.dat,假设该文件的第一行数据为1651 9 0C000000 0 1606 1,则该头文件内容的头部含义如下:

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/137935.html

(0)
上一篇 2025-06-16 19:10
下一篇 2025-06-16 19:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信