您现在的位置: 首页 观点 > > 正文
手把手用Verilog实现FIR滤波器
发布时间:2023-06-19 12:12:31 来源:Bug记录
设计思想

首先需要把FIR最基本的结构实现,也就是每个FIR抽头的数据与其抽头系数相乘这个操作。由顶层文件对这个基本模块进行多次调用。

由于FIR抽头系数是中心对称的,为了减少乘法在FPGA内的出现,每个基本结构同时会输入两个信号,也是关于中心对称的。


(资料图片仅供参考)

此外,为了防止后续相加的过程引起符号位溢出,FIR基本模块需要对乘法结果进行符号位扩展。

扩展完成后,如果同时对这些(71个)加权结果相加,肯定会使得系统运行速率上不去,而且设计的比较死板。这里需要引入流水线操作;何为流水线操作,简单地说,本来你一个人承担一桌菜,你需要洗菜,切菜,炒菜,装盘,上桌,不仅十分麻烦而且很耽误时间;这个时候有人过来帮你,一个人洗菜,一个人切菜,一个人炒菜,一个人装盘,你负责上桌,虽然费了些人,但是每个人的任务都比较轻松所以做事速度也很快,这就是流水线操作,把一件很复杂的事情划分成N个小事,虽然牺牲了面积但换取了系统运行时钟的提升。

前期准备

除了Verilog模块,我们还有几样东西需要准备。首先,需要将FIR抽头系数定点化,上一文使用的FIR抽头系数都是很小的浮点数,为此,我们直接对每个系数乘以2的15次幂,然后取整数,舍去小数位,设定FIR抽头系数位宽为16bit;因为系数本身比较小,不担心会溢出。注意,这里抽头系数的位宽尽量不超过信号位宽,否则可能会有问题。

为了方便多个模块同时调用FIR系数,这里使用Python直接将定点化的系数生成为function,输入为index,需要第N阶的FIR系数,就调用function,输入参数为N,输出为定点化的系数。

所谓定点化,这里使用的方法十分粗暴,直接对所有浮点数,乘以一个2的n次幂。然后对参数向下取整,舍弃小数位。

FIR浮点系数转化为定点数并生成function的代码如下:

def coef2function(filename, exp, gain):    # :paramfilename: FIR抽头系数文件名    # :param exp:      浮点数转定点数的位宽    # :param gain:     浮点数整体的增益,增益为power(2, gain)    # :return:    coef = set_coef(filename)    with open("fir_coef.v", "w") as f:        f.write("function [{}:0] get_coef;\\n".format(exp-1))        f.write("input [7:0] index;\\n")        f.write("case (index)\\n")        for i in range(len(coef)):            f.write("{}: get_coef = {};\\n".format(i,int(np.floor(coef[i] * np.power(2,gain)))))        f.write("default: get_coef = 0;\\n")        f.write("endcase\\nendfunction")

转换生成的function示例如下:

function [15:0] get_coef;input [7:0] index;case (index)0: get_coef = 0;1: get_coef = 0;2: get_coef = 2;3: get_coef = 10;...69: get_coef = 10;70: get_coef = 2;71: get_coef = 0;72: get_coef = 0;default: get_coef = 0;endcaseendfunction

这样,当多个基本模块并行运行时,每个模块的系数可以通过调用function获取对应的参数。

仿真需要有信号源供FIR滤波,所以直接将仿真用的信号源定点化;因为Testbench中使用readmemh或者readmemb读取txt文档数据,只能读取二进制或16进制数据,所以需要对数据进行二进制或16进制转换。

信号源选取上一文的信号源,由于该信号源最大值为3,设定信号源的位宽为16位,为防止数据溢出,信号源整体乘以2的12次幂,然后取整舍去小数位。为了方便后续转二进制,这里需要将数据由16bit有符号转为16bit无符号;转换的过程为,如果data[i]小于0,直接设定data[i] = 2^16 + data[i]。然后使用“{{:0>16b}}”.format(data[i])转换为16bit二进制,存入cos.txt。

浮点数转换定点数并转换二进制数据存入txt转换代码如下:

