【问题标题】:Tricky queue use between two CPUs两个 CPU 之间的棘手队列使用
【发布时间】:2017-03-22 06:55:30
【问题描述】:

悬崖笔记版 TI F28377S 有两个 CPU,一个主 CPU 和一个辅助 CPU(CLA,一次只能执行一项任务,任务不中断)——它们共享 RAM 的消息区域。当快速向 CLA 发出的大约 15 个字节(最大 32 个队列长度)的队列提供数据时,有时几个字节将永远不会被传输。我认为 CPU 中断存在一些问题,导致单个字节在将它们交给缓冲区时偶尔会“丢失”。

完整版 (这里使用的是 TI F28377S,其主 CPU 主频为 200 MHz,辅助独立 CLA 以相同速度运行,但一次只能执行一项任务。它们可以共享单向可写变量)。

我对如何执行这个涉及 CLA 和队列的更复杂的任务感到有些困惑。

一些快速背景知识:我有两个主要的 CLA 任务,第一个 (Task1) 由 ADC 转换结束触发(它本身由 Timer0 在 100 kHz 触发),第二个 (Task2) 由 Timer0 本身触发(这是经过大量实验和调整后得出的,因为每当我让 Task2 比 ADC 任务运行得更频繁时,ADC 任务永远不会启动 - 所以我将它们设置为使用相同的间隔,只是交错)。 Task1 完美运行,将 ADC 结果存储在一个简单的环形缓冲区中,并在 Task1 完成后 ISR 中执行简单的计算。第二个主要工作。

Task2 用于切换一些 GPIO 引脚以与外部设备通信。因为代码的总长度大约为 100 微秒,所以我没有延迟,而是在每个触发器上使用一个简单的 case 结构来确定它是否应该:什么都不做,打开代码引脚,打开频闪引脚,关闭频闪引脚,关闭代码引脚。这样每次调用任务时,它几乎立即完成,输出代码的长度适合外部设备。该任务每次处理一个代码,一旦完成,就会尝试从队列中获取另一个代码。如果没有,它会继续直接通过。

现在,棘手的部分。我有两个要求:1)我可以比任务消耗它们更快地向队列末尾添加字节(在理论上和实践中都非常容易)和 2)我可以在队列的前面添加一个字节(不是替换当前正在传输的字节,就在队列的前面)。第一个能力是发送中短消息(2-20 个字符)。这第二种能力对于发送有关任何外部中断的单个字节是必要的 - 尽可能快,甚至在传输消息的过程中也是如此。我已将其设置为任务每 500 微秒发送 1 个字节(约 300 个“开”和约 200 个“关”)。这样,如果有中断消息进来,将保证收到少于 1 个发生后的毫秒数。

目前的工作是:CPU 上的一个函数,它接收传入的字节(一次一个)并将它们添加到 CPU2CLA 缓冲区并增加 CPU2CLA 长度计数器。每次运行 Task2 时,它都会检查此队列并从 CLAonly 缓冲区的前面获取一个字节,增加它自己的缓冲区长度,并标记一个字节已被消耗。当 Task2 任务后 ISR 运行时,它会检查一个字节是否被消耗,并从 CPU2CLA 缓冲区中删除第一个字节。目前这个双缓冲系统没有添加到前面的标志,所以它不处理中断情况。

我之前尝试的是创建一个 Task3,它占用一个通过 CPU2CLA 的字节,并使用 Task3andWait 从 CPU 运行它。虽然这种方法理论上应该同时满足这两个要求,但大约有一半的时间,一两个字节的消息永远不会被传输(一个字节总是被发送)。

CLA 任务永远不会被中断,但 CPU 任务可以。这就是为什么我试图让队列的所有修改只发生在 CLA 中,这样就永远不会有可能中断队列修改的不确定状态。

【问题讨论】:

  • TL:博士。如果您“可以比任务消耗它们更快的速度将字节添加到队列末尾”,您将需要阻止输入。如果这是需要您将字节发布到队列前面的基本问题,那么您肯定会对传入的字节做出值判断:因此,您的函数执行的处理超出了应有的处理,因此是瓶颈。
  • 队列现在只有 32 字节长(已修复)。我的传入函数会简单检查长度是否 == max_length,退出(即删除字节)。
  • 那么你在什么前提下将字节发布到队列的前面?数据的必要性如何?你能承受失去它吗?如果是这样,当您无法足够快地处理传入数据时,只需重置缓冲区。
  • 相同的功能(带有用于前/后的布尔标志)。如果已满,请忽略。如果不是,则将所有字节移回 1 位,并将新字节放在前面。我应该补充一点,到目前为止,测试用例从未超过 15 个字节,并且总是附加到末尾。
  • 请用不到 100 个字来定义您的问题,并以示例 Minimal, Complete, and Verifiable example 说明问题。

标签: c queue embedded message-queue


【解决方案1】:

听起来,将高优先级和普通优先级项目拆分为单独的缓冲区将是一个近乎最佳的解决方案。

它还可以确保如果在消耗任何东西之前生产了一个高优先级项目、一个普通优先级项目和另一个高优先级项目,那么高优先级项目将在正常优先级项目之前被消耗。

(使用单个缓冲区,这种情况会导致在第二个高优先级项目之前消耗正常优先级项目。我怀疑这是非常不可取的。)

如果高优先级缓冲区中有一个项目,则接下来会消耗该项目。否则,将消耗正常优先级缓冲区中的项目。

两个缓冲区都有一个生产者和一个消费者(因此,SPSC 类型),并以简单的先进先出方式处理;因此,无锁循环缓冲区实现(针对每个缓冲区)在这里应该可以正常工作。

(如果两个缓冲区只有 32 个字节可用,请考虑先尝试 8:24 拆分。)

【讨论】:

  • 我喜欢这个。我没有考虑两个队列,但这完全解决了在开始时插入一个项目的问题。我试试看。
  • 我要补充一点,我必须弄清楚如何让一个正常的循环缓冲区在两个内存“域”之间工作。 (CPU 和 CLA 可以对任何单个变量进行 RW 或 R 访问,但这是在编译之前定义的。因此生产者和消费者都不可能同时更改长度)。
  • @AdamJones: 如果next是下一个要消耗的字节的索引,last是要消耗的最后一个字节的索引(都是模缓冲区长度),那么只有生产者写信给next,消费者写信给last
  • 这就是问题所在。消费者(在这种情况下为 CLA)永远不能写入 CPU 拥有的变量。虽然 CLA 代码可以用 C 编写,但它不能使用为 CPU 编译的库——所以我不能使用我目前正在使用的环形缓冲区库——我必须提取一些函数和重写它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-04
  • 2011-07-09
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 2011-10-03
相关资源
最近更新 更多