Verilog实现对数运算log

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

  对于FPGA而言,其对对数运算是十分有限的,但在某些特殊场景种,需要进行精度不高但快速的对数运算,FPGA就体现了他计算速度的优势,本文主要介绍采用Verilog实现对数运算的原理及实现方法。

一、理论基础

对数换底公式:  

   log ⁡ D A \log_DA logDA = log ⁡ 2 A \log_2A log2A/ log ⁡ 2 D \log_2D log2D

log ⁡ 2 A \log_2A log2A计算变换:
A = ∑ i = 0 N − 1 a i 2 i \sum_{i=0}^{N-1} a_i2^i i=0N1ai2i ,其中N为A的bit位宽, a i a_i ai为各bit的数据(0或1)
若A中不为0的最高bit位为k,即 a k a_k ak=1, a i a_i ai(i>k)均为0,则:

log ⁡ 2 A \log_2A log2A = log ⁡ 2 ( 2 k A / 2 k ) \log_2(2^kA/2^k) log2(2kA/2k) = k + log ⁡ 2 ( A / 2 k ) \log_2(A/2^k) log2(A/2k)

A / 2 k A/2^k A/2k < 2,则0< log ⁡ 2 ( A / 2 k ) \log_2(A/2^k) log2(A/2k) <1,那么就将 log ⁡ 2 A \log_2A log2A的运算分为2个部分:整数部分k和小数部分 log ⁡ 2 ( A / 2 k ) \log_2(A/2^k) log2(A/2k)
因1/ log ⁡ 2 D \log_2D log2D位固定值,故采用乘法器可计算出最终的

log ⁡ D A \log_DA logDA = 1/ log ⁡ 2 D \log_2D log2D * (k + log ⁡ 2 ( A / 2 k ) \log_2(A/2^k) log2(A/2k)

二、Verilog实现方法

  提供一种Verilog计算log2的计算方法,最终计算 log ⁡ D A \log_DA logDA需增加乘法,实际应用中,受底数和其它影响,乘法器设计较为灵活,所以不提供乘法器设计,仅仅提供log2的计算方法。
  
1、查找不为0的最高bit位k,查找方法可开用bit轮询查找或case查找。bit轮询查找每次只能查找1个bit,耗时较长,case查找可在最短1个时钟找到。
  
2、小数位计算,从k-1bit位开始,依次向后取m个bit数据,m的大小影响计算精度,m越大,精度越高。最终得到mbit的数据,将该数据作为查找表的索引即地址,查询查找表的数据即小数部分F。
  
3、定点即运算
  如最终结果定点位Fix16_8,8位小数位,则查找表中的数据位宽位8,均为小数。7位整数,将K右移8位并扩展到16位即可得到整数位数据,整数位数据和小数位数据相加即为最终 log ⁡ 2 A \log_2A log2A的值。
  因1/ log ⁡ 2 D \log_2D log2D位固定值,故采用乘法器可计算出最终的 log ⁡ D A \log_DA logDA

4、查找表制定
查找表通过matlab制定,并定点后写入文件存储,如采用m=8,数据为8位数据

m = 8;
a = log2(1+(0:2^m-1)/2^m);
a_fix_hex = dec2hex(fi(a,1,9,8)*2^m,2);
%%将a_fix_hex写入文件即可

三、Verilog实现代码

  根据以上设计,提供32bit数据log2的计算方式,以做参考,其中输出数据小数位宽位8bit,小数位计算精度为8bit。
接口

module log # (
    parameter             O_WIDTH_F = 8,   //output 小数位宽
    localparam             I_WIDTH = 32,   //input width (固定32bit,不足32bit输入高位补0)    
    localparam             O_WIDTH = O_WIDTH_F + $clog2(I_WIDTH)   //output width,O_WIDTH >= O_WIDTH_F + $clog2(I_WIDTH)
                           
)(
	input clk,
	input [I_WIDTH-1 : 0]  i_data,
	output reg [O_WIDTH-1 : 0]  o_data
);

计算整数位及小数位索引
  该计算采用case实现,可减少计算时间,1个时钟周期即可完成整数位计算和小数位索引计算。

