如果您的 MCU 应用程序需要处理数字音频,请考虑采用多线程方法。使用多线程设计方法使设计人员能够以直接的方式重用他们的设计部分。
多核和多线程是设计实时系统的有效方法。使用这些技术,系统被设计为许多任务的集合,这些任务独立运行并在需要时相互通信。将系统设计从大型单片代码块分解为更易于管理的任务,可以大大简化系统设计并加快产品开发。因此,整个系统的实时属性更容易理解。设计者只需要担心每个任务实现的保真度,提出诸如“网络协议是否正确实现?”之类的问题。
在本文中,我们将讨论如何使用多线程或多核设计方法来设计对数据流进行操作的实时系统,例如数字音频系统。我们使用几个数字音频系统来说明设计方法,包括异步 USB 音频 2、以太网 AVB 和 MP3 播放器的数字基座。在展示如何有效地使用多核和多线程来设计所需的缓冲和时钟方案之前,我们将简要讨论数字音频、多核和多线程的概念。
数字音频
在许多消费市场中,数字音频已经取代了模拟音频,原因有二。首先,大多数音频源都是数字的。无论是以有损压缩格式 (MP3) 还是以非压缩格式 (CD) 交付,数字标准已经取代了传统的模拟标准,例如磁带和磁带。其次,数字音频比模拟音频更容易处理。数据可以通过现有标准(例如 IP 或 USB)传输而不会丢失,并且硬件设计不需要任何“魔法”来降低本底噪声。就数字路径而言,本底噪声是恒定的,不受移动电话可能引起的 TDMA 噪声的影响。
数字音频系统对样本流进行操作。每个样本代表一个或多个音频通道在某个时间点的幅度,样本之间的时间由采样率控制。CD 标准有两个通道(左声道和右声道)并使用 44.1 kHz 的采样率。常见的音频标准使用 2、6 (5.1) 和 8 (7.1) 通道,以及 44.1 kHz、48 kHz 或倍数的采样率。我们使用 48 kHz 作为运行示例,但这绝不是唯一的标准。
多核和多线程
在多线程设计方法中,系统被表示为并发任务的集合。使用并发任务,而不是单一的单一程序,有几个优点:
多任务是支持关注点分离的好方法,这是软件工程最重要的方面之一。关注点分离意味着设计的不同任务可以单独设计、实现、测试和验证。一旦指定了任务之间的交互,团队或个人就可以各自完成自己的任务。
并发任务提供了一个简单的框架来指定系统应该做什么。例如,数字音频系统将播放通过网络接口接收的音频样本。换句话说,系统应该同时执行两项任务:从网络接口接收数据并在其音频接口上播放样本。将这两个任务表示为单个顺序任务是令人困惑的。
表示为并发任务集合的系统可以通过一个或多个多线程内核中的线程集合来实现(参见图 1)。我们假设线程是在指令级调度的,就像XMOS XCore 处理器上的情况一样,因为这使得并发任务能够实时运行。请注意,这与 Linux 上的多线程不同,例如,线程被调度在具有上下文切换的单处理器上。这可能使这些线程对人类而言似乎是并发的,但对一组实时设备而言却不是。
并发任务在逻辑上设计为通过消息传递进行通信,当两个任务由两个线程实现时,它们通过发送数据和控制通道进行通信。在内核内部,通道通信由内核本身执行,当线程位于不同的内核上时,通道通信通过交换机执行(参见图 2)。
多线程设计已被嵌入式系统设计人员使用了数十年。为了实现嵌入式系统,系统设计人员过去常常使用大量微控制器。例如,在音乐播放器内部,可能会发现三个控制闪存、DAC 和 MP3 解码器芯片的微控制器。
图 1:线程、通道、内核、交换机和链接。并发线程通过内核内部、芯片上的内核之间或不同芯片上的内核之间的通道进行通信。
我们认为,现代多线程环境可以替代这种设计策略。单个多线程芯片可以替代多个 MCU,并提供任务之间的集成通信模型。该系统不必在单独的 MCU 上的任务之间实现定制通信,而是作为一组通过通道进行通信的线程来实现。
使用多线程设计方法使设计人员能够以直接的方式重用他们的设计部分。在传统的软件工程中,功能和模块结合起来执行复杂的任务。但是,这种方法不一定适用于实时环境,因为依次执行两个函数可能会破坏函数或模块的实时性要求。
在理想的多线程环境中,实时任务的组合是微不足道的,因为它只是为每个新的实时任务添加一个线程(或核心)的情况。实际上,设计人员会对内核数量有所限制(例如,出于经济原因),因此必须决定哪些任务将组成并发线程,以及哪些任务将作为集合集成到单个线程中的功能。
多线程数字音频
一个数字音频系统很容易拆分为多个线程,包括一个网络协议栈线程、一个时钟恢复线程、一个音频传输线程,以及可选的用于 DSP、设备升级和驱动程序认证的线程。网络协议栈可以像以太网/IP 栈一样复杂并包含多个并发任务,或者像 S/PDIF 接收器一样简单。
图 2:具有 24 个并发线程的三核系统的物理化身。顶部设备有两个核心,底部设备有一个核心。
我们假设系统中的线程通过通道发送数据样本进行通信。在这种设计方法中,线程是在单核还是多核系统上执行并不重要,因为多核只是为设计增加了可扩展性。我们假设每个线程的计算要求可以静态建立并且不依赖于数据,这通常是未压缩音频的情况。
我们将把注意力集中在设计的两个部分:线程之间的缓冲(以及它们对性能的影响)和时钟恢复。一旦做出了这些设计决策,实现每个线程的内部就遵循正常的软件工程原则,并且与人们预期的一样难或容易。缓冲和时钟恢复很有趣,因为它们都对用户体验有定性影响(促进稳定的低延迟音频),并且在多线程编程环境中很容易理解。
缓冲
在数字解决方案中,数据样本不一定在交付时进行传输。这需要缓冲数字音频。例如,考虑一个采样率为 48 kHz 的 USB 2.0 扬声器。USB 层将在每 125 µs 窗口中传输六个样本的突发。无法保证在 125 µs 的窗口中将传送六个样本,因此需要至少 12 个样本的缓冲区,以保证样本可以实时流式传输到扬声器。
设计挑战是建立适量的缓冲。在模拟系统中,缓冲不是问题。信号按时传递。在基于非实时操作系统设计的数字系统中,程序员通常坚持使用相当大的缓冲区(250 或 1,000 个样本)以应对调度策略中的不确定性。然而,大缓冲区在内存方面、增加延迟方面以及证明它们足够大以保证无点击交付方面都是昂贵的。
多线程设计提供了一个很好的框架来非正式地和正式地推理缓冲并避免不必要的大缓冲区。例如,考虑上述 USB 扬声器增加了环境噪声校正系统。该系统将包括以下线程:
通过网络接收 USB 样本的线程。
过滤样本流的一系列 10 个或更多线程,每个线程都有一组不同的系数。
使用 I 2 S将过滤后的输出样本传送到立体声编解码器的线程。
从连接到麦克风采样环境噪声的编解码器中读取样本的线程。
将环境噪声二次采样到 8 kHz 采样率的线程。
建立环境噪声频谱特性的线程。
根据计算的光谱特性更改滤波器系数的线程。
所有线程都将在 48 kHz 基本周期的某个倍数上运行。例如,每个过滤线程将每 48 kHz 周期过滤一个样本;交付线程将在每个周期交付一个样本。每个线程也有一个定义的窗口,它在上面操作,以及一个定义的方法,通过这个方法推进这个窗口。例如,如果我们的过滤器线程是使用双二阶实现的,它将在一个包含三个样本的窗口上运行,每个周期提前一个样本。频谱线程可以在每 64 个样本推进 64 个样本的 256 个样本窗口(以执行 FFT(Fest Fourier Transform))上运行。
现在可以建立在同一周期运行的系统的所有部分,并将它们以同步部分的形式连接在一起。在这些同步部分内不需要缓冲区,尽管如果线程要在管道中运行,则需要单个缓冲区。在各个同步部分之间需要缓冲区。在我们的示例中,我们最终得到三个部分:
从 USB 接收样本、过滤并以 48 kHz 传输的部分。
以 48 kHz 采样环境噪声并以 8 kHz 传输的部分。
建立频谱特性并在 125 Hz 时更改滤波器设置的部分。
这三个部分如图 3 所示。从 USB 缓冲区接收样本的第一部分需要缓冲 12 个立体声样本。
图 3:根据频率分组在一起的线程。
传递的部分需要缓冲一个立体声样本。将 10 个过滤器线程作为管道运行需要 11 个缓冲区。这意味着从接收器到编解码器的总延迟包括 24 个采样时间,即 500 µs,并且可以添加一个额外的采样以应对时钟恢复算法中的中期抖动。这部分以 48 kHz 运行。
对环境噪声进行采样的第二部分需要在输入端存储一个样本,并在二次采样中存储六个样本。因此,在 48 kHz 或 145 µs 处有 7 个样本延迟。
建立频谱特性的第三部分需要以 8 kHz 的采样率存储 256 个样本。不需要其他缓冲区。因此,环境噪声和滤波器校正之间的延迟为 8 kHz 下的 256 个样本,二次采样时间为 145 µs,或刚好超过 32 ms。请注意,这些是我们选择使用的算法的最小缓冲区大小;如果此延迟不可接受,则必须选择不同的算法。
设计线程以对数据块而不是单个样本进行操作通常很容易,但这会增加所经历的整体延迟,增加内存需求并增加复杂性。仅当有明显的好处时才应考虑这一点,例如增加吞吐量。
计时数字音频
数字音频和模拟音频之间的一个很大区别在于,模拟音频基于此基础采样率,而数字音频需要将时钟信号分配给系统的所有部分。尽管组件都可以使用不同的采样率(例如,系统的某些部分可能使用 48 kHz,而其他一些部分可能使用 96 kHz,中间有一个采样率转换器),所有组件都应就一秒的长度达成一致,并且因此在测量频率的基础上达成一致。
数字音频的一个有趣特性是系统内的所有线程都与这个时钟频率的基数无关,假设有一个黄金标准的基频。系统中的多个核心是否使用不同的晶体并不重要,只要它们对样本进行操作即可。然而,在系统的边缘,真正的时钟频率很重要,采样在途中产生的延迟也很重要。
在多线程环境中,将留出一个线程来明确测量真实时钟频率,实施时钟恢复算法,测量本地时钟与全局时钟,并在时钟偏移上与主时钟达成一致。
可以使用互连的底层比特率隐含地测量时钟,例如 S/PDIF 或 ADAT。测量其中任何一个网络上的每秒比特数将给出主时钟的测量值。时钟可以通过使用为此目的而设计的协议来明确测量,例如以太网上的 PTP。
在时钟恢复线程中,可以实现一个控制循环,它估计时钟频率,并根据观察到的误差进行调整。在最简单的形式中,误差用作调整频率的指标,但滤波器可用于减少抖动。该软件线程实现了传统上由 PLL 但在软件中执行的功能,因此它可以廉价地适应环境。
结论
多线程开发方法使数字音频系统能够使用分而治之的方法进行开发,其中一个问题被分成一组并发任务,每个任务在多线程内核上的单独线程中执行。
像许多实时系统一样,数字音频适合多线程设计方法,因为数字音频系统显然由一组处理数据的任务组成,并且还需要这些任务同时执
|
||||||||||||
|
||||||||||||