官方论坛
官方淘宝
官方博客
微信公众号
点击联系吴工 点击联系周老师

2.14 FIR滤波器设计

发布时间:2021-08-22   作者:admin 浏览量:

    官网连载的内容将出版成图书,并将录制视频,免费公开学习,欢迎大家留意。本连载前面是基础部分,与一般教材无异,后面是项目实践,是本连载的特色。如果你有一定的基础(能看懂verilog代码即可),那么可跳过前面部分,直接学习后面的项目实践。项目实践将有16个,从基础的闪烁灯开始,到最后是信号处理的项目,如信号发生器、FIR滤波器、插值滤波器和AD采集等。 

    本连载学习效果:不仅看能懂代码,还能知道每一行代码怎么写,怎么设计。


第二阶段  项目实践

本文的文档编号:000600000025

需要看对应的视频,请点击视频编号:002700000452

1、本文档讲述FPGA产生两路正弦波数据,一路直接由DA输出,一路经过FIR处理之后输出,然后传到示波器进行观察,从而了解FIR滤波器的效果
2、801开发板使用

                         

第十四章  FIR滤波器设计


1 项目背景

1.1 FIRIIR滤波器

    FIRFinite Impulse ResponseFilter:有限冲激响应滤波器,又称为非递归线性滤波器。

    FIR滤波器,顾名思义,其脉冲响应由有限个采样值构成。长度(抽头数)为N、阶数为N?1的FIR系统的转移函数、差分方程和单位冲激响应分别如下列三式所示。

 

510

    IIRInfinite Impulse ResponseFilter:无限冲激响应滤波器,又称为递归线性滤波器。

    FIR相对与IIR来说,具有如下的优点:


  • 可以具备线性相位特性


    线性相位的概念: 如果滤波器的N个实值系数为对称或者反对称结构,该滤波器具有线性相位。 W(n)=±W(N?1?n)W(n)=±W(N?1?n)

    线性相位的特性:通过线性相位滤波器的信号的所有频率部分具有相同的延迟量。 


  • 易于设计


       但FIR也有自身的缺点:同样指标的滤波器,FIR需要更多的参数,即实现时消耗更多的计算单元,产生更大的延迟。


1.2 FIR滤波器的原理

    信号通过一个FIR滤波器其实就是信号与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

    简单的滤波器模型

    低通滤波器:1 1

    信号1与低通滤波器进行卷积运算,其结果再除以2,得到如下数据:1 1 1 1.5 2 2 2。可以看到,低频信号经过低通滤波器后,各个点仍然保持了其形状,而且在1变成2时,还变平缓了。

    信号2与低通滤波器进行卷积运算,其结果再除以2,得到如下数据:1 1 1 1 1 1 1。可以看到,直流信号与输入的信号完成相同。

    信号3与低通滤波器进行卷积运算,其结果再除以2,得到如下数据:1.5 1.5 1.5 1.5 1.5 1.5 1.5。可以看到,高频信号经过低通滤波器后,已经完成消去了形状,变成了直流信号。

    再考虑另一种滤波器模型,高通滤波器:1 -1

    信号1与高通滤波器进行卷积运算,其结果再除以2,得到如下数据:0 0 0 -0.5 0 0 0。可以看到,低频信号经过高通滤波器后,信号变化基本上消失。

    信号2与低通滤波器进行卷积运算,其结果再除以2,得到如下数据:0 0 0 0 0 0 0。可以看到,直流信号仍然是没有变化。

    信号3与低通滤波器进行卷积运算,其结果再除以2,得到如下数据:-0.5 0.5 -0.5 0.5 -0.5 0.5 -0.5 0.5。可以看到,高频信号已经仍然保持了变化的形状。

    由这两个例子可以看出,FIR滤波器其实就是信号与FIR滤波器的系数进行卷积(即乘累加)的过程。通过调整滤波器系数、抽头个数,就可实现低通、高通、带通等滤波器。


1.3 FIR滤波器的设计

1.3.1 matlab产生滤波器系数

    打开matlab在其命令窗口输入fdatool 按下回车

 

    调出FIR滤波器的设计界面

