一站式电子元器件采购平台

华强商城公众号

一站式电子元器件采购平台

元器件移动商城,随时随地采购

华强商城M站

元器件移动商城,随时随地采购

半导体行业观察第一站!

芯八哥公众号

半导体行业观察第一站!

专注电子产业链,坚持深度原创

华强微电子公众号

专注电子产业链,
坚持深度原创

电子元器件原材料采购信息平台

华强电子网公众号

电子元器件原材料采购
信息平台

在硬件平台上实现有限脉冲响应(FIR)滤波器算法的过程

来源:analog 发布时间:2023-09-26

摘要: 到目前为止,我们已经描述了DSP处理器的物理架构,解释了DSP如何比传统电路提供一些优势,并检查了数字滤波,展示了DSP的可编程特性如何使其适合这种算法。现在我们看一下在硬件平台ADSP-2181 EZ-Kit litetm上实现有限脉冲响应(FIR)滤波器算法的过程(在第2部分中简要介绍,用ADSP-2100系列汇编代码实现)。该实现被扩展为处理数据I/O问题。

到目前为止,我们已经描述了DSP处理器的物理架构,解释了DSP如何比传统电路提供一些优势,并检查了数字滤波,展示了DSP的可编程特性如何使其适合这种算法。现在我们看一下在硬件平台ADSP-2181 EZ-Kit litetm上实现有限脉冲响应(FIR)滤波器算法的过程(在第2部分中简要介绍,用ADSP-2100系列汇编代码实现)。该实现被扩展为处理数据I/O问题。

使用数字滤波器

DSP的许多架构特性,例如执行零开销循环的能力,以及在单个处理器周期中获取两个数据值的能力,将有助于实现该滤波器。简要回顾一下,FIR滤波器是一种全零滤波器,它是通过将输入数据点序列与滤波器系数进行卷积来计算的。其控制方程和直接形式表示如图1所示。


图1所示 直接形式FIR滤波器结构

在这个结构中,每个“z(-1)”方框表示输入数据在z变换表示法中的单个增量。将每个连续延迟的样本乘以相应的系数值h(m),将结果加在一起,生成一个单独的值,表示第n个输入样本对应的输出。延迟元件或滤波器抽头的数量及其系数值决定了滤波器的性能。

滤波器结构给出了在DSP上计算实现该算法所需的物理元素。对于计算本身,每个输出样本需要若干次相乘累加操作,这些操作等于过滤器的长度。

输入数据的延迟线和系数值列表需要在DSP中预留内存区域来存储数据值和系数。DSP的增强型哈佛架构允许程序员将数据存储在程序内存和数据内存中,从而在DSP的内部SRAM的每个周期中执行两次同步内存访问。数据存储器保存传入的样本,程序存储器存储系数值,数据值和系数值都可以在一个计算周期内获取。

这种DSP体系结构有利于使用循环缓冲的程序(在第2部分和本文后面的部分中进行了简要讨论)。这意味着地址指针只需要在程序开始时初始化,而循环缓冲机制确保指针不会离开其分配的内存缓冲区的边界——这一功能在FIR滤波器代码中广泛用于输入延迟线和系数。一旦确定了程序的要素,下一步就是开发DSP源代码来实现算法。

DSP软件开发

ADSP-2100系列的软件开发流程包括以下几个步骤:体系结构描述、源代码生成、软件验证(调试)和硬件实现。图2显示了一个典型的开发周期。


图2 软件开发流程

体系结构描述:首先,用户创建运行算法的硬件系统的软件描述。系统描述文件包括系统中的所有可用内存和任何内存映射的外部外设。下面是使用ADSP-2181 EZ-Kit Lite的此过程示例。

源代码生成:从理论到实践,这一步——将算法思想转化为在dsp上运行的代码——通常是整个过程中最耗时的一步。有几种方法可以生成源代码。有些程序员更喜欢用C等高级语言编写算法;其他人更喜欢使用处理器的本机汇编语言。对于程序员来说,用C语言实现可能更快,但是编译后的DSP代码由于没有充分利用处理器的架构而缺乏效率。

