数字信号处理——多速率信号处理(4)
本文主要介绍了多速率信号处理中FIR滤波器的设计与实现过程。文章从引言开始,详细描述了FIR滤波器的整体架构及其核心模块的设计思路。总体框图展示了FIR滤波器的基本组成:系数产生模块用于生成滤波器系数;Coefficient store模块负责将生成的系数存储到ROM中;Coefficient read module则负责从ROM中读取并分配到RAM中进行处理。
在“Coefficient produce”部分,使用了MATLAB工具生成并量化了FIR滤波器的系数,并通过Verilog HDL语言实现了Coefficient read module的设计与仿真验证。该模块采用分步读取的方式从ROM中获取数据,并将其转换为所需宽度(64位)进行处理。仿真结果表明该设计能够正确完成数据转换与分配任务。
此外,文章还对RAM和ROM的具体IP配置进行了说明,并通过时序图展示了整个系统的运行流程及各模块之间的协作关系。整体而言,文章系统地阐述了多速率信号处理中基于FIR滤波器的设计方法及其硬件实现技术。

目录
引言
总体框图
FIR 滤波器设计
系数产生
ROM IP配置
RAM IP配置
系数读取分配模块
引言
本文继续介绍 FIR 滤波器系数的产生、存储、读取和分配设计。
前文链接:
本文接续前面几篇:
本节主要介绍数字信号处理中的多速率信号处理技术及其应用
在本节中讨论了数字信号的多速率采样与重构技术(Second Part/Chapter)
本节主要介绍
总体框图