def float2fix_point(data, exp, gain, size):    # """    # :param data: 信号源数据    # :param exp:  浮点数转定点数的位宽    # :param gain: 浮点数整体乘以增益,增益为power(2,15)    # :param size: 转换多少点数    # :return:    # """    if size > len(data):        print("error, size > len(data)")        return    data = [int(np.floor(data[i] * np.power(2, gain) )) for i in range(size)]    fmt = "{{:0 >{}b}}".format(exp)    n = np.power(2, exp)    for i in range(size):        if data[i] > (n //2 - 1):            print("error")        if data[i] < 0:            d = n + data[i]        else:            d = data[i]        data[i] = fmt.format(d)    # data = [bin(data[i]) for i in range(4096)]    np.savetxt("cos.txt", data, fmt="%s")
实现方法

为了方便看示例代码,这里假定信号位宽DATA_BITS为16,系数位宽为COEF_BITS为16,扩展符号位宽EXTEND_BITS为5, 阶数FIR_ORDER为72。

设计思路还是从底层开始设计,首先需要实现FIR的基本模块。前面提到,为了节省乘法器,每个模块输入两个信号和一个FIR抽头系数,两个参数相加,相加结果直接乘以系数,最后做符号位扩展,防止后续操作导致符号位溢出。

fir_base.v 主要代码:

reg signed [DATA_BITS + COEF_BITS - 1:0]  data_mult;// 因为FIR系数是中心对称的,所以直接把中心对称的数据相加乘以系数// 相加符号位扩展一位wire signed [DATA_BITS:0]  data_in ;assign data_in = {data_in_A[DATA_BITS-1], data_in_A} + {data_in_B[DATA_BITS-1], data_in_B};// 为了防止后续操作导致符号位溢出,这里扩展符号位,设计位操作知识assign data_out = {{EXTEND_BITS{data_mult[DATA_BITS + COEF_BITS - 1]}},data_mult };always @(posedge clk or posedge rst) begin  if (rst) begin    // reset    fir_busy  <=  1"b0;    data_mult  <=   0 ;    output_vld  <=  1"b0;  end  else if (en) begin        //如果coef为0,不需要计算直接得0    data_mult  <=  coef != 0 ? data_in * coef : 0;    output_vld  <=  1"b1;  end  else begin    data_mult  <=  "d0;    output_vld  <=  1"b0;  endend

完成了基本模块后,顶层模块就是调用基本模块,然后对运算结果进行相加操作。但这里需要注意,顶层首先需要73个16bit的寄存器,用来保存传入的信号并实现每时钟周期上升沿,73个数据整体前移;学过数据结构的同学可以把这个想象成队列结构,每次信号上升沿时,队首信号出队,队尾补进新的信号。

实现方法如下:

// FIR输入数据暂存寄存器组reg signed   [DATA_BITS-1:0]  data_tmp [FIR_ORDER:0] ;always @(posedge clk or posedge rst) begin  if (rst) begin    // reset    data_tmp[0] <=  0;  end  else if (data_in_vld) begin    data_tmp[0] <=  data_in;  endendgenerate  genvar j;    for (j = 1; j <= FIR_ORDER; j = j + 1)  begin: fir_base  //这里无法兼顾0,FIR_HALF_ORDER    always @(posedge clk or posedge rst) begin    if (rst) begin        // reset        data_tmp[j] <=  0;    end    else if (data_in_vld) begin        data_tmp[j] <=  data_tmp[j-1];    end  endendgenerate

这里实现了从0-72共73个寄存器,使用了Verilog的类似二维数组的寄存器定义用法。可以从代码看到,0号data_tmp过于特殊,需要保存输入的信号,而其他data_tmp直接使用generate for语法实现前面提到的“队列”功能。generate for语法是可以综合的, 其中for循环的参数必须是常数,其作用就是直接在电路上复制循环体的内容。对于像这样需要规律性地赋值操作很方便,下面还会出现generate for语法。

寄存器组的问题解决后,需要与FIR参数进行乘加,这里同样适用generate for语句简化设计:

