首页 > 技术文章 > verilog求倒数-ROM实现方法

Dinging006 2018-07-27 18:35 原文

采用线性逼近法结合32段线性查找表的方式来实现1/z的计算。

首先将1/32-1/64的定点化数据存放到ROM中,ROM中存放的是扩大了2^20 次方的数字四舍五入后的整数部分。n值越大,精度越大,误差越小。这里取n=20;

ROM中存储的数据是1/(32+i)*2^20的四舍五入的整数部分。

32-64间的数据可以通过查表来实现,其他的数据则采用的是线性逼近的方法。

线性逼近的步骤为:

1.确定最高非零比特位的位置

2.对z进行左移或者右移,得到zp

3.zp查找ROM,得到1/zp,以及1/(zp+1),

4.求的1/zp-1/(zp+1),为误差A

5.N=z-zp*2^(m-5)

6.B=A/2^(m-5)*N

7.将扩大的部分缩小回去,或者缩小了的放大回去,那么1/z=(1/zp-B)*(1/2^(m-5))

 代码插入:

module top_inv(
    input clk,syn_rst,
    input [20:0]dataa,
    input [20:0]datab,
    //input [20:0]ampout,
    output reg [19:0]inv
   // output reg done
    );
    reg [4:0] address1;
    reg [4:0 ]address2;
    wire [4:0] m;
   // wire done;
    reg [19:0]invr;
    reg [20:0] ampout_r;
    reg [20:0] ampout_r1;
    wire [20:0] ampout;
    reg [20:0] ampoutr1,ampoutr2,ampoutr3,ampoutr4;
    wire [19:0] inv_r1;
    wire [19:0] inv_r2;
    reg [20:0] diff_r;
    reg [19:0] diffr;
    reg [19:0] diff;
    reg [19:0] N;
    reg [19:0] N1;
    reg en;
    
  always @(posedge clk or negedge syn_rst)
  begin 
    if(~syn_rst)
    begin
    ampoutr1<=21'd0;
    ampoutr2<=21'd0;
    ampoutr3<=21'd0;
    ampoutr4<=21'd0;
    end
    else
    ampoutr1<=ampout;
    ampoutr2<=ampoutr1;
    ampoutr3<=ampoutr2;
    ampoutr4<=ampoutr3;
  end
  reg [19:0] inv_r1t1,inv_r1t2,inv_r1t3;
 always@(posedge clk or negedge syn_rst)
 begin
    if(~syn_rst)
    begin
        inv_r1t1<=0;
        inv_r1t2<=0;
        inv_r1t3<=0;
    end
    else
    begin
        inv_r1t1<=inv_r1;
        inv_r1t2<=inv_r1t1;
        inv_r1t3<=inv_r1t2;
    end
 end
 reg [4:0] mt1,mt2,mt3,mt4,mt5;
 always@(posedge clk or negedge syn_rst)
 begin
    if(~syn_rst)
    begin
        mt1<=0;
        mt2<=0;
        mt3<=0;
        mt4<=0;
        mt5<=0;
    end
    else
    begin
        mt1<=m;
        mt2<=mt1;
        mt3<=mt2;
        mt4<=mt3;
        mt5<=mt4;
    end
 end
 reg sel;
 reg selr1,selr2;
  always @(posedge clk or negedge syn_rst)
  begin
      if(~syn_rst)
      begin
         diff<=0;
         diffr <= 0;
         ampout_r<='b0;
         ampout_r1<=0;
         address1<='b0;
         address2<='b0;
         en<=0;
         sel<=0;
      end
      else 
      begin
     // if(done)
      //begin
        if((ampout>=32)&&(ampout<=64))
            begin
                ampout_r<=0;
                ampout_r1<=0;
                address1<=ampoutr3-32;
                address2<= 0;
                diff <= 0;
                diffr <= 0;
                N <= 0;
                N1<= 0;
                en<=0;//不需要计算m的值
                sel<=0;
                selr1<=0;
                selr2<=0;
            end
        else 
            begin
             en<=1;//需要计算m的值
             if(m>5)
             begin
                // ampoutrr<=ampout;
                 ampout_r<=ampoutr1>>(m-5);
                 ampout_r1<=ampout_r;//zp
                 address1<=ampout_r-32;///inv_r1
                 address2<=ampout_r-31;///inv_r2
                 diff <= inv_r1-inv_r2;
                 diffr <=diff;
                 N1<=ampout_r1<<(mt2-5);
                 N<=ampoutr4-N1;
                 selr1<=1;
                 selr2 <= selr1;
                 sel <= selr2;
             end
             if(m<5)
             begin
                  //ampoutrr<=ampout;
                  ampout_r<=ampoutr1<<(5-m);//     mt4   mt3    mt2
                  ampout_r1 <= ampout_r;//          N    N1    ampout_r1
                  address1<=ampout_r-32;///mt4                 inv_r1
                  address2<=ampout_r-31;//inv_r1t3             inv_r2    mt1
                  diff <= inv_r1-inv_r2;//diff_r<<diffr<<diff<<address<<ampout_r<<  m  <<ampout
                  diffr <=diff;        //                      ampoutr3 ampoutr2  ampoutr1
                  N1<=ampout_r1>>(5-mt2);
                  N<=ampoutr4-N1;
                  selr1<=1;
                  selr2 <= selr1;
                  sel <= selr2;
              end
          end
          end
     // end 
      end  