汇编代码通过充分利用处理器的设计,产生高效的实现。但是程序员需要熟悉处理器的本机汇编语言。最有效的方法是将C语言用于高级程序控制功能,将汇编代码用于系统中时间紧迫、数学密集型的部分。在任何情况下,程序员都必须了解处理器的系统约束和外设细节。本文中的FIR滤波器系统示例使用ADSP-2100系列的本地汇编语言。

软件验证(“调试”):此阶段使用称为模拟器的软件工具测试代码生成的结果,以检查程序的逻辑流程并验证算法是否按预期执行。模拟器是DSP处理器的一个模型,它a)提供对所有存储器位置和处理器寄存器的可见性,b)允许用户连续运行DSP代码或一次运行一条指令,c)可以模拟向处理器提供数据的外部设备。

硬件实现:这里的代码是在真正的DSP上运行的,通常分为几个阶段:a)在评估平台(如EZ-Kit Lite)上试用;b)电路仿真,c)生产ROM生成。试用提供了程序运行的快速进行/不进行的决定;该技术是本文中使用的实现方法。电路仿真监控系统中的软件调试,其中EZ-ICE 等工具控制目标平台上的处理器操作。在所有调试完成后,可以生成包含最终代码的引导ROM;它作为最终的生产实现。

使用ADSP-2181 EZ-Kit Lite

我们的开发周期示例通过该过程,使用ADSP-2181 EZ-Kit Lite(开发包ads -21xx- EZLITE)作为滤波器算法的目标硬件。EZ-Kit Lite是一个低成本的演示和开发平台,由一个33 mhz的ADSP-2181处理器、一个AD1847立体声音频编解码器和一个插入式EPROM组成,其中包含用于通过RS-232连接将长新算法下载到DSP的监控代码(图3)。


图3 EZ-Kit Lite板布局图

要完成架构描述阶段,需要了解DSP可用的内存和内存映射外设。程序员将这些信息存储在系统描述文件中,以便开发工具软件可以为目标系统生成适当的代码。EZ-Kit Lite不需要DSP外部的存储器,因为可用的片上存储器由ADSP-2181的16,384个位置的程序存储器(PM) SRAM和16,352个位置的数据存储器(DM) SRAM组成。(用于系统控制寄存器的32个DM位置不能用于工作代码)。有关ADSP-2181、EZ-Kit Lite架构和相关主题的更多信息,可以在本文末尾提到的文本中找到。

可用的系统资源信息记录在系统描述文件中,供ADSP-2100系列开发工具使用。系统描述文件的扩展名为. sys。下面的列表显示了一个系统描述文件[EZKIT_LT]。系统:

系统的EZ_LITE;/*给这个系统一个名字*/

.adsp2181;/*指定处理器*/

.mmap0;/*指定系统启动,*/;

/* PM位置0在内存中*/

.seg / PM / RAM / ABS = 0 /代码/数据int_pm [16384];

.seg / DM / RAM / ABS = 0 int_dm [16352];

.endsys;/*结束描述*/

清单将PM的16,384个位置声明为RAM,从地址0开始,以便将代码段和数据值都放置在那里。还声明了16,352个可用的数据内存位置作为RAM,从地址0开始。由于这些处理器使用具有两个不同内存空间的哈佛体系结构,所以PM地址0与DM地址0不同。ADSP-2181 EZ-Kit Lite的编解码器使用串口连接到DSP,该串口未在系统描述文件中声明。为了使系统描述文件对其他软件工具可用,system Builder实用程序BLD21将。sys文件转换为体系结构或。ach文件。系统生成器的输出是一个名为EZKIT_LT.ACH的文件。

