明德扬论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信扫一扫,快捷登录!

查看: 10868|回复: 2

【FPGA至简设计原理与应用】书籍连载22第三篇FPGA至简设计项目 第十三章FIR滤波器设计

[复制链接]
发表于 2020-6-19 18:13:20 | 显示全部楼层 |阅读模式

马上注册,看完整文章,学更多FPGA知识。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
温馨提示:明德扬2023推出了全新课程——逻辑设计基本功修炼课,降低学习FPGA门槛的同时,增加了学习的趣味性,并组织了考试赢积分活动
(点击→了解课程详情)http://www.mdy-edu.com/ffkc/415.html感兴趣请联系易老师:13112063618(微信同步)


本案例的编号为:000600000077
,如果有疑问,请按编号在下面贴子查找答案:MDY案例交流【汇总贴】_FPGA-明德扬科教 (mdy-edu.com)
本文为明德扬原创及录用文章,转载请注明出处


大家好,近期我们会连载《
FPGA至简设计原理与应用》一书,有兴趣的同学可以学习,也希望大家可以对我们的书提出宝贵的意见和建议。

《FPGA
至简设计原理与应用》书籍连载索引目录
http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=989


读过的朋友可积极在贴后留言,书籍正式出版时,我们会从留言者中挑选20位幸运读者,幸运读者可获潘老师亲笔签名书籍一本。

注:手机浏览可能格式会乱,建议用电脑端进行浏览。





第十三章 FIR滤波器设计


本文档编号:000600000025
需要看对应的视频,请点击视频编号:002700000453
1、本文档讲述FPGA产生两路正弦波数据,一路直接由DA输出,一路经过FIR处理之后输出,然后传到示波器进行观察,从而了解FIR滤波器的效果
2、801开发板使用


第1节 项目背景

1.1 FIRIIR滤波器

有限冲激响应(Finite Impulse Response, FIR)滤波器,又被称为非递归线性滤波器,是数字信号处理系统中最基本的元件。FIR滤波器的脉冲响应由有限个采样值构成,长度(抽头数)为N、阶数为N−1的FIR系统的转移函数、差分方程和单位冲激响应分别如下列三式所示。
书籍连载章节中转166.png
书籍连载章节中转168.png
书籍连载章节中转170.png
无限冲激响应(Infinite Impulse ResponseIIR)滤波器,又被称为递归线性滤波器。顾名思义,FIR和IIR是相对的,相比于IIRFIR具备线性相位特性。这里解释一下线性相位的概念:如果滤波器的N个实值系数为对称或者反对称结构则该滤波器具有线性相位,即W(n)=±W(N−1−n)W(n)=±W(N−1−n)。此外,FIR更易于设计,但同样指标下FIR滤波器需要更多的参数,即实现时需要消耗更多的计算单元,因而会产生较大的延迟。

1.2 FIR滤波器的原理
FIR滤波器对信号的处理其实就是信号与FIR滤波器的系数进行卷积(即乘累加)的过程。以声音来举例来看一下滤波器的效果,回顾一下日常生活,在聊天时始终以一种音调说话令人感到舒适,但是如果使用了麦克风等收声设备对声音进行收集并且放大后,由于人的音调高低不平,通过麦克风放大后音调高的声音会变得刺耳。如果把音调转换成波形来描述可以得到如下图所示的波形,可以看出整个声音段比较平稳,但是由于声音的变化会出现异常高的两段波,在现实生活中这就代表着出现了两段特别刺耳的声音,即噪声。而滤波器的作用就是将一段波形中突出的部分弱化掉,因此此时就可以使用滤波器来将噪声弱化。
书籍连载章节中转688.png
3.13-1声音的波形表示

明白了滤波器的作用后来讨论一下具体的工作原理。有些读者认为在上述波形中直接将高出来的波减去一定数值就可以了。那么同学们思考一下如何将高出来的波筛选出来呢?是否可以将其按照固定的数值范围划分呢?答案是否定的。这里需要明确一个概念:不同电压值的波形振幅是不同的。依旧拿声音来举例,如果一个人的说话声音一直维持在40分贝左右,那么当他忽然尖叫声音变为了100分贝时可以试着定义80分贝以上的波需要进行过滤弱化。但是如果另外一个人的说话声音一直维持在85分贝以上,他没有进行忽然的尖叫,声音波形一直是平稳的状态。但根据80分贝以上的波需要进行过滤的定义,一直保持85分贝的这一声音就会被过滤掉。

因此在设计时无法规定一个固定范围,令滤波器将此范围的波形全都过滤掉。同时也可以得出结论:滤波器过滤的对象没有固定数值,而是将一段波中忽高峰值弱化,如果本身波形趋于平稳,波的峰值再高也不会进行滤除。


大家都知道平均数的原理,如果你有7个苹果而我有1个苹果,你拥有的苹果数量比我多出很多。但是将所有苹果放在一起,一人分4个,此时相当于我帮你承担了3个苹果后我们拥有的苹果数量就没有任何的差距了。下图所示的统计图可以更加直观的显示,原本你的苹果数量属于高峰值,我们二人均分之后我们的波形都处于平稳的状态了。同理可知,如果将一个波形中的每个相邻点都进行这样的平均就可以得到一个趋于平滑的波形。
书籍连载章节中转1335.png
3.13-2均分苹果案例

滤波器分为高通、低通和带通等不同类型,即允许不同频率的信号通过滤波器。前文分析可知无法对频率进行临界值的定义,那么如何实现不同频率的值的过滤呢?此时可以采用将相邻两个点的值与过滤器模型值相乘后再进行平均的方法。滤波器的模型值由滤波器自身定义,根据不同的需求进行调整,有可能是一个很大的值,甚至可能是负数以达到过滤的效果。滤波器对信号的处理实际上就是信号与FIR滤波器的系数进行卷积(即乘累加)的过程。

下面通过计算一个简单信号模型来更加深入地了解FIR波形器的原理,本书会将三组信号进行对比,让同学们更加直观的感受到效果。

现在有三组信号,分别是:

信号1:低频信号,即在时域上变化慢的信号,其输入先后顺序为1 1 1 1 2 2 2 2
信号2:直流信号,其输入先后顺序为1 1 1 1 1 1 1
信号3:高频信号,即在时域上变化快的信号,其输入先后顺序为1 2 1 2 1 2 1 2

滤波器选定为低通滤波器,其模型值为:11

首先信号1与低通滤波器进行卷积运算,其结果再除以2,如下图所示,第一个信号数字“1”与第二个信号数字“1”和滤波器模型分别相乘求和后再除以2即(1*1+1*1)/2=1。随后每个相邻的信号依次进行同样的操作,处理后得到如下数据:1 1 1 1.5 2 2 2。可以看到低频信号经由低通滤波器处理后各个点仍然保持了其形状,而且在1变成2时变得平缓。
书籍连载章节中转1971.png
3.13-3信号1的低通滤波处理结果

同理,信号2与低通滤波器进行卷积运算,其结果再除以2,如下图所示,第一个信号数字“1”与第二个信号数字“1”和滤波器模型分别相乘求和后再除以2即(1*1+1*1)/2=1。随后每个相邻的信号依次进行同样的操作,处理后得到如下数据:1 1 1 1 1 1 1。可以看到直流信号与输入的信号完全相同。
书籍连载章节中转2163.png
3.13-4信号2的低通滤波处理结果

最后,信号3与低通滤波器进行卷积运算,其结果再除以2,如下图所示,第一个信号数字“1”与第二个信号数字“2”和滤波器模型分别相乘求和后再除以2即(1*1+2*1)/2=1.5。随后每个相邻的信号依次进行同样的操作,处理后得到如下数据:1.5 1.5 1.5 1.5 1.5 1.5 1.5。可以看到高频信号经由低通滤波器处理后已经完成消去了其原本形状,变成了直流信号。
书籍连载章节中转2391.png
3.13-5信号3的低通滤波处理结果

如果此时滤波器模型选为高通滤波器:1 -1,结果又会如何呢?

信号1与高通滤波器进行卷积运算,其结果再除以2,如下图所示,第一个信号数字“1”与第二个信号数字“1”和滤波器模型分别相乘求和后再除以2[1*1+1*(-1)]/2=0。随后每个相邻的信号依次进行同样的操作,处理后得到如下数据:0 0 0 -0.5 0 0 0。可以看到低频信号经由高通滤波器处理后信号变化基本上消失。
书籍连载章节中转2626.png
3.13-6信号1的高通滤波处理结果

信号2与高通滤波器进行卷积运算,其结果再除以2,如下图所示,第一个信号数字“1”与第二个信号数字“1”和滤波器模型分别相乘求和后再除以2[1*1+1*(-1)]/2=0。随后每个相邻的信号依次进行同样的操作,处理后得到如下数据:0 0 0 0 0 0 0。可以看到直流信号仍然没有任何变化。
书籍连载章节中转2816.png
3.13-7信号2的高通滤波处理结果

信号3与高通滤波器进行卷积运算,其结果再除以2,如下图所示,第一个信号数字“1”与第二个信号数字“2”和滤波器模型分别相乘求和后再除以2[1*1+2*(-1)]/2= - 0.5。随后每个相邻的信号依次进行同样的操作,处理后得到如下数据:-0.5 0.5 -0.5 0.5 -0.5 0.5 -0.5。可以看到高频信号仍然保持了其变化形态。
书籍连载章节中转3031.png
3.13-8信号3的高通滤波处理结果

这里只选取了两个点进行滤波处理,如果按照同样的方法一次进行四、五个信号的卷积运算,滤波器处理后得到的波形会更加平滑。当滤波器的系数不同,抽头个数不同,得到的效果也会不同。

通过上面的案例,相信同学们都已经了解了FIR滤波器的工作原理,简而言之,FIR滤波器的信号处理过程就是信号与FIR滤波器的系数进行卷积(即乘累加)的过程。通过调整滤波器系数、抽头个数等参数可以实现低通、高通、带通等多种滤波器的设计。

1.3 FIR滤波器的设计

1.3.1 matlab产生滤波器系数
打开matlab,在其命令窗口输入fdatool按下回车。
书籍连载章节中转3333.png
3.13-9matlab命令窗口输入fdatool

调出FIR滤波器的设计界面如下图所示,在此需要进行对应的参数设计,点击Design Filter即可计算出抽头系数。
书籍连载章节中转3442.png
3.13-10MATLABFIR滤波器设计界面

在波形设计界面中需要关注以下几个选项:

Response Type:滤波器的类型选择,可以选择lowpass低通滤波器、Highpass高通滤波器、bandpass带通滤波器、bandstop带阻滤波器四个选项,在设计时根据需求进行选择;

Fs采样频率):即采样速度或者采样率,其定义了每秒从连续信号中提取并组成离散信号的采样个数;

Fstop:信号截止频率

Fpass:信号通过率

Filter Order:设置滤波器的抽头个数设置,可以在specify order中输入个数,也可以选择Minimum order让系统计算满足要求前提下的最小抽头个数。