511

    在波形设计界面中,我们重要关注以下选项。

    Response Type:选择可以选择滤波器的类型,可选择:lowpass低通滤波器、Highpass高通滤波器、bandpass带通滤波器、bandstop带阻滤波器。

    Fs采样频率):

    Fstop :信号截止频率

    Fpass

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

    点击Design Filter,就可以计算出抽头系数。

    产生系数后点击file 菜单里的Export 将系数保存到工作区

 

512

    点击export

 

513

    点击之后打开工作区里的Num

514

    而后将下图第一列的数据复制粘贴到txt文件中

 

515

    注意复制后需在两个系数间插入逗号(英文输入状态下的的逗号)

516

    这样就得到滤波器的系数了。


1.3.2 FPGA生成FIR IP

    打开工程后,在IP catalog这一界面中选择DSP下一目录中选择Filter 在选择选择 FIR II

 

517

    首先在Fitter这一界面做如下操作

 

518

    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界面的波形发生变化。

519

    在coefficient界面,还可以设置系数的格式、数据位宽等。

520

    其它选项不改按默认的来点击finish即可。

    以上就是生成 FIR滤波器的主要步骤。


2 设计目标

    本次案例将使用到采样率大于100M的双通道的示波器。将示波器的两个通道,分别与FPGADA通道1DA通道2相连,观察两路DA的输出。其连接示意如下图所示。

521

    本案例是FPGA内部产生正弦信号,这个正弦信号一路输出给DA通道1,另一路经过FIR滤波器后,输出给DA通道2

522

    正弦信号的频率受开发板上的3个拨码开关控制,用3位信号key表示,一共可以产生8种频率。

    正弦信号的频率 约等于: 100KHz * (key+1)

    例如,当key等于0时,产生约100KHz的正弦信号;

    当key等于1时,产生约200KHz的正弦波;

    当key等于7时,产生约800KHz的正弦波。

    FIR滤波器是低通滤波器,其截止频率是500KHz,这样原则上超过500KHz的信号就会被滤除。滤波器的输出给通道2

    下面是示波器的显示效果,其中黄色是通道1输出的信号(上面的波形),下面蓝色是通道2的输出信号(下面的波形)。

下图是100KHz的信号图。

100KHz的信号图

    下图是200KHz的信号图。 


200KHz的信号图

下图是300KHz的信号图。

300KHz的信号图

下图是400KHz的信号图,可以看到已经衰减了。 


400KHz的信号图

下图是500KHz的信号图,可以看到已经衰减的很小了。 


500KHz的信号图

下图是600KHz的信号图,可以看到通道2已经没有波形。 


600KHz的信号图

下图是700KHz的信号图,可以看到通道2已经没有波形。


700KHz的信号图

下图是800KHz的信号图,可以看到通道2已经没有波形。

800KHz的信号图


3 设计实现

3.1 顶层接口

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

    我们要实现的功能,概括起来就是FPGA产生控制AD9709,让其中的通道A未滤波的正弦信号,让通道B输出滤波后的正弦信号。为了控制AD9709的工作模式,就要控制AD9709MODESLEEP管脚;为了控制通道A,就需要控制AD9729CLK1WRT1DB7~0P1管脚;为了控制通道B,就需要控制AD9729CLK2WRT2DB7~0P2管脚。根据设计目标的要求,      整个工程需要以下信号:

    1. 使用clk连接到晶振,表示50M时钟的输入。

    2. 使用rst_n连接到按键,表示复位信号。

    3. 使用3位信号key,表示三位拨码开关。

    4. 使用dac_mode信号连接到AD9709MODE管脚,用来控制其工作模式。

    5. 使用dac_sleep信号连接到AD9709SLEEP管脚,用来控制其睡眠模式。

    6. 使用dac_clka信号连接到AD9709CLK1管脚,用来控制通道A的时钟。

    7. 使用dac_wra信号连接到AD9709WRT1管脚,用来控制通道A的写使能。

    8. 使用8位信号dac_da连接到AD9709DB7~0P1管脚,用来控制通道A的写数据。

    9. 使用dac_clkb号连接到AD9709CLK2脚,用来控制通道B时钟。

    10. 使用dac_wrb号连接到AD9709WRT2脚,用来控制通道B使能。

    11. 使用8位信号dac_db接到AD9709DB7~0P2脚,用来控制通道B写数据。

       综上所述,我们这个工程需要11个信号,时钟clk,复位rst_n,拨码开关的输入keydac_modedac_sleepdac_clkadac_wradac_dadac_clkbdac_wrbdac_db信号,其中dac_dadac_db8位信号,其他都是1位信号。下面表格表示了硬件电路图的连接关系。