localparam FIR_HALF_ORDER = FIR_ORDER / 2;  //36wire signed [OUT_BITS-1:0]  data_out_tmp [FIR_HALF_ORDER:0] ;// FIR输出数据后流水线相加的中间变量,多出部分变量,防止下一级相加过程中index越界reg signed   [OUT_BITS-1:0]  dat_out_reg  [FIR_HALF_ORDER+4:0] ;   //40-0always @(posedge clk or posedge rst) begin  if (rst) begin    // reset    dat_out_reg[FIR_HALF_ORDER] <= 0;  end  else if (output_vld_tmp[FIR_HALF_ORDER]) begin    dat_out_reg[FIR_HALF_ORDER] <= data_out_tmp[FIR_HALF_ORDER];  endendfir_base #(  .DATA_BITS(DATA_BITS),  .COEF_BITS(COEF_BITS),  .EXTEND_BITS(EXTEND_BITS)  )fir_inst_FIR_HALF_ORDER(  .clk    (clk),  .rst    (rst),    .en      (data_in_vld),    .data_in_A  (data_tmp[FIR_HALF_ORDER]),    .data_in_B  (12"d0),    .coef    (get_coef(FIR_HALF_ORDER)),    .fir_busy  (),    .data_out  (data_out_tmp[FIR_HALF_ORDER]),    .output_vld  (output_vld_tmp[FIR_HALF_ORDER])    );generate  genvar j;  for (j = 1; j < FIR_HALF_ORDER; j = j + 1)  begin: fir_base  fir_base  #(  .DATA_BITS(DATA_BITS),  .COEF_BITS(COEF_BITS),  .EXTEND_BITS(EXTEND_BITS)  )  fir_inst_NORMAL  (    .clk    (clk),    .rst    (rst),    .en      (data_in_vld),    .data_in_A  (data_tmp[j]),    .data_in_B  (data_tmp[FIR_ORDER-j]),    .coef    (get_coef(j)),    .fir_busy  (),    .data_out  (data_out_tmp[j]),    .output_vld  (output_vld_tmp[j])  );  always @(posedge clk or posedge rst) begin    if (rst) begin      // reset      dat_out_reg[j] <= 0;    end    else if (output_vld_tmp[j]) begin      dat_out_reg[j] <= data_out_tmp[j];    end  endendgenerate

首先由于中心点(第36阶)的系数是只乘中心点,并不像其他系数可以传入关于中心对称的两个信号。所以FIR_HALF_ORDER需要单独例化。同样,dat_out_reg也需要单独复制;其他的信号在generate for循环体完成操作,由于0号系数在阶数为偶数的情况下为0,这里跳过0号系数直接从1号系数开始,所以for循环是从1 - FIR_HALF_ORDER。

加权结果出来后,需要对结果相加,为了提升系统运行速率,这里采用三级流水线操作。每次进行4位数据相加传递给下一级流水线,所以示例代码里FIR最高阶数为4 * 4 * 4 * 2 = 128。

流水线操作过程如下:

// 流水线第一级相加,计算公式ceil(N/4)localparam FIR_ADD_ORDER_ONE = (FIR_HALF_ORDER + 3) / 4; //// 流水线第二级相加,计算公式ceil(N/4)localparam FIR_ADD_ORDER_TWO = (FIR_ADD_ORDER_ONE + 3) / 4; //3reg signed [OUT_BITS-1:0]  dat_out_A [FIR_ADD_ORDER_ONE+3:0] ;  //12-0reg signed [OUT_BITS-1:0]  dat_out_B [FIR_ADD_ORDER_TWO+3:0] ;  //6-0// 这些多余的reg直接设为0就可以了always @ (posedge clk) begin  dat_out_reg[FIR_HALF_ORDER+1] = 0;  dat_out_reg[FIR_HALF_ORDER+2] = 0;  dat_out_reg[FIR_HALF_ORDER+3] = 0;  dat_out_reg[FIR_HALF_ORDER+4] = 0;  dat_out_A[FIR_ADD_ORDER_ONE] = 0;  dat_out_A[FIR_ADD_ORDER_ONE+1] = 0;  dat_out_A[FIR_ADD_ORDER_ONE+2] = 0;  dat_out_A[FIR_ADD_ORDER_ONE+3] = 0;  dat_out_B[FIR_ADD_ORDER_TWO] = 0;  dat_out_B[FIR_ADD_ORDER_TWO + 1] = 0;  dat_out_B[FIR_ADD_ORDER_TWO + 2] = 0;  dat_out_B[FIR_ADD_ORDER_TWO + 3] = 0;end// 判定所有FIR_BASE模块完成转换assign data_out_vld = (&output_vld_tmp[FIR_HALF_ORDER:1] == 1"b1) ? 1"b1 : 1"b0;//最后一级流水线always @(posedge clk or posedge rst) begin  if (rst) begin    // reset    data_out   <=  0;  end  else if (data_out_vld) begin    data_out   <= dat_out_B[0] + dat_out_B[1] + dat_out_B[2] + dat_out_B[3];  endendgenerate  genvar j;  for (j = 1; j < FIR_HALF_ORDER; j = j + 1)          if (j <= FIR_ADD_ORDER_ONE)  begin  //流水线相加 第一级  //注意j 的范围是[1,FIR_HALF_ORDER]  //所以dat_out_A[j-1]    always @(posedge clk or posedge rst) begin      if (rst) begin        // reset        dat_out_A[j-1] <= 0;      end      else begin        dat_out_A[j-1] <= dat_out_reg[4*j-3] + dat_out_reg[4*j-2] + dat_out_reg[4*j-1] + dat_out_reg[4*j];      end    end  end    if (j <= FIR_ADD_ORDER_TWO)  begin  // 流水线相加 第二级    always @(posedge clk or posedge rst) begin      if (rst) begin        // reset        dat_out_B[j-1] <= 0;      end      else begin        dat_out_B[j-1] <= dat_out_A[4*j - 4] + dat_out_A[4*j- 3] + dat_out_A[4*j - 2] + dat_out_A[4*j - 1];      end    end  endend  endgenerate

这里第一级,第二级流水线的循环次数采用ceil(N/4)的计算方式,也就是取比N/4大的最小整数。比如5/4 = 1.25,则ceil(1.25) = 2, 而ceil(1) = 1;

定义每级寄存器组时,会多定义4个寄存器组。并且这些寄存器永远为0,这样做的原因以第一级流水线相加举例:

看第一级流水线,假定FIR_ORDER为70,FIR_HALF_ORDER为35,FIR_ADD_ORDER_ONE为9,当j为9时,dat_out_A[8] <= dat_out_reg[33] + dat_out_reg[34] + dat_out_reg[35] + dat_out_reg[36];

而我们在前面设计中正好定义了dat_out_reg[36],并且它永远为0,不影响最终结果。

可以看到,第一级,第二级流水线使用generate for, if语句。如果if条件成立,if内部的电路会被描述。最后71个数据经过三级流水线相加,结果输出。

如果想要提升FIR的最高运行频率,可以把流水线级数增加,每级流水线相加改为2个或者3个。

这个结构只适合FIR阶数为偶数的情况,由于最近比较忙,没有做更大的兼容性。

仿真结果与资源占用对比

VCS仿真结果

FIR滤波效果,Python

FIR滤波效果,Verilog

FIR_IMPLE的资源占用 QuartusII 13.1

FIR IP核资源占用,参数相同情况下,Quartus II 13.1

FIR_IMPLE的Fmax Quartus II 13.1

FIR IP核,Fmax, Quartus II 13.1

标签:

手把手用Verilog实现FIR滤波器

首先需要把FIR最基本的结构实现,也就是每个FIR抽头的数据与其抽头系数

环球热文:李渊最疼爱的女儿,平阳昭公主多厉害?一生战绩仅次于李世民

在唐高祖李渊的儿女中,选一位最宠爱的儿子不好选,但是如果选一位女儿

网贷逾期对征信影响多久,贷款逾期多久会影响征信?-全球观点

虽然根据金融机构内部的规定来看,基本上只要用户的贷款逾期了,就会影