编写完代码后,下一步就是生成可执行文件,即将代码转化为DSP可以执行的指令。首先组装DSP代码。这将程序文件转换为其他开发工具可以处理的格式。汇编还检查代码的语法错误。接下来,链接代码来生成DSP可执行文件,使用架构文件中声明的可用内存。链接器将源代码中的所有代码和数据装入内存空间;输出为DSP可执行文件,可下载到EZ-Kit Lite板上。

生成过滤代码

本系列的第2部分[对话31-2,第14页,图6]介绍了FIR滤波器的小汇编代码清单。这里,该代码被增强为包含一些EZ-Kit lite特定的功能,特别是编解码器初始化和数据I/O。过滤器算法的核心元素(乘法累加、对数据和系数使用循环缓冲区进行数据寻址以及依赖于零开销循环的效率)没有改变。

输入数据将使用板载AD1847编解码器进行采样,该编解码器具有可编程采样率、输入增益、输出衰减、输入选择和输入混音。它的可编程特性使系统具有灵活性,但也增加了对DSP系统进行初始化编程的任务。

访问数据

对于本例,编解码器的一系列控制字(在清单第一部分的程序开始处定义)将初始化它为8 khz采样率,每个输入通道具有中等增益值。由于AD1847是可编程的,用户通常会重用接口和初始化代码段,仅为不同的应用程序更改特定的寄存器值。这个例子将特定的过滤器段添加到EZ-Kit Lite软件中现有的代码段中。

该接口代码声明内存中的两个区域用于数据I/O:“tx_buf”用于从编解码器传输的数据,“rx_buf”用于接收传入数据。这些内存区域或缓冲区中的每一个都包含三个元素:一个控制字或状态字、左通道数据和右通道数据。对于每个采样周期,DSP将从编解码器接收一个状态字、左通道数据和右通道数据。在每个采样周期,DSP必须向编解码器提供一个发送控制字、左信道数据和右信道数据。在这个应用程序中,发送到编解码器的控制信息不会被改变,因此发送数据缓冲区中的第一个字将保持原样。我们将假设源是单声道麦克风,使用右通道(不关心左通道输入数据)。

使用EZ-Kit Lite软件中的I/O shell程序,我们只需要涉及标记为“input_samples”的代码部分。当从编解码器接收到准备处理的新数据时,访问这部分代码。如果只需要正确的通道数据,我们需要读取位于位置rx_buf + 2的数据内存中的数据,并将其放入数据寄存器中,以便输入到过滤器程序中。

来自编解码器的数据需要使用ADSP-2181的循环缓冲能力,通过输入延迟线馈送到滤波器算法中。输入延迟线的长度由滤波器所用系数的数目决定。因为数据缓冲区是循环的,所以缓冲区中最老的数据值将是指针在最后一次过滤器访问后所指向的位置(图4)。同样地,每次通过过滤器总是以相同的顺序访问的系数被放置在程序内存中的一个循环缓冲区中。


图4 使用圆形缓冲器过滤数据输入的例子

算法代码

要对接收到的数据进行操作,可以使用上一期文章中发布的代码部分,只需进行少量修改。为了实现这个过滤器,我们需要使用乘法/累加(MAC)计算单元和数据地址生成器。

ADSP-2181的MAC将结果存储在一个40位寄存器中(32位用于2个16位字的乘积,8位用于允许总和扩展而不会溢出)。这允许中间过滤器值根据需要增长和缩小,而不会损坏数据。所使用的代码段是通用的(即,可以用于任何长度过滤器);所以MAC的额外输出位允许运行带有未知数据的任意过滤器,而不用担心丢失数据。