器件

AD9709管脚

原理图信号

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

    将module的名称定义为fir_prj,代码如下:

1

2

3

4

5

6

7

8

9

module fir_prj(

           clk       ,

           rst_n     ,

           key       ,

           dac_mode ,

           dac_sleep ,

           dac_clka  ,

           dac_da   ,

           dac_wra  ,

           dac_clkb  ,

           dac_db   ,

           dac_wrb               

           );

    其中clkrst_n1位的输入信号,dac_dadac_db8位的输出信号,key3位输入信号,dac_modedac_clkadac_wradac_sleepdac_clkbdac_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   ;

 


3.2 正弦信号设计

    假设产生的正弦信号命名为sin_data信号。sin_data是从表XX中选择出来的值,该表一共有128个点。该表的产生方法,请看案例“信号发生器和DA转换”一章的内容。

采样点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 按下回车调出波形设计界面。 

     在波形设计界面中

     Response Type:案例要求滤波高于500KHz的信号,所以选择lowpass低通滤波器

     Fstop: 截止频率设为600KHz

     Fs采样频率: 12.5MHz(12500Khz)

526

    其它选项默认点击Design Filter

    产生系数后点击file 菜单里的Export 将系数保存的工作区

 

527

    点击export

 

528

    点击之后打开工作区里的Num

 

529

     而后将下图第一列的数据复制粘贴到txt文件中

 

530

    注意复制后需在两个系数间插入逗号(英文输入状态下的的逗号)

531

 

3.3.2 新建FPGA工程

 

532

    1.打开quartus,点击File File菜单中选择New Project Wizard....

 

533

    2.弹出Introduction界面选择Next

 

534 

    3.设置工程目录,工程名,顶层模块名

    工程目录设置为:D:mdy_book ir_prj

    工程名:fir_prj

    顶层模块名:fir_prj

    填写完毕后,点击next之后进入下一界面。

 

535

    工程类型界面,Project Type选择Empty project,选择空白工程。点Next进入下一个界面。

 

536

    4.在文件添加界面,不选择任何文件。点击Next,进入下一个界面。

 

537

    5.器件选择界面。在Device family这一项之中选择 Cyclone IV E;在下部的Available device 选择EP4CE6F23C8。完成后直接点击Finish


3.3.3 FPGA生成FIR IP

 

538

    建立工程后,在quartusIP catalog这一界面中选择DSP下一目录中选择Filter 再选择 FIR II

 

539

    点击后进入此界面给新生成的fir滤波器ip核选择如下路径:D:mdy_book ir_prjmy_fir.vIPvariation file name这一项选择verilog。点击OK后,进入FIR滤波器设置界面。

 

540

    在Fitter specification界面按如下设置:

    Filter Type:要选择Single Rate,表示只采用一种采样率。

    Clock Rate:因为我们工程使用的是50MHz时钟,所以此处要填写50MHz

    Input Sample Rate (PSPS):这个是填采样率。和matlab相匹配,因此要填12.5MHz

    其他参数默认。

    然后点击coefficients选项卡。

541

    单击import from file ,在输出的界面中,找出我们用MATLAB生成的系数文件:my_fir_coe.txt,点击import导入。导入成功后可以看到下图的 frequency response界面的波形发生变化。

    在Coefficient Bit Width中,填写16。表示每个系数用16比特量化。

 

543

    如上图,在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核。

 

544

    出现上面的提示,就是生成成功了。

 

545

    IP核生成后弹出此对话框点击yes 将此IP核添加进工程。


3.3.4 例化FIR IP
    GVIM打开D:mdy_book ir_prjmy_fir.v文件,该文件就是生成的FIR IP核文件。

546
    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位。 

    无符号数转成有符号数的方法很简单:fir_din = sin_data - 128。读者有兴趣可以验证一下。
    生成FIR IP核后,我们要对其进行例化,才行使用上这个IP核,例化名起名u_my_firfir的输出数据信号命名为fir_dout

1

2

3

4

5

6

7

8