汀泗桥镇古塘村:巧用积分制 破陈规陋习

巧用积分制

绥德县气象台发布大雾黄色预警【Ⅲ级/较重】【2023-06-18】_动态

绥德县气象台2023年06月18日23时59分发布大雾黄色预警信号:预计下述地

先予执行的程序是怎样的?申请人要求追索劳动报酬的可以申请先予执行吗? 当前热点

一、先予执行适用于3类案件:1、申请人要求追索赡养费、扶养费、抚育费

海通证券开启“ETF角逐”大动作 解锁指数投资财富“新机遇”

6月,伴随着两只科创50ETF期权合约品种的如期上市,ETF再次引发行业热

世界微动态丨金银花的副作用太大了_经常喝金银花泡水好吗

1、如果患者属于实热之证,体内有实热之邪,经常喝金银花有非常好的清

你知道“镁合金型号”有哪些吗?

镁合金分类依据镁合金是以镁为基础加入其他元素组成的合金。其特点是:

天天观焦点:华泰柏瑞纳斯达克100ETF(QDII)净值上涨1.07% 请保持关注

金融界基金06月19日讯华泰柏瑞纳斯达克100ETF(QDII)基金06月15日上涨0

江作青罗带山如碧玉簪什么意思_该句出自哪里

1、意思是:那里的江都像一条绿丝带,山像一个玉簪。2、产地:《送桂州

环球观焦点:人民币可以买港股!港币-人民币双柜台模式今日正式推出

港币-人民币双柜台模式今日正式推出证券时报记者吴少龙香港交易所将于6

环球速读:发动机总成包括哪些东西_发动机总成包括哪些部分

1、它由下述6个主要部分组成:  燃料供给与调节系统,它将燃料喷进燃

就近提供照护、诊疗、康复等服务 嵌入社区的老年护理中心有何不同?

面对人口老龄化的加剧,我国持续完善老年健康体系建设,不断探索医养结

爱心传递在行动 娄底各地开展无偿献血活动

红网时刻新闻记者罗子依综合报道今年6月14日是第20个“世界献血者日”

【天天播资讯】湖南办理生态环境损害赔偿案2500余件,涉案金额3亿多元

6月18日,记者获悉,自湖南开展生态环境损害赔偿制度改革工作以来,全

曾经英语全班最差ted演讲视频_曾经英语-全球实时

1、once一次,曾经whilom曾经,以前,从前ever曾经,永远,究竟havealready曾

2023年上海中考英语试卷涉及这些话题:校园活动、社会生活、自然探索、成长感悟

今天上午,2023年上海市初中学业水平考试(简称“中考”)英语科目顺利

再见北京首钢!闵鹿蕾身穿北控训练服曝光:2大投手加盟+有意周琦

再见北京首钢!闵鹿蕾身穿北控训练服曝光:2大投手加盟+有意周琦,闵鹿

今日看点:应对汛期!直击武警官兵冲锋舟集训

黄河流域汛期即将来临,近日,武警第一机动总队某支队展开冲锋舟集训,

除法列竖式技巧 除法列竖式计算方法

1、可以去问问老师,在网上有可能我表达的不太好,所以最好是自己问问

环球观天下!山西如何完成双碳目标? 王金南:调整能源结构,大力发展屋顶光伏

6月18日,2023年亚布力论坛创新年会在太原召开,全国政协常委、人口资

焦点信息:环首都经济圈地图(环首都经济圈)

导读1、【京津冀都市圈】是指以北京市和天津市为中心,囊括河北省的石

世界简讯:沁阳市沁园覃怀街道:创城脚步不停歇 齐心共绘“城市名片”

近期,沁阳市沁园覃怀街道充分发挥新时代文明实践队伍作用,凝聚各方力

我们想要生活多问题还是少问题?|环球视点

问:老师,孩子青春期非常叛逆,不听话也管不了,该怎么办呢?答:

环球时讯:“茶和天下·雅集”暨中国茶文化主题展在匈牙利举办

天天热点评!台退将群聚迎黄埔建校百年 侯友宜争取支持

