0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

聊聊循环缓冲区的使用

Arm社区 来源:Arm社区 2024-01-04 14:10 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

我们在为 Arm Cortex-M 处理器系列设计矢量扩展 (MVE) —— Arm Helium 技术时,希望它能广泛地适用于各种数字信号处理 (DSP) 的应用。具备高效的数据计算能力只成功了一半,同样重要的是具备在内存中访问和存储这些数据的能力。

正如之前的文章内容所述,Helium 是一种四节拍矢量架构。将数据加载到矢量中的最直接的方法是连续加载操作(见图一)。在每个节拍中,都从标量寄存器中指定的基址开始依次访问内存。无论目标数据类型如何(8、16 或 32 位),都可以通过充分利用总线宽度的访问来高效地执行这一操作,因为数据元素在内存和矢量中都是相邻的,存储操作也是如此。

8ae088c2-aac7-11ee-8b88-92fbcf53809c.png

图一:连续加载操作

内存是一种稀缺资源,通常情况下,要尽可能紧凑地打包数据,使用可容纳数据的最小数据类型。不过,在处理数据时,可能需要更多的空间,以避免在计算的中间阶段出现溢出。这可以作为一个独立的拓宽指令来执行,但正如本系列第一篇文章所述,它存在时间跨越问题(对于 8 到 32 位的扩展,将数据扩展到最后一节拍需要第一节拍的数据,而第一节拍的数据已不可用)。

因此,扩展指令不能与其他指令重叠,否则会对性能产生不利影响。相反,Helium 引入了改变大小的内存操作。数据可以作为单个 8、16 或 32 位访问,针对每个节拍高效加载,并用零或符号扩展,以匹配所需的数据类型。在图二的示例中,我们希望执行将每个矢量通道的 8 位加载扩展到 16 位。两个 8 位数据样本作为一个 16 位加载操作加载,每个样本在写入矢量通道之前扩展到 16 位。同样,对于存储来说,数据可以截断到所需的大小,实现高效存储。

8af94b8c-aac7-11ee-8b88-92fbcf53809c.png

图二:加载扩展

Helium 加载和存储指令具有与 M 系列架构的其他部分相同的丰富的寻址模式集,支持预递增或后递增以及指针回写等功能。这样,在大多数情况下就不需要单独进行指针操作了。

DSP 应用通常在数据结构而非单个元素上运行。例如,立体声音频数据通常以左右数值交织流的形式存储。同样,图像数据通常以红、绿、蓝、Alpha 交错值的形式存储。这是上一篇文章的主题内容,其中介绍了可以有效实现这一目标的结构化加载/存储指令。

有时,存储在内存中的数据无法以便捷的方式构建以实现连续访问。在某些架构中,这实际上会阻碍代码的矢量化。Helium 通过“离散‑聚合”操作解决了这一问题。这些操作将偏移矢量指向内存,这样就可以用一条指令访问多个非连续地址(见图三)。它们还能扩展或截断所访问的数据。

8b103c5c-aac7-11ee-8b88-92fbcf53809c.png

图三:汇总负载

“离散-聚合”操作是一种功能强大的指令,可为应用提供很大的灵活性。例如,它们几乎是实现 FFT(快速傅里叶变换)不可或缺的工具;在这种算法中,第一个或最后一个蝴蝶阶段的内存访问需要使用反位寻址执行。专用反位指令 (VBRSR) 生成反位寻址模式,供这些离散-聚合指令使用。为了提高性能,这些指令重叠执行,加载数据的效率远远超过同等序列的标量指令。而且,它们也更容易使用。虽然连续矢量访问能为规律模式提供更好的性能,但由于离散-聚合指令无法对数据布局做出假设,因此有多少个不同的元素,汇总加载指令就必须执行多少次单独的访问。对于 8 位数据,访问次数可能多达 16 次,这就成了不断加载的负载。

DSP 应用通常使用一种称为循环寻址的内存布局。可以按顺序访问元素,但最多只能访问配置的缓冲区大小,之后的访问会绕回到第一个元素(见图四)。例如,从元素 N‑1 开始的四元素读取操作将会访问元素 N‑1, 0, 1, 2。

8b2216d4-aac7-11ee-8b88-92fbcf53809c.png

图四:循环缓冲区示例

这在 DSP 应用中用途广泛,包括在处理数据流后只需要前 N 个数据样本时避免指针操作。在 FIR 滤波器中,最后 N 个数据样本需要与一组系数相乘,才能产生所需的滤波器响应。当一个新的数据样本到来时,需要处理的是之前的 N‑1 个样本和新样本,最旧的样本不再使用。数据可以重新排列,使要处理的缓冲区总是按正确的顺序包含元素,但这需要在开始处理前将每个样本复制到不同的位置,耗费大量资源。如果使用循环缓冲区,就可以就地访问数据,必要时还可以绕回到开头,而且只需要写入一次就可以用最新的样本替换最旧的样本。

