設計需求
設計一個32bit浮點的加法器,out = A + B,假設AB均為無符號位,或者換個說法都為正數(shù)。
clk為系統(tǒng)時鐘;rst_n為系統(tǒng)復位,低有效;en信號控制數(shù)據(jù)輸入;busy指示模塊工作狀態(tài),busy拉高時,輸入無效;aIn和bIn是數(shù)據(jù)輸入,out_vld,指示輸出數(shù)據(jù)有效。
設計的信號列表如下:
module float_adder(
input clk,
input rst_n,
input en,
input [31:0] aIn,
input [31:0] bIn,
output reg busy,
output reg out_vld,
output reg [31:0] out
);
32bit的浮點格式
EE標準754規(guī)定了三種浮點數(shù)格式:單精度、雙精度、擴展精度。前兩者正好對應C語言里頭的float、double或者FORTRAN里頭的real、double精度類型。本文設計實現(xiàn)的為單精度。
單精度格式
單精度:N共32位,其中S占1位,E占8位,M占23位。
雙精度格式
雙精度:N共64位,其中S占1位,E占11位,M占52位。
浮點數(shù)的加法過程
運算過程:對階、尾數(shù)求和、規(guī)格化、舍入、溢出判斷
對階:
和定點數(shù)不相同的是,浮點數(shù)的指數(shù)量級不一定是一樣的,所以這也就意味著,尾數(shù)直接進行加法運算時會存在問題,也就需要首先對階數(shù)進行處理。該過程有點像科學計數(shù)法的加法處理,把科學計數(shù)法的指數(shù)化為一致,求出來指數(shù)相差多少,然后移位處理后再進行加法減法。所以這里處理也要先求階差。
如果把階碼大的向階碼小的看齊,就要把階碼大的數(shù)的尾數(shù)部分左移,階碼減小。這個操作有可能在移位過程中把尾數(shù)的高位部分移掉,這樣就引發(fā)了數(shù)據(jù)的錯誤,所以,尾數(shù)左移在計算機運算中不可取。
如果把階碼小的向階碼大的看齊,在移位過程中如果發(fā)生數(shù)據(jù)丟失,也是最右邊的數(shù)據(jù)位發(fā)生丟失,最右邊的數(shù)據(jù)位丟失,只會影響數(shù)據(jù)的精度,不會影響數(shù)據(jù)的大小。
尾數(shù)求和 :
這里就是常規(guī)的補碼加法。
規(guī)格化:
右規(guī)(尾數(shù)的絕對值太大時,右規(guī)) 尾數(shù)右移一位,階碼加1。 當尾數(shù)溢出( >1 )時,需要右規(guī) 。是否溢出,可以通過兩位的符號位得出:即尾數(shù)出現(xiàn)01.xx…xx或10.xx…xx(兩位符號位不同)
提高浮點數(shù)的表示精度,這里設計考慮比較簡單,我只考慮了同號數(shù)據(jù)相加的情況,所以這里只設計了右規(guī)的情況,不考慮符號位。
舍入判斷:
這里直接用截斷處理的方式,針對數(shù)據(jù)相加上溢的情況,規(guī)定了運算后上溢后將數(shù)據(jù)規(guī)定為最大值。
實現(xiàn)代碼
module float_adder(
input clk,
input rst_n,
input en,
input [31:0] aIn,
input [31:0] bIn,
output reg busy,
output reg out_vld,
output reg [31:0] out
);
//運算過程:對階、尾數(shù)求和、規(guī)格化、舍入、溢出判斷
//分離階數(shù)、尾數(shù)
wire signal_bit = aIn[31];
wire [7:0] pow_a = aIn[30:23];
wire [7:0] pow_b = bIn[30:23];
wire [22:0] val_a = aIn[22:0];
wire [22:0] val_b = bIn[22:0];
//找到輸入指數(shù)階數(shù)較大,和階數(shù)差
//對階:在計算機中,采用小階向大階看齊的方法,實現(xiàn)對階。即右移
reg [22:0] pow_max ;
reg [23:0] pow_dif ;
reg [22:0] val_max ;
reg [22:0] val_min ;
reg en_dly0;
always @(posedge clk or negedge rst_n) begin
if(rst_n==0)begin
pow_max <= 'd0;
val_max <= 'd0;
val_min <= 'd0;
pow_dif <= 'd0;
en_dly0 <= 'd0;
end
else if( en == 1 && busy == 0)begin
if(pow_a >= pow_b)begin
pow_max <= pow_a;
val_max <= val_a;
val_min <= val_b;
en_dly0 <= 'd1;
if ( pow_a - pow_b > 'd23) begin
pow_dif <= 'd23;
end
else begin
pow_dif <= pow_a - pow_b;
end
end
else begin
pow_max <= pow_b;
val_max <= val_b;
val_min <= val_a;
en_dly0 <= 'd1;
if ( pow_b - pow_a > 'd23) begin
pow_dif <= 'd23;
end
else begin
pow_dif <= pow_b - pow_a;
end
end
end
else begin
pow_max <= pow_max;
val_max <= val_max;
val_min <= val_min;
pow_dif <= pow_dif;
en_dly0 <= 'd0;
end
end
//移位忙指示信號
reg shift_busy;
reg [4:0] shift_cnt;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
shift_busy<='d0;
end
else if(en_dly0 == 1 )begin
shift_busy <='d1;
end
else if(shift_cnt == pow_dif)begin
shift_busy <= 0;
end
end
//移位計數(shù)
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0)begin
shift_cnt <= 'd0;
end
else if (shift_busy ==1) begin
if (shift_cnt == pow_dif) begin
shift_cnt <= shift_cnt;
end
else begin
shift_cnt <= shift_cnt + 1'b1;
end
end
else begin
shift_cnt <= 'd0;
end
end