通电3年就弹警告,西数为让用户买硬盘出“奇招”

交城:职业生涯社会实践 护航学生未来成长_快播

焦点短讯!原神3.7艾尔海森养成攻略

当前关注:百度在深圳开启无人驾驶商业化试点运营

世界微头条丨重庆市彭水苗族土家族自治县2023-06-18 05:17发布暴雨黄色预警

每日播报!家用电梯(关于家用电梯介绍)

win10桌面便签小工具无法开始使用 win10桌面便签小工具

前沿热点:植物人大战僵尸解说(植物人大战僵尸秘籍)

今日播报!亩产200斤,年纯收益近二十万元 看济南这个村小龙虾的“富农账本”

观天下!个人医疗保险缴费查询_个人医疗保险

中哈霍尔果斯国际边境合作中心进出人员突破100万人次-观点

微软CEO纳德拉表示 GPT-4帮助其实现了童年梦想

岚图追光北京区域上市,30万级豪华电动轿车市场要卷起来了-即时焦点

2023年宿州市主城区小学、初中学区(试行)公布

要闻速递:农村养老保险每年交2000元,交够15年到60岁以后能领多少?

解决缺少a3dapi.dll问题的方法

过户房子在哪里办理

黑龙江省黑河市人大常委会原副主任姜福被开除党籍|环球新视野

天天看热讯:美国首都华盛顿发生枪击事件 造成一名孕妇死亡

新肿瘤相关基因MUM的功能研究_关于新肿瘤相关基因MUM的功能研究简介

萌球大战怎么合成珍宝 球球大作战遗迹珍宝箱开启方法-天天消息

再建三巨头?若保罗被太阳裁掉 湖人将发起猛烈竞争|全球报资讯

突触前膜释放神经递质的方式_突触 世界观点

【高质量发展调研行】智能化车间为高质量发展提速 一小时生产36台车_观察

世界通讯!“千万工程”调研行丨这里的农民生活为何如此惬意?——浙江嘉兴乡村走访见闻

微软升级iOS版PowerPoint 模板增至71个-全球快看

摩尔庄园阳光酥油肉松火候_摩尔庄园阳光酥油肉松-天天百事通

全球头条:家族·投资 | 行善且盈利,揭秘恒隆家族二代如何做资产配置

周末跨行转账会延迟吗_周末跨行转账多少时间到账-世界百事通

贝斯特:涡轮增压器渗透率仍不断提升,行业增速保持稳定增长-天天微动态

德信地产转让杭州凯聆企业管理73.54%股份,子公司杭州润波接盘

移动怎么删除通话清单(移动怎么删除通话记录)

翰森制药(03692.HK):6月16日南向资金减持9.2万股

当前报道:哈拉奇边境派出所暖心护送醉酒男子回家

非主流情侣网名大全(非主流情侣网名+超拽) 天天快播

新小米平板要来了?第二代骁龙8安卓平板正在准备!_最新

股票行情快报:昂立教育(600661)6月16日主力资金净卖出257.56万元

全球百事通!亦舒小说全集

这只债基自购半年即全部赎回 而基金公司多现“抄底”旗下权益基金

小燕子寓言故事_蜘蛛和燕子的寓言故事

将9月底实现量产 全新一代北京BJ40申报图

IPO要闻汇 | 审13过11,巍华新材、建发致新遭暂缓表决,农化“巨无霸”先正达过会-环球看热讯

全球球精选!健身传承两相宜 即墨区华山路小学将大红鼓文化引入校园

焦点日报:方差越大越不稳定吗_方差越大

贝克汉姆操刀,致敬经典车型,玛莎拉蒂个性化典藏系列正式发布_环球精选

惊!台电前4个月已累亏3058亿元 天天快看

2023年注会《公司战略与风险管理》第2章高频考点8:业务组合分析|世界独家

全球新资讯:母亲节贺卡的制作方法视频_母亲节贺卡的制作方法

x 广告
x 广告

Copyright ©  2015-2022 北方自然网版权所有  备案号:京ICP备2021034106号-50   联系邮箱: 55 16 53 8@qq.com