说明:该系统中滤波器系数采用16位量化精度,在处理阶段系统能够连续并行读取四个数据块,并将这些数据被分配至四条并行处理路径供滤波器组使用。其中RAM输出端的数据采用64位宽度以保证信号完整性,在完成四个数据块采集后直接进入下一处理阶段以实现高效运行。
FIR 滤波器设计
系数产生
利用MATLAB产生、存储系数的代码如下:
> 1. %% ----------------------- Anti aliasing filter design and store -----------------------
>
> 2. % | Author :Xu Y. B.( NICK NAME:在路上,正出发)
>
> 3. % | Time :2022 10 09
>
> 4. % | Store File Type :.coe(Vivado ROM Initialize File)
>
> 5. % | Filter Parameter:Changeable
>
> 6. % -----------------------------------------------------------------------------------------
>
> 7.
>
> 8. %% Preparation For Start
>
> 9. clc; % Clear Screen
>
> 10. clearvars; % Clear All Variables
>
> 11. close all; % Close All
>
> 12.
>
> 13. %% Parameter Settings
>
> 14. fs_int = 100e6;% Initial Sample Rate ;Unit:Hz
>
> 15. fs_dcm = 25e6; % Decimate Sample Rate ;Unit:Hz
>
> 16. dcm_factor = 4;% Decimator factor
>
> 17.
>
> 18. filter_length = 32;
>
> 19. coef_width = 16;
>
> 20.
>
> 21. %% Filter Design
>
> 22. H = fir1(filter_length-1,1/dcm_factor,"low");
>
> 23. figure;
>
> 24. freqz(H,1)
>
> 25.
>
> 26. %% Coefficient Store
>
> 27. H_QUAN = (round(H/(max(abs(H)))*(2^(coef_width-1)-1))).';
>
> 28. FILE_NAME = ['D:\VIVADO_WORK_SPACE\Multirate_SP\Multirate_SP.srcs\COEF_FILE\','FIR_COEF.coe'];
>
> 29. fid_coe=fopen(FILE_NAME,'w+'); % Get file id
>
> 30. fprintf(fid_coe,'memory_initialization_radix=10;\n'); % Write the first line
>
> 31. fprintf(fid_coe,'memory_initialization_vector=\n'); % Write the second line
>
> 32. fprintf(fid_coe,'%d,\n',H_QUAN((1:end-1),1)); % Write data
>
> 33. fprintf(fid_coe,'%d;',H_QUAN(end,1)); % Write last data
>
> 34. 35. % Close Files
>
> 36. fclose(fid_coe);
>
>
>
>
> AI助手
注意:
抗混叠滤波器的截止频率为 1/D。(对于fir1函数而言)
ROM IP配置



RAM IP配置



系数读取分配模块
Verilog HDL 设计源码
> 1. // |------------------------------------------------------------------------------
>
> 2. // |------------------- FIR Filter Coefficient Read Module -----------------------
>
> 3. // |------------------------------------------------------------------------------
>
> 4. // |
>
> 5. // |********* Basic Information Specify *********
>
> 6. // |Author : Xu Y.B. ( 昵称:在路上,正出发)
>
> 7. // |Abstract :
>
> 8. // | -1- FIR Filter Coefficient Width is 16 .
>
> 9. // | -2- FIR Filter Length is 32.(Per Channel has 32/4=8 Coefficients)
>
> 10. // | -3- RAM is Read After ALL the Coefficients is Stored In RAM.
>
> 11. // |FPGA Chirp Model : xc7a35tfgg484-2
>
> 12. // |Vivado Version : 2018.3
>
> 13. // |File Create Time : 2022-09-25
>
> 14. // |Advice : This is a example design source.You can also use shift register to design this module,not using IP cores
>
> 15. // | It is better to understand and improve the source code!
>
> 16. // |
>
> 17. // |********* Version Change History *********
>
> 18. // |
>
> 19. // |
>
> 20.
>
> 21. `timescale 1ns / 1ps
>
> 22. module COEF_READ_MDL(
>
> 23. // ------------------------- Input/Output Ports -------------------------
>
> 24. input I_OPR_CLK,
>
> 25. input I_OPR_RSTN,
>
> 26. 27. output O_COEF_VAL,
>
> 28. output [63:0] O_COEF_DATA
>
> 29. );
>
> 30. // ---------------------- Module Internal Signals ------------------------
>
> 31. reg R_ROM_RD_EN;
>
> 32. reg R_ROM_RD_EN_R1;
>
> 33. reg [4:0] R_ROM_RD_ADDRA;
>
> 34. wire [15:0] W_ROM_OUT_DATA;
>
> 35. reg R_ROM_OUT_VAL;
>
> 36. reg R_ROM_OUT_VAL_R1;
>
> 37.
>
> 38. reg [4:0] R_RAM_WR_ADDRA;// Read write shared address line
>
> 39. wire [63:0] W_RAM_OUT_DATA;
>
> 40. reg R_RAM_OUT_VAL;
>
> 41. reg R_RAM_OUT_VAL_R1;
>
> 42.
>
> 43. // ------------------------ Module Logic Design --------------------------
>
> 44. // ROM Read
>
> 45. always @ (posedge I_OPR_CLK)
>
> 46. begin
>
> 47. if(~I_OPR_RSTN)
>
> 48. begin
>
> 49. R_ROM_RD_ADDRA <= 0;
>
> 50. R_ROM_RD_EN <= 1;
>
> 51. end
>
> 52. else
>
> 53. begin
>
> 54. if(R_ROM_RD_ADDRA == 31)
>
> 55. begin
>
> 56. R_ROM_RD_ADDRA <= 31;
>
> 57. R_ROM_RD_EN <= 0;
>
> 58. end
>
> 59. else
>
> 60. begin
>
> 61. R_ROM_RD_EN <= 1;
>
> 62. R_ROM_RD_ADDRA <= R_ROM_RD_ADDRA + 1;
>
> 63. end
>
> 64. end
>
> 65. end
>
> 66. always @ (posedge I_OPR_CLK)
>
> 67. begin
>
> 68. if(~I_OPR_RSTN)
>
> 69. begin
>
> 70. R_ROM_RD_EN_R1 <= 0;
>
> 71. R_ROM_OUT_VAL <= 0;
>
> 72. R_ROM_OUT_VAL_R1 <= 0;
>
> 73. end
>
> 74. else
>
> 75. begin
>
> 76. R_ROM_RD_EN_R1 <= R_ROM_RD_EN;
>
> 77. R_ROM_OUT_VAL <= R_ROM_RD_EN_R1;
>
> 78. R_ROM_OUT_VAL_R1 <= R_ROM_OUT_VAL;
>
> 79. end
>
> 80. end
>
> 81.
>
> 82.
>
> 83. // RAM Write
>
> 84. always @ (posedge I_OPR_CLK)
>
> 85. begin
>
> 86. if(~I_OPR_RSTN)
>
> 87. begin
>
> 88. R_RAM_WR_ADDRA <= 0;
>
> 89. end
>
> 90. else
>
> 91. begin
>
> 92. if(R_ROM_RD_EN_R1)
>
> 93. begin
>
> 94. if(R_ROM_RD_EN_R1 & (~R_ROM_OUT_VAL))
>
> 95. begin
>
> 96. R_RAM_WR_ADDRA <= 0;
>
> 97. end
>
> 98. else
>
> 99. begin
>
> 100. R_RAM_WR_ADDRA <= R_RAM_WR_ADDRA + 1;
>
> 101. end
>
> 102. end
>
> 103. else
>
> 104. begin
>
> 105. R_RAM_WR_ADDRA <= 0;
>
> 106. end
>
> 107. end
>
> 108. end
>
> 109.
>
> 110. always @ (posedge I_OPR_CLK)
>
> 111. begin
>
> 112. if(~I_OPR_RSTN)
>
> 113. begin
>
> 114. R_RAM_OUT_VAL <= 0;
>
> 115. end
>
> 116. else
>
> 117. begin
>
> 118. if(R_RAM_WR_ADDRA % 4 == 3)
>
> 119. begin
>
> 120. R_RAM_OUT_VAL <= 1;
>
> 121. end
>
> 122. else
>
> 123. begin
>
> 124. R_RAM_OUT_VAL <= 0;
>
> 125. end
>
> 126. end
>
> 127. end
>
> 128.
>
> 129. always @ (posedge I_OPR_CLK)
>
> 130. begin
>
> 131. if(~I_OPR_RSTN)
>
> 132. begin
>
> 133. R_RAM_OUT_VAL_R1 <= 0;
>
> 134. end
>
> 135. else
>
> 136. begin
>
> 137. R_RAM_OUT_VAL_R1 <= R_RAM_OUT_VAL;
>
> 138. end
>
> 139. end
>
> 140. 141. ROM_32X16_COEF ROM_32X16_COEF_INST (
>
> 142. .clka(I_OPR_CLK), // input wire clka
>
> 143. .ena(R_ROM_RD_EN|R_ROM_RD_EN_R1), // input wire ena
>
> 144. .addra(R_ROM_RD_ADDRA), // input wire [4 : 0] addra
>
> 145. .douta(W_ROM_OUT_DATA) // output wire [15 : 0] douta
>
> 146. );
>
> 147.
>
> 148. RAM_COEF_READ RAM_COEF_READ_INST (
>
> 149. .clka(I_OPR_CLK), // input wire clka
>
> 150. .ena(1'b1), // input wire ena
>
> 151. .wea(R_ROM_OUT_VAL), // input wire [0 : 0] wea
>
> 152. .addra(R_RAM_WR_ADDRA), // input wire [4 : 0] addra
>
> 153. .dina(W_ROM_OUT_DATA), // input wire [15 : 0] dina
>
> 154. .douta(W_RAM_OUT_DATA) // output wire [63 : 0] douta
>
> 155. );
>
> 156.
>
> 157. assign O_COEF_DATA = W_RAM_OUT_DATA;
>
> 158. assign O_COEF_VAL = R_RAM_OUT_VAL_R1;
>
> 159. endmodule
>
>
>
>
> AI助手
仿真源码:
> 1. `timescale 1ns / 1ps
>
> 2. 3. module TB_COEF_READ_MDL();
>
> 4. // ------------------------- Input/Output Ports -------------------------
>
> 5. reg I_OPR_CLK;
>
> 6. reg I_OPR_RSTN;
>
> 7.
>
> 8. wire O_COEF_VAL;
>
> 9. wire [63:0] O_COEF_DATA;
>
> 10.
>
> 11. initial I_OPR_CLK = 0;
>
> 12. always #20 I_OPR_CLK = ~I_OPR_CLK;
>
> 13. initial
>
> 14. begin
>
> 15. I_OPR_RSTN = 0;
>
> 16. #1002;
>
> 17. I_OPR_RSTN = 1;
>
> 18. @(negedge O_COEF_VAL);
>
> 19. #1000;
>
> 20. $stop;
>
> 21. end
>
> 22. COEF_READ_MDL INST_COEF_READ_MDL
>
> 23. (
>
> 24. .I_OPR_CLK (I_OPR_CLK),
>
> 25. .I_OPR_RSTN (I_OPR_RSTN),
>
> 26. .O_COEF_VAL (O_COEF_VAL),
>
> 27. .O_COEF_DATA (O_COEF_DATA)
>
> 28. );
>
> 29.
>
> 30. endmodule
>
>
>
>
> AI助手
仿真时序:

说明:
RAM模块完成位宽转换无需依赖IP核来实现功能。
可以通过移位寄存器来实现。
具体建议参考本人下面的文章:
数据位宽转换(任意整数倍)设计——思路和源码
