本文翻译自TI的手册,该手册是学习GPP+DSP开发的金典文档,希望对各位入门有所帮助,有理解不当之处望请赐教。
Codec Engine Application Developer User's Guide.pdf (Literature Number: SPRUE67D)
《Codec Engine 应用开发使用手册》 http://blog.csdn.net/dyzok88/article/details/42154487
《第一章 Codec Engine 概要》 http://blog.csdn.net/dyzok88/article/details/42214813
《第二章 Codec Engine 安装和设置》 http://blog.csdn.net/dyzok88/article/details/42278109
《第三章 使用 Codec Engine 的示例应用程序》 http://blog.csdn.net/dyzok88/article/details/42302793
《第四章 使用 Codec Engine 的 API 函数 (一)》http://blog.csdn.net/dyzok88/article/details/42323123
《第四章 使用 Codec Engine 的 API 函数 (二)》http://blog.csdn.net/dyzok88/article/details/42324061
《第四章 使用 Codec Engine 的 API 函数 (三)》http://blog.csdn.net/dyzok88/article/details/42344661
《第四章 使用 Codec Engine 的 API 函数 (四)》http://blog.csdn.net/dyzok88/article/details/42353141
// 正文
4.5 发生什么 DSP 内存问题?
通过 Codec Engine 提供的创建和删除算法的 VISA API 函数,可以管理所有的算法资源。这包括CPU,内存,直接存储器存取( DMA ),等等。VISA 的创建和删除的 API 隐藏了大部分的编解码器的内存和资源管理的细节。
4.5.1 缓冲处理和共享内存映射
处理所有的 I/O 和缓存的问题是应用程序的责任。控制功能的 API 函数使用指针指向 XDM_BufDesc 类型的输入和输出缓冲器,XDM_BufDesc 结构体如下:
typedef struct XDM_BufDesc {
XDAS_Int8 **bufs;
XDAS_Int32 numBufs;
XDAS_Int32 *bufSizes;
} XDM_BufDesc;
你必须把它指向的缓冲区放在 GPP 和 DSP 共享的内存区域,这摒弃了从 GPP 到 DSP 传输大量的信号数据和回传数据所造成的过高的性能影响。
此外,您使用的共享数据缓冲区必须是连续的和高速缓存对齐的。
例如,DM644x 的默认内存映射的设计意图是,为 DSP 代码和数据提供大量的内存空间,为 DSP 算法提供大量的私有堆,为 GPP 与 DSP 之间的共享缓存区提供更大的空间。
Table 4–3 DM644x Default Memory Map
4.5.2 内存碎片
对于双 CPU 的应用程序,例外的是 Codec Engine 隐藏来自 GPP 应用程序开发人员的 DSP 内存管理问题,缓冲区传递到 DSP 必须是连续的物理内存和高速缓存对齐。
这不同于 GPP 的缓冲区处理,因为 Linux 和类似的 GPP 操作系统通过存储器管理单元( MMU )来处理非连续缓冲区,MMU 持有匹配虚拟地址到物理地址的映射表,然而 DSP 没有这样的表。
Codec Engine 验证这些约束条件在数据缓冲区所需的平台得到满足,算法缓冲区由 Memory_module 管理,Memory_module 使用不同大小的池,确保内存不是成碎片的。
然而,由 Codec Engine 创建的编解码器实例的存储空间,也必须是连续的,高速缓存对齐的。编解码器实例的创建和删除是非确定性的。例如,如果你的应用程序遵循类似于图4-1的步骤,它可能无法重新创建被更早创建和删除的一个编解码器的实例。
Figure 4–1 Non-Deterministic Buffer Creation and Deletion
因为创建实例发生在“后台”,而其它编解码器在更高的优先级运行,你不能保证按所需的时间创建一个实例。但是,你可以控制创建和删除实例的顺序。
如果编解码器或共享缓冲器不是物理上连续,当主叫方为了获得布尔 *isContiguous 参数,使用非 NULL 指针调用 Memory_getBufferPhysicalAddress() 函数,Codec Engine 设置指针( ptr )数据为真或假,但不打印任何消息。如果该指针为 NULL(这是最有可能的,因为该函数被 codec stubs 调用,codec stubs 传递 NULL 给该指针),优先级为7的跟踪消息是:
Memory_getBufferPhysicalAddress> ERROR: user buffer at
addr=, size= is NOT contiguous
仅在优先级为7时启用跟踪时此消息才被打印出来,这是默认的。
4.5.3 缓存对齐
利用高速缓存的设备(例如,C64+)还要求 I/O 缓冲区是缓存对齐的。例如,在 C64x+ 上的 DSP L2 cache line 的大小是128 bytes。分配存储空间,必须从 cache line 边界开始,大小必须为 cache line 长度的倍数。
如果这些对齐和大小的限制被侵犯,分配到与应用缓冲区相邻的任何数据对象,将与应用程序缓冲区的一部分共享一个 cache line。当高速缓冲存储器控制器( Cache Controller )回写共享的 cache line,后果可能是这条线会被破坏。
4.5.4 Cache一致性
当开发用于多处理器平台的应用程序(包括那些具有多个处理核,硬件加速器和 DMA 引擎),其中某些内存区域被缓存,您必须执行一些内存操作的一致性。当 Codec Engine 框架有足够的信息,将自动处理一些高速缓存一致性操作。然而,应用程序开发者最终负责确保一定的预处理和后处理的条件得到满足,即缓冲区的应用程序的提交和 Codec Engine 的接收。
后面的小节总结了针对不同的处理器环境,应用程序开发人员的责任。
4.5.4.1 GPP + DSP 环境
下面是达芬奇( DaVinci )环境中的常见的问题,它们存在于任何利用缓存的多核系统。
请注意,Codec Engine 框架在VISA API 的实现中,强制执行其中的一些规则。使用 VISA API 函数访问共享内存时,你应该知道这些规则。
1.
Input Buffers. 这是当一个 GPP 应用程序捕获/生成一个缓冲区并把它传递给DSP的情况。
(1) GPP side. 在每个 process/control 调用之前,Input buffers必须被写回。(否则, DSP CPU/DMA 在外部存储器中访问的数据不连贯,没有写回 GPP 端缓存的能力)。如果缓冲区没有被缓存在 GPP 端,不需要写回( writeback )。
当驱动程序填充GPP缓存输入缓冲区,并在传送给 Codec Engine之前,驱动程序应该做到以下几点:
1) 开始于缓存无效缓冲区。
2) 驱动程序可以使用 DMA 或 CPU 写入,进而填充缓冲区。
3) 如果 CPU 被用来填充缓冲区,CPU 在传递缓冲区给 Codec Engine 以前必须写回。
(2) DSP side. 在每个 process/control 调用之前, Input buffers 必须无效。(否则, DSP 可以读取它的缓存区中的过期数据,如果在相同的缓冲区传入较早的调用,这是可能发生的)。注意,在调用算法的处理功能之前,VISA API 函数的默认框架自动失效输入缓冲区。
2. Output Buffers.
(1) GPP side. 遵循 DSP-side 过程,在 GPP 端访问他们, Output buffers 必须无效。(否则, GPP 可能读取它的 GPP-side 缓存区中的过期数据)。注意,在调用算法的处理功能之前,VISA API 函数的默认框架自动失效输入缓冲区。如果缓冲区没有被缓存在 GPP 端,不需要无效( invalidation )。
(2) DSP side. 在每个 process/control 调用之前, output buffers 必须无效。(否则, 如果 DMA 用于填充缓冲区,也有可能被改写为由于 CPU 不相关的活动而被驱逐的高速缓存线(cache lines)。并且,在每个 process/control 调用之后,output buffers必须被写回。(否则,GPP 可能从外部存储器读取的数据不连贯。)注意,VISA API 函数跟随每个 process/control 调用自动回写 output buffers。
3. DMA-Related. 如果 GPP 或 DSP 采用 DMA 访问共享缓冲区,为确保一致性,还有更多的工作要做。xDAIS 为框架提供了一些DMA规则。有关详细信息,参见 TMS320 DSP Algorithm Standard Rules and Guidelines (SPRU352)。
C6000 算法不能在参与 DMA 传输的外部存储器中,发出任何 CPU 读取/写入命令给缓冲区。这也适用于通过它算法接口传递算法的输入缓冲区( input buffers )。
一些常见的缓存相关的错误有:
1. DSP“输入”("before")缓冲区是回写-无效的缓存区( cache ),而不是只是一个 process/control 调用“之前”("before")的无效。在这种情况下,如果“当前”输入缓冲区的任何数据已在“以前的” process/control 调用中被引用,那么该缓冲区的陈旧片段可能已经驻留在 DSP 的高速缓存。来自缓存中的陈旧数据回写会破坏“当前”输入缓冲区。
2. 做一个盲目的 "ALL L2 Cache" 回写-无效,而不是回写或仅算法本身的输入/输出缓冲器的失效。这将为其他算法实例造成潜在问题,其输入/输出缓冲区将受到影响。
3. 无效所有的 L2,将严重地降低所有算法实例的性能,由于导致高速缓存找不到。
4.5.4.2 单核处理器环境
以下是在 DM643x 环境下的常见问题,虽然他们存在于任何利用高速缓存的单核 CPU 系统中。
1. Input Buffers. 这是当一个 DSP 应用捕获/产生( captures/generates )一个缓冲区,并把它传递给 Codec Engine 的情况。取决于如何让输入缓冲器被捕获,缓冲区必须要么无效,要么被回写和无效:
(1) 如果应用程序(或驱动程序)修改使用 CPU 读和/或写操作的输入缓冲区的内容,缓冲器必须回写和无效。
(2) 如果应用程序(或驱动程序)修改使用 DMA 的输入缓冲器的内容,然后该缓冲区必须不能被回写,但仍必须被无效。
在这两种情况下,应用程序(或驱动程序)应该开始填充一个高速缓存无效( cache-invalidated )缓冲区。
2. Output Buffers. 输出缓冲区(被 Codec Engine 填满)在被提交到 Codec Engine 填写之前必须使其失效。并且,在从 Codec Engine 返回的时候,缓冲器应该被回写,以确保所有数据被写入到外部存储器中。