一些 DSP 通过专用访问指令和专用寄存器来实现循环缓冲区的起始地址和结束地址。指针每次递增时,硬件都会将其与结束地址进行比较,并相应地回绕。这意味着同时支持的循环缓冲区数量受到可用硬件的限制。这也意味着每次中断都需要保留大量额外状态,而这会影响延迟。为此,所需的硬件支持不容忽视;在典型的实施中,需要更复杂的地址生成单元。为了避免这种情况,一些 DSP 要求循环缓冲区的大小等于 2 的幂次方,缓冲区的地址调整为该大小的倍数。可以通过将指针与位掩码进行 AND 运算实现,从而简化硬件要求。但是,这样会限制这些缓冲区的放置和使用,特别是几乎无法直接从高级语言使用缓冲区。由于 M 系列的宗旨是让一切都能通过 C 语言轻松使用,因此我们需要想出一种更好的方法。

我们的解决方案是将循环缓冲区分成两个不同的操作,其方式与上文讨论的反位寻址类似。我们将用于生成回绕偏移的指令与离散‑聚合指令相结合来访问这些偏移地址的数据。这就为缓冲区大小和位置提供了灵活性,而且关键路径上也不需要有专用硬件。循环缓冲区生成指令 (VIWDUP) 可创建一个矢量,其中包含一连串递增的偏移量,当到达终点位置时会回绕到开头(见图五)。该指令用从 R0 值开始的序列填充矢量寄存器 Q0,并在达到 R1 值时回绕。然后,它将更新后的起始偏移量 2 写回 R0。这条指令的一个巧妙设计是,每次写入 Q0 的偏移量矢量都是由标量值重新生成的。

通常下一条指令就是使用偏移量的离散‑聚合指令,因此 Q0 可以直接重复用于其他目的。立即值指定偏移量的增量,这对于处理不同的元素大小非常有用。例如,如果加载的是 32 位数值,将使用四个字节的增量。可以指定任意增量或减量,因此该指令可用于其他需要通用数字模式的情况。通过这种方式,Helium 可以提供任意数量的循环缓冲区,在内存中具有灵活的大小、方向和对齐方式,而且这个过程只需要使用现有的硬件就可以提供序列生成指令。

8b385c6e-aac7-11ee-8b88-92fbcf53809c.png

图五:序列生成指令的操作示例

那么性能表现如何呢?虽然需要额外的偏移生成指令 (VIWDUP),但我们发现在许多情况下,可能会因为与内存访问本身重叠而隐藏了开销。在所有情况下,这一开销都小于在没有硬件支持的情况下管理回绕的计算工作量。我们之前也说过,出于性能考虑,最好使用连续访问。循环缓冲区的特别之处在于,大部分访问都是连续的,只有偶尔发生回绕时才会出现不连续。一种方法是离散-聚合指令比较偏移值,然后合并连续的访问。遗憾的是,这样做将需要大量额外的硬件,并给设计的关键部分增加许多额外的复杂性。在负载连续的情况下,离散‑聚合操作会降低性能,这违背了我们追求将每个 gate 的性能发挥到极致的原则。

当我们试图找到解决这个问题的方法时,我们注意到偏移生成指令 (VIWDUP) 已经掌握回绕点的位置。如果能将这一信息传递给离散-聚合指令,它就能将访问提升为连续访问,而无需使用昂贵又耗时的偏移比较器。那么我们能不能指定一个额外的标量寄存器来传输这些信息呢?遗憾的是,这将增加所需的读取端口数量,而且标量依赖关系从 VIWDUP 改为离散‑聚合指令将会导致指令无法重叠。Helium 实现是否可以将这些信息存储在隐藏的微架构元数据中,并在矢量发生修改时清除元数据?一般不建议这样做,因为元数据需要在中断时保留,而这会影响延迟。

但我们发现,在这种情况下,我们不需要保留元数据。在出现异常的极少数情况时,备选措施是正常执行离散-聚合,而不是优化连续访问。通过使用易失性隐藏元数据来指示连续访问,可以优化普通非回绕情况下的性能,同时避免出现额外的架构状态和中断延迟。

在受限的环境中工作极具挑战性,Helium 要求我们不断寻找创新的解决方案,充分发挥硬件性能。我们努力联合设计架构和微架构,寻找一系列内存访问指令,既能满足 DSP 应用的需要,又能最大限度地减少实现这些指令所需的硬件数量。特别是在循环缓冲区方面,我们延续了 M 系列的传统,确保每个 gate 都物尽其用,从而以较低的面积实现性能表现,同时为终端用户提供良好的体验感。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5618

    浏览量

    130386
  • ARM处理器
    +关注

    关注

    6

    文章

    361

    浏览量

    43382
  • 人工智能
    +关注

    关注

    1819

    文章

    50290

    浏览量

    266829
  • Cortex-M
    +关注

    关注

    2

    文章

    234

    浏览量

    31188

原文标题:Helium 技术讲堂 | 循环缓冲区的使用