产生对应系数后点击file”菜单里的“Export”将系数保存到工作区,如下图所示。
书籍连载章节中转3815.png
3.13-11保存滤波器系数

随后直接点击“Export”选项:
书籍连载章节中转3872.png
3.13-12设置保存滤波器系数路径

打开工作区里的Num”:
书籍连载章节中转3929.png
3.13-13MATLAB显示滤波器系数文件

将下图第一行的数据复制粘贴到txt文件中:
书籍连载章节中转3999.png
3.13-14MATLAB打开滤波器文件

特别提示:复制后需在两个系数间插入英文状态下的逗号即可得到滤波器的系数,如下图所示:
书籍连载章节中转4088.png
3.13-15复制系数到TXT文本

1.3.2 FPGA生成FIR IP
打开工程后,在“IP catalog”界面中DSP”目录下选择“Filter”目录中的“FIR II”选项,如下图所示:
书籍连载章节中转4207.png
3.13-16IP Catalog中找到FIR IP

Fitter界面进行如下图所示的设置。
书籍连载章节中转4281.png
3.13-17FIR IP核类型设置界面

Filter Type:选择滤波器类型
Interpolation Factor:插值因子
Decimation Factor:抽取因子
Max Number of channels:设置通道个数;
Clock rate:填写本IP核的工作时钟频率;
clock slack:对采样时钟速率、质量的容忍程度,借由对数据的裕量来衡量;
Input sample rate msps):采样率;

点击coefficients”进入“coefficients”界面后点击“import from file”,找出用matlab生成的系数文件,点击“import”将其导入。导入成功后可以看到“frequency response”界面的波形发生变化,如下图所示。
书籍连载章节中转4657.png
3.13-18FIR IP频率响应设置界面

coefficient界面也可以对系数的格式、数据位宽等参数进行设置。
书籍连载章节中转4740.png
3.13-19FIR IP核滤波器系数设置界面

在此界面不更改其它选项,按照系统默认进行设置,点击finish”结束。至此完成了生成 FIR滤波器的主要步骤。

第2节 设计目标
按照至简设计法的设计特色,开始一个新的设计之前首先应明确设计目标。设计目标是整个设计的核心灵魂,后续的每个步骤与操作都是围绕设计目标进行展开的。至简设计法旨在让设计师在设计过程中按照最中的简单快捷的方式实现每个步骤和思路,明确设计目标正是让后面每个阶段的工作都有意义,而不去进行不必要的工程展开,这样一来可以少走很多弯路。对于初学者来说学习阶段养成好习惯可以使之后的工程师生涯受益无穷。所以再次强调在开始设计前一定要将设计目标分析透彻,认真思考本次设计最终想要实现什么目的,达到什么效果,然后再投入到设计中去。

本次设计使用采样率大于100M的双通道的示波器与FPGA开发板相连,其连接示意如下图所示,将示波器的两个通道分别与FPGADA通道1DA通道2相连,从而观察两路DA的输出。
书籍连载章节中转5195.png
3.13-20教学板连接图

本设计需要FPGA内部产生正弦信号,该信号一路输出给DA通道1,另一路经过FIR滤波器后输出给DA通道2。正弦信号的频率约等于100KHz * (key+1),其受开发板上的3个拨码开关控制,可以用3位信号key表示,即一共可以产生8种频率。例如,当key等于0时可以产生频率约为100KHz的正弦信号;当key等于1时可以产生频率约为200KHz的正弦波;当key等于7时可以产生频率约为800KHz的正弦波。FIR滤波器是低通滤波器,其截止频率是500KHz,由此可见,原则上超过500KHz的信号就会被滤除。FIR滤波器实现架构如下图所示,其输出传送给通道2

3.13-21FIR滤波器实现架构

FPGA输出后示波器的显示效果如下所示,其中黄色(上面的波形)是通道1输出的信号,蓝色(下面的波形)是通道2的输出信号。下面带领同学们看一下实际产生的信号效果,想要观看完整版视频演示效果的同学可以登陆网址查看:www.mdy-edu.com/xxxx。

100KHz的信号图如下:
书籍连载章节中转5756.png
3.13-22100K频率信号效果

200KHz的信号图如下:
书籍连载章节中转5812.png
3.13-23200K频率信号效果

300KHz的信号图如下:
书籍连载章节中转5868.png
3.13-24300K频率信号效果

400KHz的信号图如下所示,可以看到已经有了明显的衰减:
书籍连载章节中转5940.png
3.13-25400K频率信号效果

500KHz的信号图如下所示,可以看到已经衰减了很多,波形变得非常小:
书籍连载章节中转6018.png
3.13-26500K频率信号效果

600KHz的信号图如下所示,可以看到通道2已经几乎没有波形:
书籍连载章节中转6092.png
3.13-27600K频率信号效果

700KHz的信号图如下所示,可以看到通道2也同样几乎没有波形:
书籍连载章节中转6167.png
3.13-28700K频率信号效果

800KHz的信号图如下所示,可以看到通道2完全趋于没有波形:
书籍连载章节中转6241.png
3.13-29800K频率信号效果

第3节 设计实现
接下来就进入设计的实现阶段,本书会按照步骤和原理分析与读者分享案例的实现方法,考虑到初学者的需要,此部分的内容会比较详细。基础知识掌握得比较牢靠,只想学习此设计的步骤的情况下可以跳过此部分,后面章节有简化版的步骤分享。在此还是建议初学者不要选择捷径,一定按照详细分析的内容进行学习,只有掌握基础知识、打好基础,才可以从容的独立完成项目设计。

3.1 顶层接口
新建目录:D:\mdy_book\fir_prj在该目录中,新建一个名为fir_prj.v的文件。用GVIM打开后开始编写代码。这里再次强调,建议初学者按照书中提供的文件路径以及文件名进行设置,避免后续跳出未知错误。

首先来确定顶层信号。分析设计目标可知本设计需要实现以下功能:FPGA产生控制DA9709的信号,令通道A输出未滤波的正弦信号,令通道B输出滤波后的正弦信号,将两种信号进行对比。在此过程中,想要控制DA9709的工作模式则需控制DA9709MODESLEEP管脚;想要控制通道A则需控制AD9280CLK1WRT1DB7~0P1管脚;想要控制通道B则需要控制AD9280CLK2WRT2DB7~0P2管脚。在设计中使用信号clk连接到晶振来表示50M时钟的输入;使用信号rst_n连接到按键来表示复位;使用3位信号key来表示三位拨码开关;将dac_mode信号连接到DA9709MODE管脚用来控制其工作模式;将dac_sleep信号连接到DA9709SLEEP管脚用来控制其睡眠模式;将dac_clka信号连接到DA9709CLK1管脚用来控制通道A的时钟;将dac_wra信号连接到DA9709WRT1管脚用来控制通道A的写使能;将8位信号dac_da连接到DA9709DB7~0P1管脚用来控制通道A的写数据;将dac_clkb号连接到DA9709CLK2脚用来控制通道B时钟;将dac_wrb信号连接到DA9709WRT2脚用来控制通道B使能;将8位信号dac_db连接到DA9709DB7~0P2脚用来控制通道B写数据。

综上所述,本工程需要11个信号:时钟信号clk,复位信号rst_n,拨码开关的输入信号keydac_modedac_sleepdac_clkadac_wradac_dadac_clkbdac_wrbdac_db信号,其中dac_dadac_db8位信号,其它都是1位信号。信号和硬件的对应关系如下表所示。
3.13 - 1信号和管脚关系
器件
DA9709管脚
原理图信号
FPGA管脚
FPGA工程信号
U8
MODE
DAC_MODE
Y4
dac_mode
SLEEP
DAC_SLEEP
H2
dac_sleep
CLK1
DA_CLKA
R2
dac_clka
WRT1
DA_WRA
U1
dac_wra
DB7P1
DAC_DA7
AA1
dac_da[7]
DB6P1
DAC_DA6
Y2
dac_da[6]
DB5P1
DAC_DA5
Y1
dac_da[5]
DB4P1
DAC_DA4
W2
dac_da[4]
DB3P1
DAC_DA3
W1
dac_da[3]
DB2P1
DAC_DA2
V2
dac_da[2]
DB1P1
DAC_DA1
V1
dac_da[1]
DB0P1
DAC_DA0
U2
dac_da[0]
CLK2
DA_CLKB
R1
dac_clkb
WRT2
DA_WRB
P2
dac_wrb
DB7P2
DAC_DB7
P1
dac_db[7]
DB6P2
DAC_DB6
N2
dac_db[6]
DB5P2
DAC_DB5
N1
dac_db[5]
DB4P2
DAC_DB4
M2
dac_db[4]
DB3P2
DAC_DB3
M1
dac_db[3]
DB2P2
DAC_DB2
J1
dac_db[2]
DB1P2
DAC_DB1
J2
dac_db[1]
DB0P2
DAC_DB0
H1
dac_db[0]
X1

SYS_CLK
G1
clk
K1

SYS_RST
AB12
rst_n

SW_D0
SW_DIP0
AA3
key[0]

SW_D1
SW_DIP1
AB3
key[1]

SW_D2
SW_DIP2
ABB5
key[2]

module的名称定义为fir_prj。已知该设计有11个信号:clkrst_nkeydac_modedac_sleepdac_clkadac_wradac_dadac_clkbdac_wrbdac_db信号,将与外部相连接的信号写入模块接口列表,具体顶层代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
module fir_prj(
clk  ,
rst_n  ,
           key       ,
dac_mode ,
dac_sleep ,
dac_clka  ,
dac_da  ,
dac_wra  ,
dac_clkb  ,
dac_db  ,
dac_wrb
           );

随后声明输入输出属性。这里需要声明这一信号对于FPGA来说属于输入还是输出,如果是输入信号则声明其为input,如果是输出信号则声明其为output。在本设计中,由于clk是外部的晶振输入给FPGA的,因此在FPGAclk1位的输入信号input;同样地,rst_n是外部按键输送给FPGA的,因此在FPGA中rst_n也为1位输入信号inputkey3位输入信号inputdac_dadac_db8位的输出信号outputdac_modedac_clkadac_wradac_sleepdac_clkbdac_wrb均为是1位输出信号output。综上所述,补充输入输出端口定义后的代码如下:
1
2
3
4
5
6
7
input             clk  ;
input             rst_n  ;
input  [ 3-1:0]    key        ;
output            dac_mode ;
output            dac_clka  ;
output [ 8-1:0]    dac_da  ;
output            dac_wra  ;
output            dac_sleep ;
output            dac_clkb  ;
output [ 8-1:0]    dac_db  ;
output            dac_wrb  ;