assign fir_din = sin_data - 128;

 

my_fir u_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)得到。那么可以写出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

    dac_clka为了满足tS的时间要求,可以让dac_clka = ~clk

    dac_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 = ~clk

    dac_wrb可以与dac_clkb相同。

1

2

3

assign dac_wrb   = dac_clkb ;

assign dac_clkb  = ~clk      ;


3.5 信号定义

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

    addr是用assign设计的,因此类型为wire。其值最大为127,一共有7根线,位宽为7,故而代码如下

1

wire    [6:0]    addr    ;

    addr_tmp是用always设计的,因此类型为reg。如前面所述,该信号的位宽是17,故而代码如下

1

reg   [16:0]    addr_tmp    ;

    sin_data是用always设计的,因此类型为reg。其最大值为255,要有8根线表示,位宽为8,故而代码如下

1

reg   [7:0]    sin_data    ;

    fir_din是用assign设计的,因此类型为wire。其位宽为8,故而代码如下

1

wire    [7:0]    fir_din    ;

    fir_dout是用例化模块的输出,非always设计的,因此类型为wire。其位宽为8,故而代码如下

1

wire    [7:0]    fir_dout    ;

    fir_dout2是用assign设计的,非always设计的,因此类型为wire。其位宽为8,故而代码如下

1

wire    [7:0]    fir_dout2    ;

    dac_da是用always设计的,因此类型为reg。其位宽为8dac_sleep是用assign设计的,因此类型为wire,位宽为1dac_wra是用assign设计的,因此类型为wire,位宽为1dac_clka是用assign设计的,因此类型为wire,位宽为1dac_mode是用assign设计的,因此类型为wire,位宽为1。故而代码如下

1

reg   [7:0]    dac_da    ;

wire          dac_sleep  ;

wire          dac_wra   ;

wire          dac_clka   ;

wire          dac_mode ;

    dac_db是用always设计的,因此类型为reg。其位宽为8dac_wrb是用assign设计的,因此类型为wire,位宽为1dac_clkb是用assign设计的,因此类型为wire,位宽为1。故而代码如下

1

reg   [7:0]    dac_db    ;

wire          dac_wrb   ;

wire          dac_clkb   ;

    在代码的最后一行写下endmodule

1

endmodule

    至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象。


4 综合与上板

4.1 添加文件到工程

547

    1.前面已经介绍了新建工程。现在打开quartus,在Project菜单中选择Add/Remove File to Project,弹出文件窗口。

 

548

    点击右上角的,在弹出来的窗口中,双击选择D:mdy_book ir_prj目录下的fir_prj.v文件。然后记得要点Add,才算正式加到工程。

549

    点OK关闭本窗口。


4.2 综合

 

550

    在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。

 

551

    出现上面的界面,就说明编译综合成功。

4.3 配置管脚

 

552

    在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。

 

553

    在配置窗口中的location一列,可以填写每个管脚所对应的FPGA管脚号。

器件

AD9709管脚

原理图信号

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

    按上面配置好每个信号的管脚,其最终效果如下图。

 

554

    关闭Pin Planner,软件自动会保存管脚配置信息。


4.4 再次综合

 

555

    在菜单栏中,选中Processing,然后选择Start Compilation,开始对整个工程进行编译和综合。

 

556

   出现上面的界面,就说明编译综合成功。


4.5 连接开发板

557

    连接示意如上图所示。将电源接上开发板;USB BLASTER一端连接到JTAG插口,另一端连到PCUSB接口;将开发板上的P7接口和P11与示波器的两个通道相连。最后再将电源打开。


4.6 上板

 

558

    在quartusTask窗口中,右键Program Device 选择Open 进入烧录界面。

 

559

    在上面的界面中,默认会选中文件output/fir_prj.sof,如果没有生成请看XXXX

    在上面的界面中,Hardware Setup的旁边会显示:USB-Blaster。如果不是,请看XXXX

 

560

    点击statr,在progress这一条显示100%即表示成功,此时可以看FPGA输出效果了。



技术交流QQ群:97925396
更多FPGA技术资讯:微信公众号 fpga资讯
返回目录 >> 明德扬FPGA实验手册


上一篇:2.4 PWM呼吸灯
下一篇:2.5 串口通信
   拓展阅读