为了实现FIR滤波器,对每个数据点的滤波器的所有轻击重复乘法/累加操作。为了做到这一点(并为下一个数据点做好准备),MAC指令以循环的形式编写。ADSP-21xx的零开销循环能力允许MAC指令重复指定数量的计数,而无需编程干预。计数器设置为轻击数减一,循环机制自动减少每个循环操作的计数器。将循环计数器设置为“taps-1”确保数据指针在执行完成后在正确的位置结束,并允许最后的MAC操作包括舍入。由于AD1847是一个16位编解码器,带舍入的MAC提供了一个统计无偏的结果,舍入到最接近的16位值。这个最终结果被写入编解码器。

为了优化代码执行,每个指令周期都应该执行一次有意义的数学计算。ADSP-21xxs通过多功能指令实现了这一点:处理器可以在同一指令周期内执行多个功能。对于FIR滤波器代码,每个乘法累加(MAC)操作可以与两个数据访问并行执行,一个来自数据存储器,一个来自程序存储器。这个功能意味着在每个循环迭代中都执行一个MAC操作。同时,获取下一个数据值和系数,计数器自动递减。所有这些都不需要浪费时间来维护循环。

当对每个输入数据样本执行过滤器代码时,MAC循环的输出将被写入输出数据缓冲区tx_buf。虽然这个程序只处理单通道输入数据,但是通过写入内存缓冲区地址tx_buf+1和tx_buf+2,结果将被写入两个通道。

最后的源代码清单见第15页。过滤器算法本身列在“中断服务例程”下。其余的代码用于编解码器和DSP初始化以及中断服务例程定义。这些主题将在本系列的后续文章中进行探讨。

EZ-Kit Lite

EZ-Kit Lite提供的基于windows的监控软件可以将可执行文件加载到EZ-Kit Lite板上的ADSP-2181中。这是通过选择“下载用户程序并运行”(图5)下拉“Lo long”菜单完成的。这将下载滤波器程序到ADSP-2181并开始程序执行。


图5 EZ-Kit Lite下载菜单

复习和预习

本文的目标是概述从算法描述到可以在硬件开发平台上运行的DSP可执行程序的步骤。介绍的问题包括软件开发流程、体系结构描述、源代码生成、数据I/O和EZ-Kit Lite硬件平台。

与这些主题相关的细节层次很多,这篇简短的文章无法公正地描述。下面的参考资料提供了进一步的信息。本系列将继续在此应用程序的基础上添加其他主题。下一篇文章将通过处理器中断结构更详细地研究数据输入/输出(I/O)问题,并讨论简单过滤器算法的其他特性。

FIR过滤器代码清单的EZ-Kit Lite

/**************************************************************



* hello81。DSP模板文件用于2181 ez-kit lite板



*这个示例程序被组织成以下几个部分:



*组装时间常数(system.h)

*中断向量表

* adsp2181初始化(init1847.dsp)

* ADSP 1847编解码器初始化(init1847.dsp)

*中断服务程序



*该程序使用AD1847编解码器实现简单的“通话”。

*初始化例程已放入init1847。dsp文件。这

*文件包含中断向量表、主“虚拟”循环和

*中断服务程序为按钮和串口0接收。

*按钮(IRQE)导致EZ-Kit板上的LED切换

*按下每个按钮。



*控制采样率、增益等参数都包含在

*文件init1847.dsp。串口0用于与AD1847通信。

发送中断用于配置编解码器,然后它们是

*被禁用,接收中断被用来实现“通话”

*音频。



内存映射控制寄存器的定义包含在

*文件:system.h



*应用程序可以通过以下方式构建:



* asm21 -c -l -2181 hello81

* asm21 -c -l -2181 init1847

* ld21 hello81 init1847 -a 2181 -e hello81 -g -x



**********************************************************/

.module / RAM / ABS = 0 EzHello;

# include

#定义水龙头255 /*过滤器水龙头长度*/

.var / dm /保监会filt_data(水龙头);/*输入数据缓冲区*/

.var /下午/保监会filt_coeffs(水龙头);/*系数缓冲区*/

.init filt_coeffs:;/*初始化系数*/

.external rx_buf, tx_buf;

.external init_cmd, stat_flag;