//  assign diff=sel?(inv_r1-inv_r2):'b0;
  //assign N=sel?(ampout-N1):0;
  //assign diff_r = en?(diff*N>>(m-5)):0;
  //assign diff_r = (m>5)?(diff*N>>(m-5)):(diff*N<<(5-m));
 // assign inv = sel?(inv_r1-diff_r)>>(m-5):inv_r1;
 
  always@(posedge clk or negedge syn_rst)
  begin
      if(~syn_rst)
      begin
        invr<=0;
      //  done<=0;
      diff_r<=0;
      end
      else 
      begin
        if(sel) begin
        
            if(m>5)begin
                diff_r <= diffr*N>>(mt4-5);
                invr<=(inv_r1t3-diff_r)>>(mt5-5);
         //       done<=1;
                end
            else begin
                diff_r <= diffr*N<<(5-mt4);
                invr<=(inv_r1t3-diff_r)<<(5-mt5);
         //       done<=1;
                end
            end
        else 
        begin
            diff_r<=0;
            invr<=inv_r1t3;
        end
      end
  end
 always@(posedge clk or negedge syn_rst)
 begin
    if(~syn_rst)
    begin
        inv<=0;
    end
    else 
    begin
        if(invr)
            inv<= invr;
        else
            inv<=inv;
    end
 end
 //ROM 核的例化
 
 rom u_rom(.clk(clk),
           .address1(address1),
           .address2(address2),
           .inv_r1(inv_r1),
           .inv_r2(inv_r2)//,
           //.c(c)
       );
 //例化寻找最高非零位
 not_0 u_not_0 (
 // port map - connection between master ports and signals/registers   
     .ampout(ampout),
     .clk(clk),
     .m(m),
     .en(en),
     .syn_rst(syn_rst)
 );
complex_abs u_comlex_abs(
       .clk(clk),
       .syn_rst(~syn_rst),
       .dataa(dataa),
       .datab(datab),
       .ampout(ampout)
       );
endmodule

 

那么最终的仿真结果:如果直接查询的话,结果输出延时一个时钟周期,如果线性逼近的方法得到,延时3-5个时钟周期,这里周期设定为20ns;

占用资源报告:

增加一个求平方根的模块以后的仿真结果(数据输入后,一共需要约10个时钟周期才可以计算出一个平方更求导数值)。有一个小疑问就是怎么添加一个标志信号,让我们知道哪里输出的inv 信号是有效的

推荐阅读