always @ (posedge clk) begin
	casez(r_i_data) 
        {{00{1'b0}},1'b1,{(I_WIDTH-01){1'b?}}}: begin int_d <= I_WIDTH - 01; fra_index <= (PRECISION_W <= 31)? r_i_data[30 -: PRECISION_W] : {r_i_data[30 -: 31],{(PRECISION_W-31){1'b0}}}; end
        {{01{1'b0}},1'b1,{(I_WIDTH-02){1'b?}}}: begin int_d <= I_WIDTH - 02; fra_index <= (PRECISION_W <= 30)? r_i_data[29 -: PRECISION_W] : {r_i_data[29 -: 30],{(PRECISION_W-30){1'b0}}}; end 
        {{02{1'b0}},1'b1,{(I_WIDTH-03){1'b?}}}: begin int_d <= I_WIDTH - 03; fra_index <= (PRECISION_W <= 29)? r_i_data[28 -: PRECISION_W] : {r_i_data[28 -: 29],{(PRECISION_W-29){1'b0}}}; end 
        {{03{1'b0}},1'b1,{(I_WIDTH-04){1'b?}}}: begin int_d <= I_WIDTH - 04; fra_index <= (PRECISION_W <= 28)? r_i_data[27 -: PRECISION_W] : {r_i_data[27 -: 28],{(PRECISION_W-28){1'b0}}}; end 
        {{04{1'b0}},1'b1,{(I_WIDTH-05){1'b?}}}: begin int_d <= I_WIDTH - 05; fra_index <= (PRECISION_W <= 27)? r_i_data[26 -: PRECISION_W] : {r_i_data[26 -: 27],{(PRECISION_W-27){1'b0}}}; end 
        {{05{1'b0}},1'b1,{(I_WIDTH-06){1'b?}}}: begin int_d <= I_WIDTH - 06; fra_index <= (PRECISION_W <= 26)? r_i_data[25 -: PRECISION_W] : {r_i_data[25 -: 26],{(PRECISION_W-26){1'b0}}}; end 
        {{06{1'b0}},1'b1,{(I_WIDTH-07){1'b?}}}: begin int_d <= I_WIDTH - 07; fra_index <= (PRECISION_W <= 25)? r_i_data[24 -: PRECISION_W] : {r_i_data[24 -: 25],{(PRECISION_W-25){1'b0}}}; end 
        {{07{1'b0}},1'b1,{(I_WIDTH-08){1'b?}}}: begin int_d <= I_WIDTH - 08; fra_index <= (PRECISION_W <= 24)? r_i_data[23 -: PRECISION_W] : {r_i_data[23 -: 24],{(PRECISION_W-24){1'b0}}}; end 
        {{08{1'b0}},1'b1,{(I_WIDTH-09){1'b?}}}: begin int_d <= I_WIDTH - 09; fra_index <= (PRECISION_W <= 23)? r_i_data[22 -: PRECISION_W] : {r_i_data[22 -: 23],{(PRECISION_W-23){1'b0}}}; end 
        {{09{1'b0}},1'b1,{(I_WIDTH-10){1'b?}}}: begin int_d <= I_WIDTH - 10; fra_index <= (PRECISION_W <= 22)? r_i_data[21 -: PRECISION_W] : {r_i_data[21 -: 22],{(PRECISION_W-22){1'b0}}}; end 
        {{10{1'b0}},1'b1,{(I_WIDTH-11){1'b?}}}: begin int_d <= I_WIDTH - 11; fra_index <= (PRECISION_W <= 21)? r_i_data[20 -: PRECISION_W] : {r_i_data[20 -: 21],{(PRECISION_W-21){1'b0}}}; end 
        {{11{1'b0}},1'b1,{(I_WIDTH-12){1'b?}}}: begin int_d <= I_WIDTH - 12; fra_index <= (PRECISION_W <= 20)? r_i_data[19 -: PRECISION_W] : {r_i_data[19 -: 20],{(PRECISION_W-20){1'b0}}}; end 
        {{12{1'b0}},1'b1,{(I_WIDTH-13){1'b?}}}: begin int_d <= I_WIDTH - 13; fra_index <= (PRECISION_W <= 19)? r_i_data[18 -: PRECISION_W] : {r_i_data[18 -: 19],{(PRECISION_W-19){1'b0}}}; end 
        {{13{1'b0}},1'b1,{(I_WIDTH-14){1'b?}}}: begin int_d <= I_WIDTH - 14; fra_index <= (PRECISION_W <= 18)? r_i_data[17 -: PRECISION_W] : {r_i_data[17 -: 18],{(PRECISION_W-18){1'b0}}}; end 
        {{14{1'b0}},1'b1,{(I_WIDTH-15){1'b?}}}: begin int_d <= I_WIDTH - 15; fra_index <= (PRECISION_W <= 17)? r_i_data[16 -: PRECISION_W] : {r_i_data[16 -: 17],{(PRECISION_W-17){1'b0}}}; end 
        {{15{1'b0}},1'b1,{(I_WIDTH-16){1'b?}}}: begin int_d <= I_WIDTH - 16; fra_index <= (PRECISION_W <= 16)? r_i_data[15 -: PRECISION_W] : {r_i_data[15 -: 16],{(PRECISION_W-16){1'b0}}}; end 
        {{16{1'b0}},1'b1,{(I_WIDTH-17){1'b?}}}: begin int_d <= I_WIDTH - 17; fra_index <= (PRECISION_W <= 15)? r_i_data[14 -: PRECISION_W] : {r_i_data[14 -: 15],{(PRECISION_W-15){1'b0}}}; end 
        {{17{1'b0}},1'b1,{(I_WIDTH-18){1'b?}}}: begin int_d <= I_WIDTH - 18; fra_index <= (PRECISION_W <= 14)? r_i_data[13 -: PRECISION_W] : {r_i_data[13 -: 14],{(PRECISION_W-14){1'b0}}}; end 
        {{18{1'b0}},1'b1,{(I_WIDTH-19){1'b?}}}: begin int_d <= I_WIDTH - 19; fra_index <= (PRECISION_W <= 13)? r_i_data[12 -: PRECISION_W] : {r_i_data[12 -: 13],{(PRECISION_W-13){1'b0}}}; end 
        {{19{1'b0}},1'b1,{(I_WIDTH-20){1'b?}}}: begin int_d <= I_WIDTH - 20; fra_index <= (PRECISION_W <= 12)? r_i_data[11 -: PRECISION_W] : {r_i_data[11 -: 12],{(PRECISION_W-12){1'b0}}}; end 
        {{20{1'b0}},1'b1,{(I_WIDTH-21){1'b?}}}: begin int_d <= I_WIDTH - 21; fra_index <= (PRECISION_W <= 11)? r_i_data[10 -: PRECISION_W] : {r_i_data[10 -: 11],{(PRECISION_W-11){1'b0}}}; end 
        {{21{1'b0}},1'b1,{(I_WIDTH-22){1'b?}}}: begin int_d <= I_WIDTH - 22; fra_index <= (PRECISION_W <= 10)? r_i_data[09 -: PRECISION_W] : {r_i_data[09 -: 10],{(PRECISION_W-10){1'b0}}}; end 
        {{22{1'b0}},1'b1,{(I_WIDTH-23){1'b?}}}: begin int_d <= I_WIDTH - 23; fra_index <= (PRECISION_W <= 09)? r_i_data[08 -: PRECISION_W] : {r_i_data[08 -: 09],{(PRECISION_W-09){1'b0}}}; end 
        {{23{1'b0}},1'b1,{(I_WIDTH-24){1'b?}}}: begin int_d <= I_WIDTH - 24; fra_index <= (PRECISION_W <= 08)? r_i_data[07 -: PRECISION_W] : {r_i_data[07 -: 08],{(PRECISION_W-08){1'b0}}}; end 
        {{24{1'b0}},1'b1,{(I_WIDTH-25){1'b?}}}: begin int_d <= I_WIDTH - 25; fra_index <= (PRECISION_W <= 07)? r_i_data[06 -: PRECISION_W] : {r_i_data[06 -: 07],{(PRECISION_W-07){1'b0}}}; end 
        {{25{1'b0}},1'b1,{(I_WIDTH-26){1'b?}}}: begin int_d <= I_WIDTH - 26; fra_index <= (PRECISION_W <= 06)? r_i_data[05 -: PRECISION_W] : {r_i_data[05 -: 06],{(PRECISION_W-06){1'b0}}}; end 
        {{26{1'b0}},1'b1,{(I_WIDTH-27){1'b?}}}: begin int_d <= I_WIDTH - 27; fra_index <= (PRECISION_W <= 05)? r_i_data[04 -: PRECISION_W] : {r_i_data[04 -: 05],{(PRECISION_W-05){1'b0}}}; end 
        {{27{1'b0}},1'b1,{(I_WIDTH-28){1'b?}}}: begin int_d <= I_WIDTH - 28; fra_index <= (PRECISION_W <= 04)? r_i_data[03 -: PRECISION_W] : {r_i_data[03 -: 04],{(PRECISION_W-04){1'b0}}}; end 
        {{28{1'b0}},1'b1,{(I_WIDTH-29){1'b?}}}: begin int_d <= I_WIDTH - 29; fra_index <= (PRECISION_W <= 03)? r_i_data[02 -: PRECISION_W] : {r_i_data[02 -: 03],{(PRECISION_W-03){1'b0}}}; end 
        {{29{1'b0}},1'b1,{(I_WIDTH-30){1'b?}}}: begin int_d <= I_WIDTH - 30; fra_index <= (PRECISION_W <= 02)? r_i_data[01 -: PRECISION_W] : {r_i_data[01 -: 02],{(PRECISION_W-02){1'b0}}}; end 
        {{30{1'b0}},1'b1,{(I_WIDTH-31){1'b?}}}: begin int_d <= I_WIDTH - 31; fra_index <= (PRECISION_W <= 01)? r_i_data[00 -: PRECISION_W] : {r_i_data[00 -: 01],{(PRECISION_W-01){1'b0}}}; end 
        {{31{1'b0}},1'b1,{(I_WIDTH-32){1'b?}}}: begin int_d <= I_WIDTH - 32; fra_index <= (PRECISION_W <= 00)? r_i_data[00 -: PRECISION_W] :{(PRECISION_W-00){1'b0}}; end 
        default:begin int_d <= {$clog2(I_WIDTH){1'b0}}; fra_index <= {PRECISION_W{1'b0}};end
	endcase
end

ROM设计及读取

(* rom_style="{distributed | block}" *)
reg [O_WIDTH_F-1:0] log_rom [(2**PRECISION_W)-1:0];
reg [O_WIDTH_F-1:0] fra_data;
initial 
  $readmemh("log2_f.txt", log_rom, 0, (2**PRECISION_W)-1);
always @(posedge clk) fra_data <= log_rom[fra_index];

log2结果输出

always @(posedge clk) o_data <= {int_d_c0,fra_data};

四、仿真

  仿真tb比较简单,建立时钟及32bit输入数据即可

module tb_log(

    );
reg clk = 1'b0;
reg [31:0] i_data = 32'd0;  
wire [12:0] o_data;

always #5 clk <=~clk;
always @(posedge clk) i_data <= i_data + 1'b1;
log DUT(
	.clk(clk),
	.i_data(i_data),
	.o_data(o_data)
);
endmodule

最终仿真结果如下
在这里插入图片描述

五、典型工程应用

计算功率:  

20 log ⁡ 10 ( A ) \log10(A) log10(A) = 20 log ⁡ 2 A \log_2A log2A/ log ⁡ 2 10 \log_210 log210 = (20/ log ⁡ 2 10 \log_210 log210) log ⁡ 2 A \log_2A log2A = 6 log ⁡ 2 A \log_2A log2A

将3中 log ⁡ 2 A \log_2A log2A的结果乘以6即可得到最后的功率,乘以6可采用 log ⁡ 2 A \log_2A log2A<<2 + log ⁡ 2 A \log_2A log2A<<1;
  

10 log ⁡ 10 ( A ) \log10(A) log10(A) = 10 log ⁡ 2 A \log_2A log2A/ log ⁡ 2 10 \log_210 log210 = (10/ log ⁡ 2 10 \log_210 log210) log ⁡ 2 A \log_2A log2A = 3 log ⁡ 2 A \log_2A log2A

将3中 log ⁡ 2 A \log_2A log2A的结果乘以3即可得到最后的功率,乘以3可采用 log ⁡ 2 A \log_2A log2A<<1 + log ⁡ 2 A \log_2A log2A;

定点数log计算:
  以上的计算均是以A为整数计算的,得到的结果全为正,在实际应用中,可能A为定点小数,得到的结果可能为正也可能为负。此时在设计时需要将A做一个变式,即可转化为整数的log计算和加法。如定义B为Fix16_8的无符号定点数(分辨率为0.00390625),则:

log ⁡ 2 B \log_2B log2B = log ⁡ 2 B ∗ 2 8 / 2 8 \log_2B*2^8/2^8 log2B28/28 = log ⁡ 2 B ∗ 2 8 \log_2B*2^8 log2B28 - 8

A = B ∗ 2 8 A =B*2^8 A=B28,A为整数,则:

log ⁡ 2 B \log_2B log2B = log ⁡ 2 B ∗ 2 8 / 2 8 \log_2B*2^8/2^8 log2B28/28 = log ⁡ 2 A \log_2A log2A - 8

这样就将定点小数的对数运算转换为定点整数的对数运算。

  
源文件参考
https://download.csdn.net/download/u014035968/88311247

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值