.external next_cmd, init_1847, init_system_regs, init_sport0;

/**********************************************************

*中断向量表

**********************************************************/

启动;rti;rti;rti;/* 00:复位*/

rti;rti;rti;rti;/* 04: irq2 */

rti;rti;rti;rti;/* 08: irql1 */

rti;rti;rti;rti;/* 0c: IRQL0 */

Ar = dm(stat_flag);/* 10: SPORT0 tx */

Ar = pass Ar;

If = rti;

跳next_cmd;

跳input_samples;/* 14: SPORT1 rx */

rti;rti;rti;

跳irqe;rti;rti;rti;/* 18: irqe */

rti;rti;rti;rti;/* 1c: BDMA */

rti;rti;rti;rti;/* 20: SPORT1 tx或IRQ1 */

rti;rti;rti;rti;/* 24: SPORT1 rx或IRQ0 */

rti;rti;rti;rti;/* 28: timer */

rti;rti;rti;rti;/* 2c:下电*/

/************************************************************

* adsp2181初始化

************************************************************/

开始:

0 = ^rx_buf;/*记住编解码器自动缓冲使用i0和i1 !!* /

L0 = %rx_buf;

I1 = ^tx_buf;

L1 = %tx_buf;

I3 = ^init_cmd;/* i3可用于编解码器init */之后的其他内容

L3 = %init_cmd;

M0 = 0;

M1 = 1;

/*初始化串口0,用于与AD1847编解码器通信*/

叫init_sport0;

/*初始化其他系统寄存器等*/

叫init_system_regs;

/*初始化AD1847编解码器*/

叫init_1847;

Ifc = b#00000011111111;/*清除所有挂起的中断*/

nop;/* ifc */有一个1周期的延迟

/*设置数据和系数指针*/

I2 = ^filt_data;

L2 = %filt_data;

I5 = ^filt_coefs;

M5 = 1;

L5 = %filt_coefs;

imask = b # 0000110000;/*启用rx0中断*/

/* |||||||||+ |计时器

||||||||+- | SPORT1 rec或IRQ0

|||||||+—| SPORT1 trx或IRQ1

||||||+——| bdma

|||||+---- | irqe

||||+----- | SPORT0 rec

|||+------| SPORT0 trx

||+-------| IRQL0

|+--------| IRQL1

+---------| IRQ2

* /

/*----------------------------------------------------------------------

-等待中断和循环永远

----------------------------------------------------------------------*/

这个通话:闲置;

这个跳;

/**************************************************************

*中断服务程序

**************************************************************/

/*----------------------------------------------------------------------

- FIR滤波器

----------------------------------------------------------------------*/

input_samples:

ena sec_reg;/*使用影子寄存器库*/

0 = dm (rx_buf + 1);/*从转换器读取数据*/

Dm (2,m1) = ax0;/*将新数据写入延迟线,指针

现在指向最老的数据*/

CNTR = taps - 1;

Mr = 0, mx0 = dm(i2,m1), my0 = pm(i5,m5);/*清除累加器,首先获取

数据及系数值*/

执行filt_loop直到ce;/*设置零开销循环*/

Filt_loop: Mr = Mr + mx0 * my0(1), mx0 = dm(1,m1), my0 = pm(1,m5);

/* MAC和两次数据读取*/

Mr = Mr + mx0 * my0 (rnd);/*最后的乘法,四舍五入到16位的结果*/

如果我看见先生;/*检查溢出*/

Dm (tx_buf+1) = mr1;

Dm (tx_buf+2) = mr1;/*输出数据到两个通道*/

rti;

.endmod;


参见:(第一部分)(第二部分)(第四部分)

声明:本文观点仅代表作者本人,不代表华强商城的观点和立场。如有侵权或者其他问题,请联系本站修改或删除。

社群二维码

关注“华强商城“微信公众号

调查问卷

请问您是:

您希望看到什么内容: