DSP汇总整理
DSP架构
TMS320C6678有8个C66x核,每个CorePac核的频率最高为1.25 GHz,提供强大的定点和浮点运算能力,同时芯片内部集成了Multicore Navigator、RapidIO、千兆以太网和EDMA等外设,芯片处理能力强,外设功能丰富,而且片内集成了大量的硬件加速器。
SYS/BIOS
可扩展的实时的操作系统。
线程创建:SYS/BIOS定义的四种线程类型(硬件中断(HWI),软件中断(SWI),任务(TASK),空闲(Idle)),高优先级的线程能够抢线程占低优先级的线程,优先执行。
线程同步:信号量通常用于协调一些处于竞争关系的任务之间对共享资源的访问。通过改变信号量的计数值,信号量的值为0的时候任务处于挂起状态,信号量为1的时候任务恢复继续执行。
内存管理:动态分配都是通过Memory模块完成的,Heap模块负责管理内存。
NDK开发环境
提供了完整的TCP/IP功能环境,包括应用层,传输层,网络层和数据链路层。
DMA传输
基于C66x的内核处理器主要具有两种DMA传输:IDMA、EDMA3。
IDMA:只提供核内部(L1P、L1D、L2、CFG)的数据移动服务。C6678处理器中,IDMA具有两个通道。
EDMA3:服务于器件上两个存储器映射的从终端之间的数据传输(如DDR3、MSM、SRAM和L2)。
EDMA3(增强型内存直接访问控制器)
EDMA3控制器主要由两部分构成:
EDMA3通道控制器(EDMA3CC):充当EDMA3控制器的用户接口
EDMA3传输控制器(EDMA3TC):依附于EDMA3通道控制器,负责数据传输,向编程传输的源和目的地址发布读写请求。
EDMA3的工作流程
首先编程者先配置DMA/QDMA通道逻辑参数,然后参数传送给事件队列,事件队列获取PaRAM参数,然后提交传输请求到传输控制器。传输控制器来完成数据读写和数据传输。传输完成后通道控制器的传输完成检测会收到传输控制器的反馈,此时通道控制器向CPU提交传输完成中断。
EDMA3通道控制器结构
EDMA3有3种触发方式,事件触发、手动触发、链接触发,它们由寄存器配置。事件触发后会通过优先级编码器仲裁,将高优先级送往队列,经过队列后,通道映射根据事件所对应的通道来选择参数集(在此之前通道和参数集已经做了映射了),然后提交传输请求给EDMA3传输控制器。传输完成后通道控制器的传输完成检测会收到传输控制器的请求,通道控制器向CPU提交传输完成中断。
触发方式:DMA与QDAM的主要的不同就是触发方式的不同。
1、DMA通道有3种触发方式:
事件触发:一个外围设备,系统或外部产生的事件触发一个传输请求。
手动触发:通过设置寄存器的相应位,DSP手动触发一个传输。
链接触发:在另一个传输或子传输完成后,触发另一个传输。
2、QDMA通道有2种触发方式:
自动触发:写到已触发的触发字触发一个传输。
链接触发:当连接发生,写到触发字触发传输。
EDMA3传输控制器结构
EDMA3传输控制器收到通道控制器传来的数据后,进行数据读写以及数据传输,在传输完成后,给EDMA3通道控制器中断反馈,表明数据传输完成。
EDMA3的传输数据块
每一个EDMA3传输数据都是可以看作是一个三维的数据,这个数据的大小由ACNT,BCNT,CCNT来描述。
第1维 阵列(A):由ACNT个连续字节构成。
第2维 帧(B):由BCNT个ACNT字节的阵列。第2维里的每个阵列传输通过一个索引彼此分开,这个索引使用SRCBIDX和DSTBIDX来配置。
第3维 块(C):由CCNT个帧,每个帧由BCNT个阵列,而每个阵列有ACNT个连续字节。第2维里的每个传输由以一个索引同前一个分开,这个索引使用SRCCIDX或DSTCIDX来配置。
EDMA3传输类型
A同步传输:每次传输一个阵列,也就是ACNT个字节。传输一个ACNT x BCNT x CCNT的数据块总共需要传输BCNT x CCNT次。
如上图的例子,3(CCNT)个帧,每帧4(BCNT)个阵列,每阵列n(ACNT)个字节的A-同步传输。为了完成一个PaRAM集的传输需要12个同步事件(BCNT x CCNT)。
如果EDMA3搬运的是一片连续的内存,则SRCBIDX=DSTBIDX=ACNT,SRCCIDX=DSTCIDX=ACNT,即上一行最后一个阵列的地址到下一行第一个阵列的地址的长度。
AB同步传输:每次传输一帧,也就是ACNTxBCNT个字节。传输一个ACNT x BCNT x CCNT的数据块总共需要传输 CCNT次。
如果EDMA3搬运的是一片连续的内存,则SRCBIDX=DSTBIDX=ACNT,SRCCIDX=ACNTxBCNT,即上一行第一个阵列的起始地址到下一行的起始地址。
参数PaRAM
参数RAM存放每个EDMA3通道需要的传输控制参数。每个EDMA3事件对应一个参数组,每组包括8个字(32字节),n是对于一个器件中支持的PaRAM参数集的数目。
OPT参数
通过配置OPT参数选项,确定触发方式、中断方式、传输类型、寻址模式、FIFO宽度等选项。
缓存机制与Cache一致性
DSP缓存机制
在DSP中,不同存储器的工作主频不同,这就导致了存储器和处理器的性能之间存在了差异,当CPU在主频较低的存储器上读取数据时,会因为二者的速度差异而去等待造成性能上的浪费,为解决存储器和处理器性能之间的矛盾,产生了缓存(Cache)机制。
即在CPU和内存之间增加一个速度极快但是容量极小的设备,我们称这段存储为Cache,采用Cache机制来实现外部数据在靠近处理器的内核的存储器中保留一份复制。在读写数据时,CPU会首先从Cache中查找对应地址的数据是否缓存在Cache中,如果其数据缓存在Cache中,直接从Cache中拿到数据并返回给CPU。因此在使用时,在程序中打开Cache,把经常使用的程序和数据放入Cache中,能在一定程度上提高程序的运行效率。
C66x DSP存储器由内部两级基于Cache的存储器(L1(L1P和L1D),L2)和外部存储器组成,L1P和L1D都可以被配置成SRAM和Cache,Cache最大可达到32KB,L2可以被配置。C6678器件上电配置如下:
存储器 | 组织方式 | 配置(复位后) | 大小 |
L1P | 直接映射 | Cache | 32KB |
L1D | 2路组相连 | Cache | 32KB |
L2 | 4路组相连 | L2全是SRAM,Cache的容量可配置为32/64/128/256KB或全为Cache |
处理器访问存储器流程
当CPU想要访问一段数据的时候,会先访问缓存,当我们需要的数据在Cache中有被缓存到,我们就称为一次“命中(hit)”,反之当数据不存在的时候,我们称为一次“缺失(miss)”。当我们的计算机系统存在多级缓存的时候,计算机在进行数据访问的时候会逐级进行查找,即:CPU首先从L1 Cache进行查找,当数据在L1 Cache时,我们就将数据返回给CPU(一次缓存命中),但是当L1 Cache不存在这段数据的话,CPU会将这一次查找延伸到下一级缓存L2 Cache,那么L1 Cache的访问就是一次缓存缺失;CPU在L2 Cache进行查找的时候,如果命中了就会将数据返回给CPU,如果缺失就继续延伸到下一级缓存(如果不存在后续缓存就到内存中查找)。
Cache一致性问题分析
在任何时刻,CPU或者其它外设访问存储器中数据时,由于Cache的存在造成不能够得到最近更新过的数据,就会出现Cache一致性问题。
读一致性模型
在这个模型中,Cache一致性问题的存在取决于图中虚线箭头指示的第二步操作能否在Core从Cache中重新读数据之前完成。如果不能,则会造成Core读取的数据不是其它Master更新后的数据,而是原来Cache中的内容,从而导致一致性的问题。
写一致性模型
在这个模型中,Cache一致性问题的存在取决于图中虚线箭头指示的第二步操作能否在其它Master从存储器中读数据之前完成。如果不能,会造成其它Master从存储器中读到的数据是原来的数据而不是Core更新过的数据,从而导致一致性的问题。
Cache软件一致性维护操作
出现Cache一致性问题时,为了保证Core或者其它Master在进行数据操作的时候能够得到最新的数据,需要进行Cache的一致性维护操作。
由软件控制的一致性维护操作包含三种:
Cache操作 | 全局一致性使用的寄存器 | 块一致性使用的寄存器 | 说明 |
失效 | _INV | _IBAR _IWC | 有效的Cache Line被置成无效,受影响的Cache Line内容被废弃 |
写回 | _WB | _WBAR _WIWC | Vilid和Dirty的Cache Line内容被写到更低级别的存储器中 |
写回失效 | _WBINV | _WIBAR _WIWC | 只有受影响的Cache Line内容被写到更低级别的存储器中,但是所有行都失效了 |
软件维护代码Cache一致性:
- 外设对L2中代码更新修改
软件触发L1P失效操作,在L2中读取,同时将相应代码复制到L1P当中。
- 外设对DDR中代码更新修改
软件触发L1P、L2失效操作,在DDR中读取,同时将相应代码复制到L1P和L2当中。
软件维护数据Cache一致性:
- 外设对DDR中数据进行更新,主核读取DDR中数据
软件触发L1P、L2失效操作,在DDR中读取,同时将相应数据复制到L1P和L2当中。
- 主核对DDR中数据进行更新,外设读取DDR中数据
更新的数据可能被保存在L1D或者L2 Cache中,L1D或者L2 Cache中更新的此部分数据回写到DDR,主外设即可读取到DDR中数据。
相关外设
EMIF16外围设备
外部存储器接口,EMIF可实现DSP与不同类型存储器(SRAM、Flash、RAM、DDR-RAM等)的连接。一般EMIF与FPGA相连,从而使FPGA平台充当一个协同处理器、高速数据处理器或高速数据传输接口。
EMIF16异步接口的基本框图及描述
异步访问配置 EMIF16
- EMIF16时钟为CPU/6频率。
- 在任何给定时间可以通过四个片选访问这些存储器中的总共 256M 字节,每个片选可以访问 64M 字节。
- EMIF16 支持以下模式:
• WE 扩展等待模式
• SS 选通模式 - #EMIFCE, #EMIFWE, #EMIFOE, #EMIFBE[1:0] 是决定读/写周期开始和结束的控制信号。
- #EMIFCEx(x=0,1,2,3) 片选信号将在整个异步访问期间保持激活状态。它允许两个 8 位设备连接到同一个片选。
- Select Strobe (SS) 模式通过设置异步配置寄存器中的“ss”位来激活所考虑的芯片选择。每个芯片选择有一个异步配置寄存器。在SS模式下,片选作为选通信号。因此片选 #EMIFCEx将跟随 #EMIFOE 进行读取 #EMIFWE 进行写入,并且仅在选通期间有效。
16 位和 8 位 SRAM/NOR Flash 连接到片选 0 的 EMIF16 连接图分别如下。
EMIFA[23:22] 表现为地址选择。对于 16 位接口, EMIFA23 连接到 ASRAM/NOR Flash 的地址引脚 A0。 对于 8 位接口, EMIFA[23:22]连接到 ASRAM/NORFlash 的地址引脚 A[1:0]。
通用目的输入输出(GPIO)
GPIO提供专用的通用引脚,可配置为输入或输出。此外,GPIO外设可以在不同的中断/事件生成模式下产生CPU中断和EDMA同步事件。一些GPIO引脚与其他设备引脚复用。
GPIO外围设备框图
GPIO的配置
- GPIO寄存器地址范围为0x02320000~0x023200FF。
- 用户可通过方向寄存器将每个GPIO引脚(GPn)独立配置为输入或输出。逻辑0为输出,逻辑1为输入。
- 当配置为输出时,若要将某一位的GPn驱动到逻辑高状态,将1写到对应的设置数据寄存器(SET_DATA)中。若要将某一位的GPn驱动到逻辑低状态,将1写到对应的清除数据寄存器(CLR_DATA)中。此外,每个GPn的输出状态也可以通过写入输出数据寄存器(OUT_DATA)来直接控制。
- 当配置为输入的GPIO引脚,读取输入数据寄存器(IN_DATA)将返回引脚状态。
- 每个GPIO引脚(GPn)都可以配置为生成CPU中断和EDMA同步事件。并可以在上升沿、下降沿或两个边沿都生成,由两个内部寄存器RIS_TRIG和FAL_TRIG来指定。
多核处理
多核并行任务处理模式主要有主从模式和数据流模式。
主从模式:
主从模式中,人为地将8个内核分为主核和从核。由主核负责对任务进行分配,完成流程控制。0核具备和FPGA建立SRIO通信的能力,FPGA通过SRIO接口向DSP发送回波数据,在数据发送完毕后,发送doorbell中断,进入中断服务函数。通过核号,将数据分为八份,八个核采用相同的算法处理不同的数据,每个核相互独立,适合处理非耦合性的数据。此种模式下数据处理时间可接近单核的八分之一,每个核采用的算法完全相同,易于工程实现。
数据流模式:
在数据流模式中,人为地将算法分为八个部分,每个核执行算法的一部分。该模式下,核间不是相互独立的,而是高度依赖的。核0在收到FPGA的doorbell中断后,开始算法处理。0核首先执行属于0核的任务,此时1-7核属于等待状态,0核执行完毕后,1核开始处理0核处理后的数据,此时2-7核处于等待状态,0核开始接收下一帧的回波数据并处理,依次循环,当0核收到第九帧的回波数据后,7核正好处理完第一帧的回波数据,并准备处理第二帧的回波数据。此种模式下,只要不断有数据进行处理,再处理完八帧数据后,数据处理的时间的单核的八分之一,但需要按照计算量,严格地对算法就行分割。该模式下各核与任务之间的关系如下图所示。
通过以上的分析发现,两种模式的数据处理时间都可近似达到单核的八分之一。对于主从模式,并非对所有数据都适用,需要行数据或列数据之间不存在耦合性,可以直接对数据进行分块处理;对于数据流模式,对任何数据的处理均适用,但数据流模式下,需要严格地将总算法按计算量分为八份,大大加剧了工程人员的工作量,工程实现较为困难。因此在DSP进行并行处理操作时,需要选用合适的并行处理模式。
C66x多核协作 核间通信
DSP软件采用多核多线程架构,各线程之间通过信号量和消息队列进行同步和数据传递,通过核间通信机制实现多核同步,进行信息、数据交换。
可以使用官方提供的IPC模块进行核间通信。
IPC常见的通信模块如下:
执行Ipc_start(),每个核之间完成同步,如果不需要每个核之间都同步时,就可以去手动调用Ipc_attach(),让特定的几个核之间建立联系。
MessageQ模块:
主核(核0)在将数据输入共享内存后,需要将数据的缓存的地址打包成消息,通过消息队列传递给从核。MessageQ是用来传递少量信息的,一般是地址,标志位之类的参数,不是传递大规模数组用的。对于大规模的数组,可以利用messageQ传递其首地址。实现了处理期间变长消息的传递,所需要传递的消息一般超过32bit。
主核如果给从核发送messageQ,步骤如下:
1 主核创建heapBufMP
2 从核创建messageQ messageQ_create() (谁接收,谁创建)
读线程创建一个消息队列。要动态创建一个 MessageQ 对象,其语法如下:
MessageQ_Handle MessageQ_create(String name,MessageQ_Params *params);创建队列时,需要指定名称字符串,因为MessageQ _ open ()函数需要此名称。如果调用成功,则返回messageq_handle。如果调用失败,则返回NULL。
3 主核打开messageQ messageQ_open() (谁发送,谁打开)
Writer 线程打开创建的消息队列来访问它们。为了获得已创建的消息队列的句柄,编写线程必须调用 MessageQ _ open () ,其语法如下。
Int MessageQ_open(String name, MessageQ_QueueId *queueId);
如果 MessageQ open 成功,队列 ID 被填充 MessageQ_S_SUCCESS。
4 主核分配空间 messageQ_alloc()
MessageQ 通过 MessageQ _ alloc ()和 MessageQ _ free ()函数管理消息分配;
5 主核发送messageQ messageQ_put() (主核在打开之后,完成数据的发送)
一旦打开消息队列并分配了消息,就可以通过 MessageQ _ put ()函数将消息发送到 MessageQ。函数语法如下:Int MessageQ_put(MessageQ_QueueId queueId, MessageQ_Msg msg);
如果 MessageQ _ put ()成功,则返回 MessageQ _ s _ success。
6 从核接收messageQ messageQ_get() (接收完毕之后进行从核内部自己的处理)
为了接收消息,读线程调用MessageQ _ get () API接口,接口函数如下所示:
Int MessageQ_get(MessageQ_Handle handle,MessageQ_Msg *msg, UInt timeout)
7 从核处理函数
MessageQ _ delete ()释放存储在本地内存中的 MessageQ 对象。如果任何消息仍然在内部链接列表中,它们将被释放。句柄的内容被函数取空,以防止删除后使用。
Void MessageQ _ delete (MessageQ _ handle * handle) ;
一旦消息队列被删除,就不能向它发送任何消息。
Notify模块:
具有如下特点:
(1) 可独立于MessageQ模块进行使用。
(2) 着重于多核通知功能,是更为简单的多核通信形式。支持最多32bit的数据传输。
(3) Notify通过硬件中断传输消息,Receiver注册Notify事件中断,将某个事件源和一个回调函数(callback)绑定。Sender通过Notify发送事件中断,从而实现通知并携带小量消息的目的。
■ Receiver:调用Notify_registerEvent()注册事件中断服务函数。
■ Sender:调用Notify_sendEvent()发送事件中断。