3.2 正弦信号设计
将正弦信号命名为sin_data信号,sin_data是从表3.13- 2中选择出来的值,该表一共有128个点。该表的计算方法,在上一章节“信号发生器和DA转换”中有详细描述,这里就不再进行赘述了。
3.13- 2 DAC输出采样点对应幅度值
采样点i
sin_data
16进制)
采样点i
sin_data
16进制)
采样点i
sin_data
16进制)
采样点i
sin_data
16进制)
0
7F
32
FE
64
7D
96
1
1
85
33
FE
65
77
97
1
2
8C
34
FE
66
70
98
2
3
92
35
FD
67
6A
99
3
4
98
36
FC
68
64
100
4
5
9E
37
FA
69
5E
101
6
6
A4
38
F8
70
58
102
7
7
AA
39
F6
71
52
103
A
8
B0
40
F4
72
4C
104
C
9
B6
41
F1
73
46
105
F
10
BC
42
EF
74
41
106
12
11
C1
43
EB
75
3C
107
15
12
C6
44
E8
76
36
108
19
13
CB
45
E4
77
31
109
1D
14
D0
46
E0
78
2C
110
21
15
D5
47
DC
79
28
111
25
16
DA
48
D8
80
23
112
2A
17
DE
49
D3
81
1F
113
2E
18
E2
50
CE
82
1B
114
33
19
E6
51
C9
83
17
115
38
20
EA
52
C4
84
14
116
3E
21
ED
53
BE
85
11
117
43
22
F0
54
B9
86
E
118
49
23
F3
55
B3
87
B
119
4E
24
F5
56
AD
88
9
120
54
25
F7
57
A7
89
7
121
5A
26
F9
58
A1
90
5
122
60
27
FB
59
9B
91
3
123
67
28
FC
60
95
92
2
124
6D
29
FD
61
8F
93
1
125
73
30
FE
62
89
94
1
126
79
31
FE
63
82
95
1
127
7F

定义一个7位的选择信号addr,只要控制好addr信号就可以快速得到sin_data。可以写出下面代码:
1
2
3
4
5
6
7
8
9
always  @(*)begin
    case(addr)
          0: sin_data = 8'h7F;
          1: sin_data = 8'h85;
          2: sin_data = 8'h8C;
          3: sin_data = 8'h92;
          4: sin_data = 8'h98;
          5: sin_data = 8'h9E;
          6: sin_data = 8'hA4;
          7: sin_data = 8'hAA;
          8: sin_data = 8'hB0;
          9: sin_data = 8'hB6;
         10: sin_data = 8'hBC;
         11: sin_data = 8'hC1;
         12: sin_data = 8'hC6;
         13: sin_data = 8'hCB;
         14: sin_data = 8'hD0;
         15: sin_data = 8'hD5;
         16: sin_data = 8'hDA;
         17: sin_data = 8'hDE;
         18: sin_data = 8'hE2;
         19: sin_data = 8'hE6;
         20: sin_data = 8'hEA;
         21: sin_data = 8'hED;
         22: sin_data = 8'hF0;
         23: sin_data = 8'hF3;
         24: sin_data = 8'hF5;
         25: sin_data = 8'hF7;
         26: sin_data = 8'hF9;
         27: sin_data = 8'hFB;
         28: sin_data = 8'hFC;
         29: sin_data = 8'hFD;
         30: sin_data = 8'hFE;
         31: sin_data = 8'hFE;
         32: sin_data = 8'hFE;
         33: sin_data = 8'hFE;
         34: sin_data = 8'hFE;
         35: sin_data = 8'hFD;
         36: sin_data = 8'hFC;
         37: sin_data = 8'hFA;
         38: sin_data = 8'hF8;
         39: sin_data = 8'hF6;
         40: sin_data = 8'hF4;
         41: sin_data = 8'hF1;
         42: sin_data = 8'hEF;
         43: sin_data = 8'hEB;
         44: sin_data = 8'hE8;
         45: sin_data = 8'hE4;
         46: sin_data = 8'hE0;
         47: sin_data = 8'hDC;
         48: sin_data = 8'hD8;
         49: sin_data = 8'hD3;
         50: sin_data = 8'hCE;
         51: sin_data = 8'hC9;
         52: sin_data = 8'hC4;
         53: sin_data = 8'hBE;
         54: sin_data = 8'hB9;
         55: sin_data = 8'hB3;
         56: sin_data = 8'hAD;
         57: sin_data = 8'hA7;
         58: sin_data = 8'hA1;
         59: sin_data = 8'h9B;
         60: sin_data = 8'h95;
         61: sin_data = 8'h8F;
         62: sin_data = 8'h89;
         63: sin_data = 8'h82;
         64: sin_data = 8'h7D;
         65: sin_data = 8'h77;
         66: sin_data = 8'h70;
         67: sin_data = 8'h6A;
         68: sin_data = 8'h64;
         69: sin_data = 8'h5E;
         70: sin_data = 8'h58;
         71: sin_data = 8'h52;
         72: sin_data = 8'h4C;
         73: sin_data = 8'h46;
         74: sin_data = 8'h41;
         75: sin_data = 8'h3C;
         76: sin_data = 8'h36;
         77: sin_data = 8'h31;
         78: sin_data = 8'h2C;
         79: sin_data = 8'h28;
         80: sin_data = 8'h23;
         81: sin_data = 8'h1F;
         82: sin_data = 8'h1B;
         83: sin_data = 8'h17;
         84: sin_data = 8'h14;
         85: sin_data = 8'h11;
         86: sin_data = 8'hE ;
         87: sin_data = 8'hB ;
         88: sin_data = 8'h9 ;
         89: sin_data = 8'h7 ;
         90: sin_data = 8'h5 ;
         91: sin_data = 8'h3 ;
         92: sin_data = 8'h2 ;
         93: sin_data = 8'h1 ;
         94: sin_data = 8'h1 ;
         95: sin_data = 8'h1 ;
         96: sin_data = 8'h1 ;
         97: sin_data = 8'h1 ;
         98: sin_data = 8'h2 ;
         99: sin_data = 8'h3 ;
        100: sin_data = 8'h4 ;
        101: sin_data = 8'h6 ;
        102: sin_data = 8'h7 ;
        103: sin_data = 8'hA ;
        104: sin_data = 8'hC ;
        105: sin_data = 8'hF ;
        106: sin_data = 8'h12;
        107: sin_data = 8'h15;
        108: sin_data = 8'h19;
        109: sin_data = 8'h1D;
        110: sin_data = 8'h21;
        111: sin_data = 8'h25;
        112: sin_data = 8'h2A;
        113: sin_data = 8'h2E;
        114: sin_data = 8'h33;
        115: sin_data = 8'h38;
        116: sin_data = 8'h3E;
        117: sin_data = 8'h43;
        118: sin_data = 8'h49;
        119: sin_data = 8'h4E;
        120: sin_data = 8'h54;
        121: sin_data = 8'h5A;
        122: sin_data = 8'h60;
        123: sin_data = 8'h67;
        124: sin_data = 8'h6D;
        125: sin_data = 8'h73;
        126: sin_data = 8'h79;
        127: sin_data = 8'h7F;
endcase
end

那么addr信号又如何设计呢?在本设计中addr用以控制选择数据的地址,通过控制addr的增加值来产生多种频率的正弦波。以频率为100KHz的正弦信号为例,该正弦信号的周期是10000ns。本工程的工作时钟是20ns,也就是10000/20 = 500个时钟输出一个正弦信号,即500个时钟需要输出128个点,因此每个时钟addr增加的值为128/500 = 0.256

按同样的分析方法,可以得到其他信号频率的addr增加值如下所示:

100KHz的正弦信号,每个时钟addr增加:128/250= 0.256
200KHz的正弦信号,每个时钟addr增加:128/250= 0.512
300KHz的正弦信号,每个时钟addr增加:128/166.6667 = 0.7679
400KHz的正弦信号,每个时钟addr增加:128/125= 1.024
500KHz的正弦信号,每个时钟addr增加:128/100 = 1.28  
600KHz的正弦信号,每个时钟addr增加:128/83.3333= 1.5358
700KHz的正弦信号,每个时钟addr增加:128/71.4286 = 1.792
800KHz的正弦信号,每个时钟addr增加:128/62.5 = 2.048

addr表示的是采样点的值,即为0~127的整数,但是上面计算出的addr的每次增加值包含小数,众所周知FPGA是没有小数的,因此需要将上面的小数乘以1024后取整,得到每次要增加的整数并将该结果保存到addr_tmp中。即:

100KHz的正弦信号,每个时钟addr_tmp增加:0.256*1024 = 262.144 262
200KHz的正弦信号,每个时钟addr_tmp增加:0.512*1024 = 524.288 524
300KHz的正弦信号,每个时钟addr_tmp增加:0.7679*1024 =786.3296 786
400KHz的正弦信号,每个时钟addr_tmp增加:1.024*1024 =1028.576 1029
500KHz的正弦信号,每个时钟addr_tmp增加:1.28*1024 =1310.72  1311
600KHz的正弦信号,每个时钟addr_tmp增加:1.5358*1024 =1572.6592 1573
700KHz的正弦信号,每个时钟addr_tmp增加:1.792*1024 =1835.008 1835
800KHz的正弦信号,每个时钟addr_tmp增加: 2.048*1024 =2097.152 2097

前文分析可知:以上8种频率信号都是由拨码信号key进行控制,因此可以写出addr_tmp的代码如下:
1
2
3
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
addr_tmp<= 0;
    end
    else if(key==0) begin
addr_tmp<= addr_tmp + 262;
    end
    else if(key==1) begin
addr_tmp<= addr_tmp + 524;
    end
    else if(key==2) begin
addr_tmp<= addr_tmp + 786;
    end
    else if(key==3) begin
addr_tmp<= addr_tmp + 1029;
    end
    else if(key==4) begin
addr_tmp<= addr_tmp + 1311;
    end
    else if(key==5) begin
addr_tmp<= addr_tmp + 1573;
    end
    else if(key==6) begin
addr_tmp<= addr_tmp + 1835;
    end
    else begin
addr_tmp<= addr_tmp + 2097;
    end
end

由于addr_tmp是小数乘以1024后得到的,因此最终addr_tmp需要除以1024后再赋给addr。除以1024的操作可以用右移10位来实现,addr_tmp向右移10位后保留7位结果赋给addr即可。因此addr_tmp位宽为17位,其代码表示如下:
1
assign addr = addr_tmp>>10 ;

3.3 FIR滤波器设计


3.3.1 matlab生成FIR系数
打开matlab,在其命令窗口输入fdatool按下回车调出波形设计界面。
书籍连载章节中转16189.png
3.13-30命令窗口输入fdatool

在波形设计界面中按照设计目标进行参数选择。

Response Type:案例要求滤波高于500KHz的信号,因此选择lowpass低通滤波器;
Fstop:截止频率设为600KHz
Fs采样频率: 12.5MHz(12500Khz)

其它选项默认

最后点击Design Filter”,波形设计界面如下图所示:
书籍连载章节中转16392.png
3.13-31设置FIR滤波器参数

产生系数后点击file”菜单里的“Export”将系数保存到工作区,如下图所示:
书籍连载章节中转16476.png
3.13-32保存FIR滤波器系数

滤波器系数类型和路径界面如下图所示,此界面不进行改变,直接点击“Export”。
书籍连载章节中转16559.png
3.13-33保存滤波器系数类型和路径

打开工作区里的Num”,如下图所示。
书籍连载章节中转16623.png
3.13-34MATLAB打开系数

随后将下图第一行的数据复制粘贴到txt文件中:
书籍连载章节中转16689.png
3.13-35滤波器系数

注意复制后需在两个系数间在英文输入状态下插入逗号得到滤波器系数,如下图所示。
书籍连载章节中转16765.png
3.13-36复制滤波器系数到TXT文件

3.3.2 新建FPGA工程
打开软件“Quartus”,点击“File”菜单下的“New Project Wizard”,如下图所示。
书籍连载章节中转16874.png
3.13-37QUARTUS新建工程命令

弹出Introduction”界面后点击“Next”,如下图所示。
书籍连载章节中转16954.png
3.13-38QUARTUS新建工程介绍界面

设置工程目录,工程名,顶层模块名,其中工程目录设置为:D:\mdy_book\fir_prj;工程名设置为:fir_prj;顶层模块名设置为:fir_prj,如下图所示,填写完毕后直接点击“next”。
书籍连载章节中转17103.png
3.13-39QUARTUS设置新工程路径和名称

在设置新工程类型界面选择Empty project”选项建立空白工程,如下图所示,随后点击“Next”。
书籍连载章节中转17207.png
3.13-40QUARTUS设置新工程类型

错误!未找到引用源。接着是添加文件界面,此时不选择任何文件,直接点击“Next”,如下图所示。
书籍连载章节中转17324.png
3.13-41QUARTUS添加文件到工程界面

在选择芯片型号界面选择“Cyclone ⅣE”,在芯片型号选择处选择“EP4CE15F23C8”,完成后直接点击“Finish”,如下图所示。
书籍连载章节中转17445.png
3.13-42QUARTUS设置新工程的芯片型号

3.3.3 FPGA生成FIR IP
建立工程后,在软件“Quartus”中“IP catalog”这一界面下选择DSP”目录下“Filter”的“FIR II”选项,如下图所示。
书籍连载章节中转17582.png
3.13-43IP Catalog查找FIR IP

进入此界面后将新生成的FIR滤波器IP核路径设置为:D:\mdy_book\fir_prj\my_fir.vIPvariation file name”这一项选择“Verilog”,随后点击“OK”进入FIR滤波器设置界面,如下图所示。
书籍连载章节中转17755.png file:///C:/Users/Administrator/Desktop/%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%872/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC.files/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC17755.png
3.13-44设置FIR IP核名称和类型界面

Fitter specification”界面需要进行以下设置。

Filter Type:选择Single Rate,表示只采用一种采样率;
Clock Rate50MHz
Input Sample Rate (PSPS)12.5MHz

其余选项默认参数,然后点击coefficients”选项卡,如下图所示。
书籍连载章节中转17966.png

3.13-45设置FIR IP核类型界面


单击import from file ”选项,在输出的界面中找出用matlab生成的系数文件:my_fir_coe.txt,点击“import”将其导入。导入成功后可以看到下图的“frequency response”界面的波形发生变化。在“Coefficient Bit Width”选项中填写16,表示每个系数用16比特量化。
书籍连载章节中转18178.png
3.13-46FIR IP核系数设置界面

Input/Output Options”选项卡中,需要做如下设置:

Input Type:选择Signed Binary,表示输入的数据是有符号数(补码形式);
Input Width:输入8,表示输入的数据是8位位宽;
Output Type:选择Signed Binary,表示输出的数据是有符号数(补码形式);
MSB Rounding:选择Truncation。表示输出结果的高位要截断;
MSB Bits to Remove:填写3,表示MSB要截取3个符号位;
LSB Rounding:选择Truncation,表示输出结果的低位要截断;
LSB Bits to Remove:填写19,表示LSB要截断低19位;这样最终输出的结果就是8位。

其它选项默认,点击Finish”,生成FIR IP核。
书籍连载章节中转18584.png
3.13-47FIR IP核输入输出设置界面

生成FIR IP核界面如下:
书籍连载章节中转18646.png
3.13-48生成FIR IP成功提示界面

IP核生成后弹出如下图所示对话框,点击“yes”将此IP核添加进工程。
书籍连载章节中转18728.png
3.13-49FIR IP核加入工程提示界面

3.3.4 例化FIR IP
软件GVIM”打开D:\mdy_book\fir_prj\my_fir.v文件,生成的FIR IP核文件如下图所示。
书籍连载章节中转18847.png
3.13-50FIR IP核模块和输出输出信号

my_fir模块的各个信号的描述见下表:
3.13- 3 my_fir模块各信号描述
信号名
I/O
位宽
作用
clk
I
1
时钟输入信号。在FIR设置时已经填写其为50MHz,所以此时连接50MHz时钟。
reset_n
I
1
复位信号,低电平有效。
ast_sink_data
I
8
FIR滤波器输入的数据输入。注意,输入的是有符号数。
ast_sink_valid
I
1
FIR滤波器输入数据的有效指示信号。
ast_sink_error
I
1
输入数据错误指示信号,此处直接填0。
ast_source_data
O
8
FIR滤波器的输出。注意,是有符号数。
ast_source_valid
O
1
FIR滤波器输出有效指示信号。由于输入始终有效,输出也始终有效,因此此信号可以忽略不用,例化时不进行连接。
ast_source_error
O
2
FIR滤波器输出错误指示信号。由于输入没错误,输出也不会有错误,所以可以忽略该信号,例化时不进行连接。

由于滤波器的输入数据和输出数据都是有符号数(补码形式,-128~127),但是正弦信sin_data是无符号数(0~255)。因此需要将sin_data变成有符号数后再发送给FIR进行滤波。定义转换后的信号为fir_din,该信号位宽为8位。将sin_data减去128转换成有符号数,即fir_din = sin_data–128

生成FIR IP核后需要对该IP核进行例化后才可以使用。所谓例化则为把FIR IP核的信号连接到对应的滤波器上。虽然这些信号是一样的,但是在每个模块中信号的命名不同,如果不进行连接,模块与模块之间就会互相不知道对方的存在。因此需要将两个相同的信号连接在一起来达成合作的效果。例化名为u_my_firfir的输出数据信号命名为fir_dout,可以写出代码如下:
1
2
3
4
5
6
7
8
assign fir_din = sin_data - 128;

my_firu_my_fir(
.clk             (clk     ) ,              
.reset_n         (rst_n   ) ,         
.ast_sink_data   (fir_din ) ,   
.ast_sink_valid  (1       ) ,   
.ast_sink_error  (0       ) ,   
.ast_source_data (fir_dout) ,  
.ast_source_valid(        ) ,
.ast_source_error(        )   
);

3.4 DA接口信号设计
首先设计信号dac_dadac_da是直接输出正弦信号的,但由于DA的输出电压与dac_da成反比例线性关系,所以dac_da通过(255-sin_data)得到。写代码时可以调用至简设计法模板,在编辑状态下输入“Shixu2”,将其补充完整得到dac_da的代码表示如下:
1
2
3
4
5
6
7
8
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
dac_da<= 0;
    end
    else begin
dac_da<= 255 - sin_data;
    end
end

接着设计信号dac_sleepAD一直进行工作,因此dac_sleep始终为0;为了满足tS的时间要求,可以令dac_clka = ~clkdac_wra信号与dac_clka相同,其具体代码表示如下:
1
2
3
assign dac_sleep = 0        ;
assign dac_wra   = dac_clka ;
assign dac_clka  = ~clk      ;

然后设计信号dac_dbdac_db是直接输出滤波后的信号fir_dout。需要注意的是fir_dout是有符号数(范围是-128~127),所以要转为无符号数(0~255)。假设转换后的信号为fir_dout2,则fir_dout2 = fir_dout + 128。由于DA的通道2的输出电压与dac_db是成反比例线性关系,所以dac_db通过(255-fir_dout2)得到。写出dac_db的代码表示如下:
1
2
3
4
5
6
7
8
assign fir_dout2 = fir_dout + 128;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
dac_db<= 0;
    end
    else begin
dac_db<= 255 - fir_dout2;
    end
end

最后设计dac_clkb信号,为了满足tS的时间要求,可以让dac_clkb = ~clkdac_wrb信号与dac_clkb相同,其具体代码表示如下:
1
2
3
assign dac_wrb   = dac_clkb ;
assign dac_clkb  = ~clk      ;

至此,模块主体已经完成。

3.5 信号定义
接下来将module补充完整,首先来定义信号类型。regwire的判断很容易搞不清楚总会有多余的联想,比如认为reg就是寄存器,wire是线;或者认为reg会综合成寄存器,wire不会综合成寄存器。但是这些其实和reg型还是wire型都并无关系,在进行信号类型判断时不需要做任何的联想,只要记住一个规则:“用always实现的是reg型,其他都是wire型”就可以了。

addr是用assign设计的,因此类型为wireaddr值最大为127,需要用7根线表示,即位宽为7

关于信号位宽的获取,在这里至简设计法分享一个非常实用的技巧:打开计算器,点击“查看”,选择“程序员”模式,在“十进制”下将信号值输入进去,就会获得对应的信号位宽。利用这一方法将addr的最大计数值127输入到计算器中,如下图所示,可以看到其位宽为7。
书籍连载章节中转21505.png
3.13-51通过计算器获取信号位宽

综上所述,addr的定义代码如下:
1
wire    [6:0]    addr    ;

addr_tmp是用always设计的,因此其类型为reg。该信号的位宽是17,代码表示如下:
1
reg   [16:0]    addr_tmp    ;

sin_data是用always设计的,因此类型为reg。其最大值为255,要用8根线表示,位宽为8,可以在编辑状态下输入“Reg8”调用至简设计法模板,补充完整后得到代码如下:
1
reg   [7:0]    sin_data    ;

fir_din是用assign设计的,因此类型为wire。该信号位宽为8,在编辑状态下输入“Wire8”调用至简设计法模板,补充完整后得到代码如下:
1
wire    [7:0]    fir_din    ;

fir_dout是用例化模块的输出,非always设计因此类型为wire。其位宽为8,依旧在编辑状态下输入“Wire8”调用至简设计法模板,补充完整后得到代码如下:
1
wire    [7:0]    fir_dout    ;

fir_dout2是用assign设计的,非always设计因此类型为wire。其位宽为8,同样在编辑状态下输入“Wire8”调用至简设计法模板,补充完整后得到代码如下:
1
wire    [7:0]    fir_dout2    ;

dac_da是用always设计的,因此类型为reg,其位宽为8
dac_sleep是用assign设计的,因此类型为wire,其位宽为1
dac_wra是用assign设计的,因此类型为wire,其位宽为1
dac_clka是用assign设计的,因此类型为wire,其位宽为1
dac_mode是用assign设计的,因此类型为wire,其位宽为1

依旧在编辑状态下输入“ Reg8”“Wire1”调用至简设计法模板,补充完整后得到相应代码如下:
1
reg   [7:0]    dac_da    ;
wire          dac_sleep  ;
wire          dac_wra  ;
wire          dac_clka  ;
wire          dac_mode ;

dac_db是用always设计的,因此类型为reg,其位宽为8
dac_wrb是用assign设计的,因此类型为wire,其位宽为1
dac_clkb是用assign设计的,因此类型为wire,其位宽为1

在编辑状态下输入“Reg8”“Wire1”调用至简设计法模板,补充完整后得到相应代码如下:
1
reg   [7:0]    dac_db    ;
wire          dac_wrb  ;
wire          dac_clkb  ;

在代码的最后一行写下endmodule
1
endmodule

至此,整个代码的设计工作已经完成。完整版的工程代码如下:
  1. module fir_prj(

  2. clk  ,

  3. rst_n  ,

  4. key       ,

  5. dac_mode ,

  6. dac_sleep ,

  7. dac_clka  ,

  8. dac_da  ,

  9. dac_wra  ,

  10. dac_clkb  ,

  11. dac_db  ,

  12. dac_wrb

  13.            );



  14. input             clk  ;

  15. input             rst_n  ;

  16. input  [ 3-1:0]    key        ;

  17. output            dac_mode ;

  18. output            dac_clka  ;

  19. output [ 8-1:0]    dac_da  ;

  20. output            dac_wra  ;

  21. output            dac_sleep ;

  22. output            dac_clkb  ;

  23. output [ 8-1:0]    dac_db  ;

  24. output            dac_wrb  ;



  25. wire    [6:0]    addr    ;

  26. reg   [16:0]    addr_tmp    ;

  27. reg   [7:0]    sin_data    ;

  28. wire    [7:0]    fir_din    ;

  29. wire    [7:0]    fir_dout    ;

  30. wire    [7:0]    fir_dout2    ;

  31. reg   [7:0]    dac_da    ;

  32. wire          dac_sleep  ;

  33. wire          dac_wra  ;

  34. wire          dac_clka  ;

  35. wire          dac_mode ;



  36. always  @(*)begin

  37.     case(addr)

  38.           0: sin_data = 8'h7F;

  39.           1: sin_data = 8'h85;

  40.           2: sin_data = 8'h8C;

  41.           3: sin_data = 8'h92;

  42.           4: sin_data = 8'h98;

  43.           5: sin_data = 8'h9E;

  44.           6: sin_data = 8'hA4;

  45.           7: sin_data = 8'hAA;

  46.           8: sin_data = 8'hB0;

  47.           9: sin_data = 8'hB6;

  48.          10: sin_data = 8'hBC;

  49.          11: sin_data = 8'hC1;

  50.          12: sin_data = 8'hC6;

  51.          13: sin_data = 8'hCB;

  52.          14: sin_data = 8'hD0;

  53.          15: sin_data = 8'hD5;

  54.          16: sin_data = 8'hDA;

  55.          17: sin_data = 8'hDE;

  56.          18: sin_data = 8'hE2;

  57.          19: sin_data = 8'hE6;

  58.          20: sin_data = 8'hEA;

  59.          21: sin_data = 8'hED;

  60.          22: sin_data = 8'hF0;

  61.          23: sin_data = 8'hF3;

  62.          24: sin_data = 8'hF5;

  63.          25: sin_data = 8'hF7;

  64.          26: sin_data = 8'hF9;

  65.          27: sin_data = 8'hFB;

  66.          28: sin_data = 8'hFC;

  67.          29: sin_data = 8'hFD;

  68.          30: sin_data = 8'hFE;

  69.          31: sin_data = 8'hFE;

  70.          32: sin_data = 8'hFE;

  71.          33: sin_data = 8'hFE;

  72.          34: sin_data = 8'hFE;

  73.          35: sin_data = 8'hFD;

  74.          36: sin_data = 8'hFC;

  75.          37: sin_data = 8'hFA;

  76.          38: sin_data = 8'hF8;

  77.          39: sin_data = 8'hF6;

  78.          40: sin_data = 8'hF4;

  79.          41: sin_data = 8'hF1;

  80.          42: sin_data = 8'hEF;

  81.          43: sin_data = 8'hEB;

  82.          44: sin_data = 8'hE8;

  83.          45: sin_data = 8'hE4;

  84.          46: sin_data = 8'hE0;

  85.          47: sin_data = 8'hDC;

  86.          48: sin_data = 8'hD8;

  87.          49: sin_data = 8'hD3;

  88.          50: sin_data = 8'hCE;

  89.          51: sin_data = 8'hC9;

  90.          52: sin_data = 8'hC4;

  91.          53: sin_data = 8'hBE;

  92.          54: sin_data = 8'hB9;

  93.          55: sin_data = 8'hB3;

  94.          56: sin_data = 8'hAD;

  95.          57: sin_data = 8'hA7;

  96.          58: sin_data = 8'hA1;

  97.          59: sin_data = 8'h9B;

  98.          60: sin_data = 8'h95;

  99.          61: sin_data = 8'h8F;

  100.          62: sin_data = 8'h89;

  101.          63: sin_data = 8'h82;

  102.          64: sin_data = 8'h7D;

  103.          65: sin_data = 8'h77;

  104.          66: sin_data = 8'h70;

  105.          67: sin_data = 8'h6A;

  106.          68: sin_data = 8'h64;

  107.          69: sin_data = 8'h5E;

  108.          70: sin_data = 8'h58;

  109.          71: sin_data = 8'h52;

  110.          72: sin_data = 8'h4C;

  111.          73: sin_data = 8'h46;

  112.          74: sin_data = 8'h41;

  113.          75: sin_data = 8'h3C;

  114.          76: sin_data = 8'h36;

  115.          77: sin_data = 8'h31;

  116.          78: sin_data = 8'h2C;

  117.          79: sin_data = 8'h28;

  118.          80: sin_data = 8'h23;

  119.          81: sin_data = 8'h1F;

  120.          82: sin_data = 8'h1B;

  121.          83: sin_data = 8'h17;

  122.          84: sin_data = 8'h14;

  123.          85: sin_data = 8'h11;

  124.          86: sin_data = 8'hE ;

  125.          87: sin_data = 8'hB ;

  126.          88: sin_data = 8'h9 ;

  127.          89: sin_data = 8'h7 ;

  128.          90: sin_data = 8'h5 ;

  129.          91: sin_data = 8'h3 ;

  130.          92: sin_data = 8'h2 ;

  131.          93: sin_data = 8'h1 ;

  132.          94: sin_data = 8'h1 ;

  133.          95: sin_data = 8'h1 ;

  134.          96: sin_data = 8'h1 ;

  135.          97: sin_data = 8'h1 ;

  136.          98: sin_data = 8'h2 ;

  137.          99: sin_data = 8'h3 ;

  138.         100: sin_data = 8'h4 ;

  139.         101: sin_data = 8'h6 ;

  140.         102: sin_data = 8'h7 ;

  141.         103: sin_data = 8'hA ;

  142.         104: sin_data = 8'hC ;

  143.         105: sin_data = 8'hF ;

  144.         106: sin_data = 8'h12;

  145.         107: sin_data = 8'h15;

  146.         108: sin_data = 8'h19;

  147.         109: sin_data = 8'h1D;

  148.         110: sin_data = 8'h21;

  149.         111: sin_data = 8'h25;

  150.         112: sin_data = 8'h2A;

  151.         113: sin_data = 8'h2E;

  152.         114: sin_data = 8'h33;

  153.         115: sin_data = 8'h38;

  154.         116: sin_data = 8'h3E;

  155.         117: sin_data = 8'h43;

  156.         118: sin_data = 8'h49;

  157.         119: sin_data = 8'h4E;

  158.         120: sin_data = 8'h54;

  159.         121: sin_data = 8'h5A;

  160.         122: sin_data = 8'h60;

  161.         123: sin_data = 8'h67;

  162.         124: sin_data = 8'h6D;

  163.         125: sin_data = 8'h73;

  164.         126: sin_data = 8'h79;

  165.         127: sin_data = 8'h7F;

  166. endcase

  167. end



  168. always  @(posedge clk or negedge rst_n)begin

  169.     if(rst_n==1'b0)begin

  170. addr_tmp<= 0;

  171.     end

  172.     else if(key==0) begin

  173. addr_tmp<= addr_tmp + 262;

  174.     end

  175.     else if(key==1) begin

  176. addr_tmp<= addr_tmp + 524;

  177.     end

  178.     else if(key==2) begin

  179. addr_tmp<= addr_tmp + 786;

  180.     end

  181.     else if(key==3) begin

  182. addr_tmp<= addr_tmp + 1029;

  183.     end

  184.     else if(key==4) begin

  185. addr_tmp<= addr_tmp + 1311;

  186. end

  187.     else if(key==5) begin

  188. addr_tmp<= addr_tmp + 1573;

  189.     end

  190.     else if(key==6) begin

  191. addr_tmp<= addr_tmp + 1835;

  192.     end

  193.     else begin

  194. addr_tmp<= addr_tmp + 2097;

  195.     end

  196. end





  197. assign addr = addr_tmp>>10 ;

  198. assign fir_din = sin_data - 128;



  199. my_firu_my_fir(

  200. .clk             (clk     ) ,              

  201. .reset_n         (rst_n   ) ,         

  202. .ast_sink_data   (fir_din ) ,   

  203. .ast_sink_valid  (1       ) ,   

  204. .ast_sink_error  (0       ) ,   

  205. .ast_source_data (fir_dout) ,  

  206. .ast_source_valid(        ) ,

  207. .ast_source_error(        )   

  208. );





  209. always  @(posedge clk or negedge rst_n)begin

  210.     if(rst_n==1'b0)begin

  211. dac_da<= 0;

  212.     end

  213.     else begin

  214. dac_da<= 255 - sin_data;

  215.     end

  216. end





  217. assign dac_sleep = 0        ;

  218. assign dac_wra   = dac_clka ;

  219. assign dac_clka  = ~clk      ;



  220. assign fir_dout2 = fir_dout + 128;

  221. always  @(posedge clk or negedge rst_n)begin

  222.     if(rst_n==1'b0)begin

  223. dac_db<= 0;

  224.     end

  225.     else begin

  226. dac_db<= 255 - fir_dout2;

  227.     end

  228. end



  229. assign dac_wrb   = dac_clkb ;

  230. assign dac_clkb  = ~clk      ;



  231. endmodule
复制代码




第4节 综合与上板


4.1 添加文件
上一节中已经介绍了新建工程的过程,这里就不再赘述了。现在打开软件“Quartus”,在“Project”菜单中选择“Add/Remove File to Project”,如下图所示,随后会弹出文件窗口。
书籍连载章节中转30135.png
3.13-52添加文件命令界面

点击右上角 书籍连载章节中转30181.png 按钮,在弹出来的窗口中双击选择D:\mdy_book\fir_prj目录下的fir_prj.vmy_fir.qip文件。点击Add”添加成功后关闭本窗口,添加文件成功界面如下图所示。
书籍连载章节中转30276.png
3.13-53添加文件成功界面

4.2 综合
编译界面如下图所示,在菜单栏中选中Processing后选择Start Compilation,开始对整个工程进行编译和综合。
书籍连载章节中转30384.png
3.13-54编译命令界面

当出现如下图所示界面时说明编译综合成功。
书籍连载章节中转30443.png
3.13-55编译成功界面

4.3 配置管脚
下面需要对相应管脚进行配置。如下图所示,在菜单栏中,选中“Assignments”,然后选择“Pin Planner”,随后就会弹出配置管脚的窗口。
书籍连载章节中转30561.png
3.13-56配置管脚命令界面

在配置窗口最下方中的“location”一列,参考3.2-2信号和管脚关系,按照表3.13- 1中最右两列配置好FPGA管脚。配置管理来源参见管脚配置环节,最终配置的结果如图3.13-58。配置完成后,关闭Pin Planner,软件自动会保存管脚配置信息。
3.13 –1 信号和管脚关系
器件
DA9709管脚
原理图信号
FPGA管脚
FPGA工程信号
U8
MODE
DAC_MODE
Y4
dac_mode
SLEEP
DAC_SLEEP
H2
dac_sleep
CLK1
DA_CLKA
R2
dac_clka
WRT1
DA_WRA
U1
dac_wra
DB7P1
DAC_DA7
AA1
dac_da[7]
DB6P1
DAC_DA6
Y2
dac_da[6]
DB5P1
DAC_DA5
Y1
dac_da[5]
DB4P1
DAC_DA4
W2
dac_da[4]
DB3P1
DAC_DA3
W1
dac_da[3]
DB2P1
DAC_DA2
V2
dac_da[2]
DB1P1
DAC_DA1
V1
dac_da[1]
DB0P1
DAC_DA0
U2
dac_da[0]
CLK2
DA_CLKB
R1
dac_clkb
WRT2
DA_WRB
P2
dac_wrb
DB7P2
DAC_DB7
P1
dac_db[7]
DB6P2
DAC_DB6
N2
dac_db[6]
DB5P2
DAC_DB5
N1
dac_db[5]
DB4P2
DAC_DB4
M2
dac_db[4]
DB3P2
DAC_DB3
M1
dac_db[3]
DB2P2
DAC_DB2
J1
dac_db[2]
DB1P2
DAC_DB1
J2
dac_db[1]
DB0P2
DAC_DB0
H1
dac_db[0]
X1
SYS_CLK
G1
clk
K1
SYS_RST
AB12
rst_n

书籍连载章节中转31482.png

3.13-57配置管脚界面

书籍连载章节中转31520.png

3.13-58管脚配置成功界面


4.4 再次综合
再次打开“QUARTUS”软件,在菜单栏中选中“Processing”,然后选择“Start Compilation”,再次对整个工程进行编译和综合,如下图所示。
书籍连载章节中转31647.png
3.13-59编译命令界面

当出现3.2-19QUARTUS编译成功标志,就说明编译综合成功。
书籍连载章节中转31743.png
3.13-60编译成功界面

4.5 连接开发板
完成编译后开始进行上板调试操作,开发板连接方式如下图所示。将电源接上开发板USB BLASTER一端连接到JTAG插口,另一端连到PCUSB接口。将开发板上的AD接口和DA与示波器的两个通道相连,连接完成后将电源打开。
书籍连载章节中转31899.png
3.13-61教学板连接示意图

4.6 上板
“Quartus”的“Task”窗口中,右键“Program Device”选择“Open”进入烧录界面,如下图所示。
书籍连载章节中转32003.png
3.13-62打开配置程序命令

默认选中文件output/fir_prj.sofHardware Setup的旁边会显示USB-Blaster,如下图所示。
书籍连载章节中转32107.png
3.13-63配置程序界面

当进度条到100%提示成功后即可在示波器上观看相应现象。
书籍连载章节中转32174.png
3.13-64配置成功界面

下载完成后,如果操作无误此时可以在示波器上看到对应的波形。如果没有显示成功,就需要返回检查一下连接是否到位,有没有写错代码或者软件参数的选择有没有发生错误。如果无法自己完成错误排查的话,可以重新按照步骤操作一遍,相信一定会达到想要的效果。

第5节 简化版步骤分享
这里依旧会分享简化版的步骤,方便掌握基础原理后进行反复操作复习。

5.1 设计实现

5.1.1 顶层接口
新建目录:D:\mdy_book\fir_prj在该目录中,新建一个名为fir_prj.v的文件,并用GVIM打开,开始编写代码。

分析设计目标确定顶层信号,信号和硬件的对应关系图见下表所示。
3.13 –1信号和管脚关系
器件
DA9709管脚
原理图信号
FPGA管脚
FPGA工程信号
U8
MODE
DAC_MODE
Y4
dac_mode
SLEEP
DAC_SLEEP
H2
dac_sleep
CLK1
DA_CLKA
R2
dac_clka
WRT1
DA_WRA
U1
dac_wra
DB7P1
DAC_DA7
AA1
dac_da[7]
DB6P1
DAC_DA6
Y2
dac_da[6]
DB5P1
DAC_DA5
Y1
dac_da[5]
DB4P1
DAC_DA4
W2
dac_da[4]
DB3P1
DAC_DA3
W1
dac_da[3]
DB2P1
DAC_DA2
V2
dac_da[2]
DB1P1
DAC_DA1
V1
dac_da[1]
DB0P1
DAC_DA0
U2
dac_da[0]
CLK2
DA_CLKB
R1
dac_clkb
WRT2
DA_WRB
P2
dac_wrb
DB7P2
DAC_DB7
P1
dac_db[7]
DB6P2
DAC_DB6
N2
dac_db[6]
DB5P2
DAC_DB5
N1
dac_db[5]
DB4P2
DAC_DB4
M2
dac_db[4]
DB3P2
DAC_DB3
M1
dac_db[3]
DB2P2
DAC_DB2
J1
dac_db[2]
DB1P2
DAC_DB1
J2
dac_db[1]
DB0P2
DAC_DB0
H1
dac_db[0]
X1

SYS_CLK
G1
clk
K1

SYS_RST
AB12
rst_n

写出顶层代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
module fir_prj(
clk  ,
rst_n  ,
key       ,
dac_mode ,
dac_sleep ,
dac_clka  ,
dac_da  ,
dac_wra  ,
dac_clkb  ,
dac_db  ,
dac_wrb
           );

声明输入输出属性:
1
2
3
4
5
6
7
input             clk  ;
input             rst_n  ;
input  [ 3-1:0]    key        ;
output            dac_mode ;
output            dac_clka  ;
output [ 8-1:0]    dac_da  ;
output            dac_wra  ;
output            dac_sleep ;
output            dac_clkb  ;
output [ 8-1:0]    dac_db  ;
output            dac_wrb  ;


5.2 正弦信号设计
设计正弦信号sin_data信号:
1
2
3
4
5
6
7
8
9
always  @(*)begin
    case(addr)
          0: sin_data = 8'h7F;
          1: sin_data = 8'h85;
          2: sin_data = 8'h8C;
          3: sin_data = 8'h92;
          4: sin_data = 8'h98;
          5: sin_data = 8'h9E;
          6: sin_data = 8'hA4;
          7: sin_data = 8'hAA;
          8: sin_data = 8'hB0;
          9: sin_data = 8'hB6;
         10: sin_data = 8'hBC;
         11: sin_data = 8'hC1;
         12: sin_data = 8'hC6;
         13: sin_data = 8'hCB;
         14: sin_data = 8'hD0;
         15: sin_data = 8'hD5;
         16: sin_data = 8'hDA;
         17: sin_data = 8'hDE;
         18: sin_data = 8'hE2;
         19: sin_data = 8'hE6;
         20: sin_data = 8'hEA;
         21: sin_data = 8'hED;
         22: sin_data = 8'hF0;
         23: sin_data = 8'hF3;
         24: sin_data = 8'hF5;
         25: sin_data = 8'hF7;
         26: sin_data = 8'hF9;
         27: sin_data = 8'hFB;
         28: sin_data = 8'hFC;
         29: sin_data = 8'hFD;
         30: sin_data = 8'hFE;
         31: sin_data = 8'hFE;
         32: sin_data = 8'hFE;
         33: sin_data = 8'hFE;
         34: sin_data = 8'hFE;
         35: sin_data = 8'hFD;
         36: sin_data = 8'hFC;
         37: sin_data = 8'hFA;
         38: sin_data = 8'hF8;
         39: sin_data = 8'hF6;
         40: sin_data = 8'hF4;
         41: sin_data = 8'hF1;
         42: sin_data = 8'hEF;
         43: sin_data = 8'hEB;
         44: sin_data = 8'hE8;
         45: sin_data = 8'hE4;
         46: sin_data = 8'hE0;
         47: sin_data = 8'hDC;
         48: sin_data = 8'hD8;
         49: sin_data = 8'hD3;
         50: sin_data = 8'hCE;
         51: sin_data = 8'hC9;
         52: sin_data = 8'hC4;
         53: sin_data = 8'hBE;
         54: sin_data = 8'hB9;
         55: sin_data = 8'hB3;
         56: sin_data = 8'hAD;
         57: sin_data = 8'hA7;
         58: sin_data = 8'hA1;
         59: sin_data = 8'h9B;
         60: sin_data = 8'h95;
         61: sin_data = 8'h8F;
         62: sin_data = 8'h89;
         63: sin_data = 8'h82;
         64: sin_data = 8'h7D;
         65: sin_data = 8'h77;
         66: sin_data = 8'h70;
         67: sin_data = 8'h6A;
         68: sin_data = 8'h64;
         69: sin_data = 8'h5E;
         70: sin_data = 8'h58;
         71: sin_data = 8'h52;
         72: sin_data = 8'h4C;
         73: sin_data = 8'h46;
         74: sin_data = 8'h41;
         75: sin_data = 8'h3C;
         76: sin_data = 8'h36;
         77: sin_data = 8'h31;
         78: sin_data = 8'h2C;
         79: sin_data = 8'h28;
         80: sin_data = 8'h23;
         81: sin_data = 8'h1F;
         82: sin_data = 8'h1B;
         83: sin_data = 8'h17;
         84: sin_data = 8'h14;
         85: sin_data = 8'h11;
         86: sin_data = 8'hE ;
         87: sin_data = 8'hB ;
         88: sin_data = 8'h9 ;
         89: sin_data = 8'h7 ;
         90: sin_data = 8'h5 ;
         91: sin_data = 8'h3 ;
         92: sin_data = 8'h2 ;
         93: sin_data = 8'h1 ;
         94: sin_data = 8'h1 ;
         95: sin_data = 8'h1 ;
         96: sin_data = 8'h1 ;
         97: sin_data = 8'h1 ;
         98: sin_data = 8'h2 ;
         99: sin_data = 8'h3 ;
        100: sin_data = 8'h4 ;
        101: sin_data = 8'h6 ;
        102: sin_data = 8'h7 ;
        103: sin_data = 8'hA ;
        104: sin_data = 8'hC ;
        105: sin_data = 8'hF ;
        106: sin_data = 8'h12;
        107: sin_data = 8'h15;
        108: sin_data = 8'h19;
        109: sin_data = 8'h1D;
        110: sin_data = 8'h21;
        111: sin_data = 8'h25;
        112: sin_data = 8'h2A;
        113: sin_data = 8'h2E;
        114: sin_data = 8'h33;
        115: sin_data = 8'h38;
        116: sin_data = 8'h3E;
        117: sin_data = 8'h43;
        118: sin_data = 8'h49;
        119: sin_data = 8'h4E;
        120: sin_data = 8'h54;
        121: sin_data = 8'h5A;
        122: sin_data = 8'h60;
        123: sin_data = 8'h67;
        124: sin_data = 8'h6D;
        125: sin_data = 8'h73;
        126: sin_data = 8'h79;
        127: sin_data = 8'h7F;
endcase
end

设计addr_tmp信号:
1
2
3
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
addr_tmp<= 0;
    end
    else if(key==0) begin
addr_tmp<= addr_tmp + 262;
    end
    else if(key==1) begin
addr_tmp<= addr_tmp + 524;
    end
    else if(key==2) begin
addr_tmp<= addr_tmp + 786;
    end
    else if(key==3) begin
addr_tmp<= addr_tmp + 1029;
    end
    else if(key==4) begin
addr_tmp<= addr_tmp + 1311;
    end
    else if(key==5) begin
addr_tmp<= addr_tmp + 1573;
    end
    else if(key==6) begin
addr_tmp<= addr_tmp + 1835;
    end
    else begin
addr_tmp<= addr_tmp + 2097;
    end
end

设计addr信号:
1
assign addr = addr_tmp>>10 ;

5.2.1 FIR滤波器设计
步骤一:matlab生成FIR系数

打开matlab后在其命令窗口输入fdatool按下回车调出波形设计界面。
书籍连载章节中转38497.png

3.13-9matlab命令窗口输入fdatool


点击Design Filter”设计参数,波形设计界面如下图所示。
书籍连载章节中转38601.png
3.13-11设置FIR滤波器参数

点击file”菜单里的“Export”将系数保存到工作区,如下图所示。
书籍连载章节中转38681.png
3.13-11保存FIR滤波器系数

随后直接点击“Export”。
书籍连载章节中转38740.png
3.13-12保存滤波器系数类型和路径

打开工作区里的Num”,如下图所示。
书籍连载章节中转38821.png
3.13-13MATLAB显示滤波器系数文件

将下图第一行的数据复制粘贴到txt文件中,得到滤波器系数。
书籍连载章节中转38916.png
3.13-14MATLAB打开滤波器文件

特别提示:复制后需在两个系数间插入英文状态下的逗号即可得到滤波器的系数,如下图所示。
书籍连载章节中转39022.png
3.13-15复制系数到TXT文本

步骤二:新建FPGA工程

打开软件“Quartus”,点击“File”菜单下的“New Project Wizard”。
书籍连载章节中转39142.png
3.13-37QUARTUS新建工程命令

弹出Introduction”界面后点击“Next”。
书籍连载章节中转39233.png
3.13-37 QUARTUS新建工程介绍界面

设置工程目录(目录为D:\mdy_book\fir_prj,工程名和顶层名为fir_prj)后点击“Next”。
书籍连载章节中转39356.png
3.13-37 QUARTUS设置新工程路径和名称

选择“Empty project”后点击“Next”。
书籍连载章节中转39453.png
3.13-37 QUARTUS设置新工程类型

此界面不选择任何文件,点击“Next”。
书籍连载章节中转39539.png
3.13-37 QUARTUS添加文件到工程界面

对芯片型号进行选择,在“Device family”选项中选择“Cyclone ⅣE”,“Available devices”选项下选择“EP4CE15F23C8”,随后点击“Next”。
书籍连载章节中转39701.png
3.13-37 QUARTUS设置新工程的芯片型号

步骤三:FPGA生成FIR IP

建立工程后在软件“Quartus”的“IP catalog”界面中选择DSP”目录下“Filter”的“FIR II”选项,如下图所示。
书籍连载章节中转39858.png
3.13-37IP Catalog查找FIR IP

选择路径为D:\mdy_book\fir_prj\my_fir.v,随后选择“Verilog”并点击“OK”。
书籍连载章节中转39983.png
3.13-37设置FIR IP核名称和类型界面

随后对IP核类型进行设置。Filter Type”选择“Single Rate”,“Clock Rate”填写“50MHz”,“Input Sample Rate (PSPS)”填写“12.5MHz”,随后点击“coefficients”选项卡。
书籍连载章节中转40174.png
3.13-37设置FIR IP核类型界面

单击import from file”选项,导入文件“my_fir_coe.txt”后点击“import”。
书籍连载章节中转40293.png
3.13-37 FIR IP核系数设置界面

设置Input/Output Options”选项卡后点击“Finish”。
书籍连载章节中转40397.png
3.13-37 FIR IP核输入输出设置界面

生成FIR IP核界面如下所示。
书籍连载章节中转40480.png
3.13-37生成FIR IP成功提示界面

IP核生成后会弹出如下图所示的对话框,点击“yes”将此IP核添加进工程。
书籍连载章节中转40582.png
3.13-37FIR IP核加入工程提示界面

步骤四:例化FIR IP

打开shiD:\mdy_book\fir_prj\my_fir.v文件,生成的FIR IP核文件如下图所示。
书籍连载章节中转40717.png
3.13-50FIR IP核模块和输出输出信号

FIR IP核例化,具体代码如下:
1
2
3
4
5
6
7
8
assign fir_din = sin_data - 128;

my_firu_my_fir(
.clk             (clk     ) ,              
.reset_n         (rst_n   ) ,         
.ast_sink_data   (fir_din ) ,   
.ast_sink_valid  (1       ) ,   
.ast_sink_error  (0       ) ,   
.ast_source_data (fir_dout) ,  
.ast_source_valid(        ) ,
.ast_source_error(        )   
);

5.2.2 DA接口信号设计
设计信号dac_da
1
2
3
4
5
6
7
8
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
dac_da<= 0;
    end
    else begin
dac_da<= 255 - sin_data;
    end
end

设计信号dac_sleepdac_clkadac_wra
1
2
3
assign dac_sleep = 0        ;
assign dac_wra   = dac_clka ;
assign dac_clka  = ~clk      ;

设计信号dac_db
1
2
3
4
5
6
7
8
assign fir_dout2 = fir_dout + 128;
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
dac_db<= 0;
    end
    else begin
dac_db<= 255 - fir_dout2;
    end
end

设计信号dac_clkbdac_wrb
1
2
3
assign dac_wrb   = dac_clkb ;
assign dac_clkb  = ~clk      ;

至此,模块主体已经完成,接下来将module补充完整。

5.2.3 信号定义
首先定义信号类型,addr的信号定义如下:
1
wire    [6:0]    addr    ;

addr_tmp的信号定义如下:
1
reg   [16:0]    addr_tmp    ;

sin_data的信号定义如下:
1
reg   [7:0]    sin_data    ;

fir_din的信号定义如下:
1
wire    [7:0]    fir_din    ;

fir_dout的信号定义如下:
1
wire    [7:0]    fir_dout    ;

fir_dout2的信号定义如下:
1
wire    [7:0]    fir_dout2    ;

dac_dadac_sleepdac_wradac_clka、dac_mode的信号定义如下:
1
reg   [7:0]    dac_da    ;
wire          dac_sleep  ;
wire          dac_wra  ;
wire          dac_clka  ;
wire          dac_mode ;

dac_dbdac_wrbdac_clkb的信号定义如下:
1
reg   [7:0]    dac_db    ;
wire          dac_wrb  ;
wire          dac_clkb  ;

在代码的最后一行写下endmodule
1
endmodule

至此,整个代码的设计工作已经完成。完整版的工程代码如下:
  1. module fir_prj(

  2. clk  ,

  3. rst_n  ,

  4. key       ,

  5. dac_mode ,

  6. dac_sleep ,

  7. dac_clka  ,

  8. dac_da  ,

  9. dac_wra  ,

  10. dac_clkb  ,

  11. dac_db  ,

  12. dac_wrb

  13.            );



  14. input             clk  ;

  15. input             rst_n  ;

  16. input  [ 3-1:0]    key        ;

  17. output            dac_mode ;

  18. output            dac_clka  ;

  19. output [ 8-1:0]    dac_da  ;

  20. output            dac_wra  ;

  21. output            dac_sleep ;

  22. output            dac_clkb  ;

  23. output [ 8-1:0]    dac_db  ;

  24. output            dac_wrb  ;



  25. wire    [6:0]    addr    ;

  26. reg   [16:0]    addr_tmp    ;

  27. reg   [7:0]    sin_data    ;

  28. wire    [7:0]    fir_din    ;

  29. wire    [7:0]    fir_dout    ;

  30. wire    [7:0]    fir_dout2    ;

  31. reg   [7:0]    dac_da    ;

  32. wire          dac_sleep  ;

  33. wire          dac_wra  ;

  34. wire          dac_clka  ;

  35. wire          dac_mode ;





  36. always  @(*)begin

  37.     case(addr)

  38.           0: sin_data = 8'h7F;

  39.           1: sin_data = 8'h85;

  40.           2: sin_data = 8'h8C;

  41.           3: sin_data = 8'h92;

  42.           4: sin_data = 8'h98;

  43.           5: sin_data = 8'h9E;

  44.           6: sin_data = 8'hA4;

  45.           7: sin_data = 8'hAA;

  46.           8: sin_data = 8'hB0;

  47.           9: sin_data = 8'hB6;

  48.          10: sin_data = 8'hBC;

  49.          11: sin_data = 8'hC1;

  50.          12: sin_data = 8'hC6;

  51.          13: sin_data = 8'hCB;

  52.          14: sin_data = 8'hD0;

  53.          15: sin_data = 8'hD5;

  54.          16: sin_data = 8'hDA;

  55.          17: sin_data = 8'hDE;

  56.          18: sin_data = 8'hE2;

  57.          19: sin_data = 8'hE6;

  58.          20: sin_data = 8'hEA;

  59.          21: sin_data = 8'hED;

  60.          22: sin_data = 8'hF0;

  61.          23: sin_data = 8'hF3;

  62.          24: sin_data = 8'hF5;

  63.          25: sin_data = 8'hF7;

  64.          26: sin_data = 8'hF9;

  65.          27: sin_data = 8'hFB;

  66.          28: sin_data = 8'hFC;

  67.          29: sin_data = 8'hFD;

  68.          30: sin_data = 8'hFE;

  69.          31: sin_data = 8'hFE;

  70.          32: sin_data = 8'hFE;

  71.          33: sin_data = 8'hFE;

  72.          34: sin_data = 8'hFE;

  73.          35: sin_data = 8'hFD;

  74.          36: sin_data = 8'hFC;

  75.          37: sin_data = 8'hFA;

  76.          38: sin_data = 8'hF8;

  77.          39: sin_data = 8'hF6;

  78.          40: sin_data = 8'hF4;

  79.          41: sin_data = 8'hF1;

  80.          42: sin_data = 8'hEF;

  81.          43: sin_data = 8'hEB;

  82.          44: sin_data = 8'hE8;

  83.          45: sin_data = 8'hE4;

  84.          46: sin_data = 8'hE0;

  85.          47: sin_data = 8'hDC;

  86.          48: sin_data = 8'hD8;

  87.          49: sin_data = 8'hD3;

  88.          50: sin_data = 8'hCE;

  89.          51: sin_data = 8'hC9;

  90.          52: sin_data = 8'hC4;

  91.          53: sin_data = 8'hBE;

  92.          54: sin_data = 8'hB9;

  93.          55: sin_data = 8'hB3;

  94.          56: sin_data = 8'hAD;

  95.          57: sin_data = 8'hA7;

  96.          58: sin_data = 8'hA1;

  97.          59: sin_data = 8'h9B;

  98.          60: sin_data = 8'h95;

  99.          61: sin_data = 8'h8F;

  100.          62: sin_data = 8'h89;

  101.          63: sin_data = 8'h82;

  102.          64: sin_data = 8'h7D;

  103.          65: sin_data = 8'h77;

  104.          66: sin_data = 8'h70;

  105.          67: sin_data = 8'h6A;

  106.          68: sin_data = 8'h64;

  107.          69: sin_data = 8'h5E;

  108.          70: sin_data = 8'h58;

  109.          71: sin_data = 8'h52;

  110.          72: sin_data = 8'h4C;

  111.          73: sin_data = 8'h46;

  112.          74: sin_data = 8'h41;

  113.          75: sin_data = 8'h3C;

  114.          76: sin_data = 8'h36;

  115.          77: sin_data = 8'h31;

  116.          78: sin_data = 8'h2C;

  117.          79: sin_data = 8'h28;

  118.          80: sin_data = 8'h23;

  119.          81: sin_data = 8'h1F;

  120.          82: sin_data = 8'h1B;

  121.          83: sin_data = 8'h17;

  122.          84: sin_data = 8'h14;

  123.          85: sin_data = 8'h11;

  124.          86: sin_data = 8'hE ;

  125.          87: sin_data = 8'hB ;

  126.          88: sin_data = 8'h9 ;

  127.          89: sin_data = 8'h7 ;

  128.          90: sin_data = 8'h5 ;

  129.          91: sin_data = 8'h3 ;

  130.          92: sin_data = 8'h2 ;

  131.          93: sin_data = 8'h1 ;

  132.          94: sin_data = 8'h1 ;

  133.          95: sin_data = 8'h1 ;

  134.          96: sin_data = 8'h1 ;

  135.          97: sin_data = 8'h1 ;

  136.          98: sin_data = 8'h2 ;

  137.          99: sin_data = 8'h3 ;

  138.         100: sin_data = 8'h4 ;

  139.         101: sin_data = 8'h6 ;

  140.         102: sin_data = 8'h7 ;

  141.         103: sin_data = 8'hA ;

  142.         104: sin_data = 8'hC ;

  143.         105: sin_data = 8'hF ;

  144.         106: sin_data = 8'h12;

  145.         107: sin_data = 8'h15;

  146.         108: sin_data = 8'h19;

  147.         109: sin_data = 8'h1D;

  148.         110: sin_data = 8'h21;

  149.         111: sin_data = 8'h25;

  150.         112: sin_data = 8'h2A;

  151.         113: sin_data = 8'h2E;

  152.         114: sin_data = 8'h33;

  153.         115: sin_data = 8'h38;

  154.         116: sin_data = 8'h3E;

  155.         117: sin_data = 8'h43;

  156.         118: sin_data = 8'h49;

  157.         119: sin_data = 8'h4E;

  158.         120: sin_data = 8'h54;

  159.         121: sin_data = 8'h5A;

  160.         122: sin_data = 8'h60;

  161.         123: sin_data = 8'h67;

  162.         124: sin_data = 8'h6D;

  163.         125: sin_data = 8'h73;

  164.         126: sin_data = 8'h79;

  165.         127: sin_data = 8'h7F;

  166. endcase

  167. end



  168. always  @(posedge clk or negedge rst_n)begin

  169.     if(rst_n==1'b0)begin

  170. addr_tmp<= 0;

  171.     end

  172.     else if(key==0) begin

  173. addr_tmp<= addr_tmp + 262;

  174.     end

  175.     else if(key==1) begin

  176. addr_tmp<= addr_tmp + 524;

  177.     end

  178.     else if(key==2) begin

  179. addr_tmp<= addr_tmp + 786;

  180.     end

  181.     else if(key==3) begin

  182. addr_tmp<= addr_tmp + 1029;

  183.     end

  184.     else if(key==4) begin

  185. addr_tmp<= addr_tmp + 1311;

  186. end

  187.     else if(key==5) begin

  188. addr_tmp<= addr_tmp + 1573;

  189.     end

  190.     else if(key==6) begin

  191. addr_tmp<= addr_tmp + 1835;

  192.     end

  193.     else begin

  194. addr_tmp<= addr_tmp + 2097;

  195.     end

  196. end





  197. assign addr = addr_tmp>>10 ;

  198. assign fir_din = sin_data - 128;



  199. my_firu_my_fir(

  200. .clk             (clk     ) ,              

  201. .reset_n         (rst_n   ) ,         

  202. .ast_sink_data   (fir_din ) ,   

  203. .ast_sink_valid  (1       ) ,   

  204. .ast_sink_error  (0       ) ,   

  205. .ast_source_data (fir_dout) ,  

  206. .ast_source_valid(        ) ,

  207. .ast_source_error(        )   

  208. );





  209. always  @(posedge clk or negedge rst_n)begin

  210.     if(rst_n==1'b0)begin

  211. dac_da<= 0;

  212.     end

  213.     else begin

  214. dac_da<= 255 - sin_data;

  215.     end

  216. end





  217. assign dac_sleep = 0        ;

  218. assign dac_wra   = dac_clka ;

  219. assign dac_clka  = ~clk      ;



  220. assign fir_dout2 = fir_dout + 128;

  221. always  @(posedge clk or negedge rst_n)begin

  222.     if(rst_n==1'b0)begin

  223. dac_db<= 0;

  224.     end

  225.     else begin

  226. dac_db<= 255 - fir_dout2;

  227.     end

  228. end



  229. assign dac_wrb   = dac_clkb ;

  230. assign dac_clkb  = ~clk      ;



  231. endmodule
复制代码


5.3 综合与上板


5.3.1 添加文件

上一节已经介绍了新建工程的过程,这里不再赘述了。

打开“Quartus”软件,在“Project”菜单中选择“Add/Remove File to Project”后弹出文件窗口。
书籍连载章节中转49773.png
3.13-50添加文件命令界面

点击右上角的 书籍连载章节中转49836.png 按钮,在弹出来的窗口中双击选择D:\mdy_book\fir_prj目录下的fir_prj.vfir_prj.qip文件。点击Add”添加成功后关闭窗口。
书籍连载章节中转49917.png
3.13-50添加文件成功界面

5.3.2 综合
在菜单栏中选中Processing”后选择“Start Compilation”,开始对整个工程进行编译和综合。
书籍连载章节中转50038.png
3.13-50编译命令界面

当出现下图的界面则说明编译综合成功。
书籍连载章节中转50113.png
3.13-50编译成功界面

5.3.3 配置管脚
配置管脚界面如下图所示,在菜单栏中选中“Assignments”后选择“Pin Planner”,随后就会弹出配置管脚的窗口。
书籍连载章节中转50238.png
3.13-50配置管脚命令界面

在配置窗口“location”配置管脚,配置完成关闭“Pin Planner”即可自动保存配置信息。
书籍连载章节中转50347.png file:///C:/Users/Administrator/Desktop/%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%872/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC.files/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC50238.png
3.13-50配置管脚界面

5.3.4 再次综合file:///C:/Users/Administrator/Desktop/%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%872/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC.files/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC50347.png
再次打开“QUARTUS”软件,在菜单栏中选中“Processing”后选择“Start Compilation”,再次对整个工程进行编译和综合,如下图所示。
书籍连载章节中转50489.png
3.13-50编译命令界面
当出现3.2-19QUARTUS编译成功标志则说明编译综合成功。file:///C:/Users/Administrator/Desktop/%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%872/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC.files/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC50489.png
书籍连载章节中转50602.png
3.13-50编译成功界面

5.3.5 连接开发板file:///C:/Users/Administrator/Desktop/%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%872/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC.files/%E4%B9%A6%E7%B1%8D%E8%BF%9E%E8%BD%BD%E7%AB%A0%E8%8A%82%E4%B8%AD%E8%BD%AC50602.png
开发板连接方式如下图所示。将电源接上开发板USB BLASTER一端连接到JTAG插口,另一端连到PCUSB接口。将开发板上的AD接口和DA与示波器的两个通道相连,连接完成后将电源打开。
书籍连载章节中转50760.png
3.13-50教学板连接示意图

5.3.6 上板

“Quartus”的“Task”窗口中,右键“Program Device”选择“Open”进入烧录界面,如下图所示。
书籍连载章节中转50882.png
3.13-50打开配置程序命令

默认选中文件output/fir_prj.sof,在Hardware Setup旁边会显示USB-Blaster,如下图所示。
书籍连载章节中转51004.png
3.13-50配置程序界面

进度条提示成功后可在示波器上观察相应现象。
书籍连载章节中转51082.png
3.13-50配置成功界面

第6节 扩展练习
至此,整个FIR滤波器设计就分享完毕了,希望读者朋友们学会了整个设计之后也可以展开更多思考,在学会低通滤波器的基础上尝试一下高通滤波器的设计。基于原理不变的情况下,多做一些尝试,这样可以帮助同学们更深刻的掌握案例。也欢迎有更多思路和想法的同学前往至简设计法论坛进行交流讨论。


下面是工程使用的系数文件 my_fir_coe.txt (1.1 KB, 下载次数: 679)

0

主题

1

帖子

106

积分

注册会员

Rank: 2

积分
106
发表于 2021-5-19 10:58:39 | 显示全部楼层

删不了留言

本帖最后由 923161517 于 2021-5-19 17:11 编辑

删不了留言

0

主题

1

帖子

85

积分

注册会员

Rank: 2

积分
85
发表于 2022-4-1 16:22:03 来自手机 | 显示全部楼层
有没有测试文件可以参考一下啊?{:9001:}
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|MDYBBS ( 粤ICP备16061416号 )

GMT+8, 2024-4-26 16:36 , Processed in 0.147920 second(s), 24 queries .

Powered by Discuz! X3.4

本论坛由广州健飞通信有限公司所有

© 2001-2019 Comsenz Inc.

快速回复 返回顶部 返回列表