文章出处:【微信号:Arm社区,微信公众号:Arm社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    DMA传输完成通知未在S32K324上的半满缓冲区时触发,为什么?

    S32K344发生第一次中断时(半缓冲区已满并发生中断): 第一次中断发生时S32K324: 我使用以下代码片段来配置 DMA 传输: K344 上的 TCD: TCD 对S32K324:
    发表于 04-02 08:10

    C语言的缓冲区(缓存)详解

    缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。   缓冲区根据其对应的是输入设
    发表于 01-14 07:30

    CW32L052串口的缓冲区机制

    默认缓冲区配置 CW32L052的UART模块支持硬件FIFO(通常为16字节),但HAL库或用户代码需手动管理接收缓冲区。若未显式分配足够大的软件缓冲区,可能导致数据溢出。 HAL库缓冲区
    发表于 11-24 06:40

    飞凌嵌入式ElfBoard-标准IO接口之设置缓冲区

    1.setvbuf 用于以对文件的 stdio 缓冲区进行设置,譬如缓冲区缓冲模式、缓冲区的大小、起 始地址等。 1)头文件 #include 2)函数原型 int setvbuf
    发表于 11-14 09:02

    移植的lvgl,在运行的时候,缓冲区无法释放怎么解决?

    代码在运行的时候,只有lvgl线程用于刷新,另一个线程只有一个串口打印。 当运行一段时间后,发现,程序会在LVGl中,lv_refr.c这个库下面第625行代码, 在这一直判断,看介绍说是在等待释放缓冲区,求大神给个思路
    发表于 09-09 07:28

    如何清除CYUSB3014的缓冲区数据?USB接口数据什么时候发送到电脑?

    、如何清除GPIF II接口处对应的DMA BUFFER数据?当标志设置为满/非满状态时,一旦 DMA 缓冲区已满,标志信号就会指示它已满。清除DMA缓冲区数据后,相应的标志信号会改变吗?会不会变成非满状态
    发表于 07-18 07:58

    USB缓冲区中的内容满了之后,是否有标志位进行反馈?

    USB缓冲区中的内容满了之后,是否有标志位进行反馈。
    发表于 07-17 07:13

    请问USB缓冲区取数据可以多次取吗?

    在使用USB软件获取数据是,下位机给我发送了13个32位数据到USB IN缓冲区,为什么我调用API函数想要第一次取1个32位数据,取完之后再取12位数据,程序会卡死。
    发表于 07-16 08:12

    socket缓冲区溢出的原因?怎么解决?

    我在测试视频通话时 发现丢帧特别严重 进行了一些列的排查 发现socket本身似乎有问题 通过测试代码发现了大量的缓冲区溢出我尝试换了不同的服务器 我还分别测试了wifi网卡和4G网卡 全都这样
    发表于 06-19 06:34

    解析RZ/N2L CANFD模块的缓冲区机制(2)

    在工业自动化、智能交通、机器人等领域,CANFD(CAN with Flexible Data-Rate)技术正逐步取代传统CAN,以适应更高的数据速率和更复杂的通信需求。本文将深入解析RZ/N2L CANFD模块的缓冲区机制,帮助工程师更高效地管理CAN消息,提高系统性能。
    的头像 发表于 05-19 14:13 1578次阅读
    解析RZ/N2L CANFD模块的<b class='flag-5'>缓冲区</b>机制(2)

    解析RZ/N2L CANFD模块的缓冲区机制(1)

    在工业自动化、智能交通、机器人等领域,CANFD(CAN with Flexible Data-Rate)技术正逐步取代传统CAN,以适应更高的数据速率和更复杂的通信需求。本文将深入解析RZ/N2L CANFD模块的缓冲区机制,帮助工程师更高效地管理CAN消息,提高系统性能。
    的头像 发表于 05-19 14:10 1426次阅读
    解析RZ/N2L CANFD模块的<b class='flag-5'>缓冲区</b>机制(1)

    FX3 Socket缓冲区切换的最大时间是多少?

    FX3_Programmers_Manual 文档的第 10 章提到“每个缓冲区缓冲区切换开销为 550 - 900 ns”。 Getting_Started_with_EZ-USB_FX3 文档
    发表于 05-16 07:51

    在传输DMA通道中的所有缓冲区后,DMA标志(就绪和部分)被卡住了是怎么回事?

    是,旗帜最初的表现是正确的。 它们被配置为 ACTIVE HIGH 标志,初始值设为 LOW。 整个 DMA 通道默认使用两个 DMA 缓冲区。 传输开始时,第一个缓冲区被正确填满:部分标志(标志 b
    发表于 05-16 07:18

    DMA缓冲区设置为48K,如果没有写满48K,数据会自动被发送出去吗?

    请问我的DMA缓冲区设置为48K,如果没有写满48K,数据会自动被发送出去吗。2.有没有清空DMA缓冲区的函数,我直接调用CyU3PUsbFlushEp()不起作用。
    发表于 05-09 06:21

    求助,关于3014的缓冲区设置疑问求解

    rgb24 1080p@60fps是静态图像,dma缓冲区的配置即大小和数量分别为16kb,6,沿用的是yuy2的配置,但yuy2 1080p@60fps 是动态且不颠倒,是否可以认为是由于缓冲区
    发表于 05-06 13:42