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 都物尽其用,从而以较低的面积实现性能表现,同时为终端用户提供良好的体验感。






审核编辑:刘清

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

    关注

    30

    文章

    5032

    浏览量

    117743
  • ARM处理器
    +关注

    关注

    6

    文章

    346

    浏览量

    41324
  • 人工智能
    +关注

    关注

    1776

    文章

    43871

    浏览量

    230622
  • Cortex-M
    +关注

    关注

    2

    文章

    224

    浏览量

    29574

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

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

收藏 人收藏

    评论

    相关推荐

    使用UART FIFO缓冲区时,缓冲区中的数据有时会损坏的原因?

    我在使用 UART FIFO 缓冲区时遇到了以下问题。 问题描述: 当通过两个 UART 通道使用完整的 UART FIFO 缓冲区并通过两个通道进行通信时,缓冲区中的数据有时会损坏,例如,UART
    发表于 03-06 06:59

    没有办法通过FX3固件检查缓冲区是卡住了还是已满?

    我正在尝试通过多通道 DMA 缓冲区直播 1080p60 视频,但是我们遇到了一个问题,即在向 FX3 写入一定数量的缓冲区之后,我们看到当前线程 WATERMARK 和 READY 标志保持不变
    发表于 02-23 08:01

    西门子博诊断缓冲区的使用方法

    可从在线和诊断视图中“诊断”文件夹的“诊断缓冲区”组中读出 CPU 的诊断缓冲区
    的头像 发表于 12-11 10:24 1232次阅读
    西门子博诊断<b class='flag-5'>缓冲区</b>的使用方法

    使用malloc建立缓冲区时出现错误怎么解决?

    ,我不知道现在这个已知系统的中断频率,所以我做了一个偷懒的想法,先把输入数据3秒的数据放在自建的缓冲区,再将这个数据处理后再输出,延时大概3s数据的时间,现在但是使用malloc建立缓冲区时出现错误,请问,怎么拯救?
    发表于 11-30 07:39

    C++环形缓冲区设计与实现

    Buffer) 环形缓冲区(Circular Buffer),也被称为循环缓冲区(Cyclic Buffer)或者环形队列(Ring Buffer),是一种数据结构类型,它在内存中形成一个环形
    的头像 发表于 11-09 11:21 480次阅读
    C++环形<b class='flag-5'>缓冲区</b>设计与实现

    SPI串行时钟和缓冲区数据关系是什么?

    仅当存在待发送数据时串行时钟波特发生器才产生对应数据位模式的时钟脉冲吗?如果缓冲区没数据,串行时钟就一直空闲吗?
    发表于 10-17 08:20

    SPI在通信的过程中怎么实现环形缓冲区读取?

    SPI在通信的过程中怎么实现环形缓冲区读取
    发表于 10-11 08:11

    STM32进阶之串口环形缓冲区实现

    STM32进阶之串口环形缓冲区实现
    的头像 发表于 09-19 09:20 1652次阅读
    STM32进阶之串口环形<b class='flag-5'>缓冲区</b>实现

    关于NUC980的网口的发送描述符的缓冲区地址要求4字节对齐的问题

    NUC980的网口的发送描述符的缓冲区地址要求 4字节对齐的问题,因为我用的协议栈里面要求IP数据段4字节对齐,加上以太网14字节,如果发送缓冲区要求4字节对齐,带来的问题我必须要完成一次拷贝,不能
    发表于 09-04 06:44

    【Start_DSC28034PNT湖人开发板免费体验】基于循环缓冲区的串口驱动

    前言 前面进行了串口的收发测试,现在来实现基于环形缓冲区的串口驱动,以提供给应用层方便使用的收发接口。 设计 设计思路 设计的核心思想是,使用缓冲区,串口接收中断时往缓冲区写数据,串口读API则查询
    发表于 08-02 22:41

    NUC980的网口的发送描述符的缓冲区地址要求4字节对齐的问题求解

    NUC980的网口的发送描述符的缓冲区地址要求 4字节对齐的问题,因为我用的协议栈里面要求IP数据段4字节对齐,加上以太网14字节,如果发送缓冲区要求4字节对齐,带来的问题我必须要完成一次拷贝,不能把数据缓冲区直接发送 另外一个
    发表于 06-27 07:34

    NUC980的网口的发送描述符的缓冲区地址要求4字节对齐的问题求解

    NUC980的网口的发送描述符的缓冲区地址要求 4字节对齐的问题,因为我用的协议栈里面要求IP数据段4字节对齐,加上以太网14字节,如果发送缓冲区要求4字节对齐,带来的问题我必须要完成一次拷贝,不能
    发表于 06-13 07:01

    88w9098如何配置/修改套接字缓冲区大小?

    我想知道如何在驱动程序中配置套接字缓冲区大小?我正在使用 88w9098 芯片。 我正在做一些实验。我已经在内核中更改了套接字缓冲区大小,因此我可能还需要更改 Wi-Fi 驱动程序端的缓冲区大小。有人能告诉我如何配置/修改套接字
    发表于 05-31 11:00

    有没有办法检查/设置css缓冲区

    你好。有没有办法检查/设置 css 缓冲区?似乎当我添加太多 css 特性时,事情会变得不稳定。
    发表于 05-10 09:03

    如何使用Dma从Bctu Fifo1到缓冲区的数据传输?

    : 我想在一个小循环和一个大循环中传输这 6 个 fifo 条目,你看到我的配置有什么问题吗? 为了进行测试,我对 Vrefh 进行了采样,并且缓冲区具有值,例如 32727。如何解释该值,我是否需要应用任何移位或掩码
    发表于 05-06 07:08