本文對原文的代碼做了一些注釋和微調,為的是更加友善了解!!!
一、逐次逼近算法
逐次逼近算法流程如圖 1所示,首先資料輸入data[7:0],接着設定實驗值D_z[3:0]和确定值D_q[3:0],然後按照從高往低的順序,依次将每一位置1(如D_z[3]置1),再将實驗值平方後與輸入資料比較,若實驗值的平方大于輸入值(
D_z^2 > data
),則此位為0(D_q[3]為0),反之(
D_z^2 ≤ data
)此位為1(D_q[3]為1);以此疊代到最後一位。
可見,如果是n bit的資料,那麼需要n/2次疊代,每次計算如果一個周期,則需要n/2個周期。

二、算法實作
module sqrt
#( //8,4,5
parameter d_width = 8,
parameter q_width = d_width/2,
parameter r_width = q_width + 1 )
(
input wire clk,
input wire rst_n,
input wire i_vaild,
input wire [d_width-1:0] data_i, //輸入
output reg o_vaild,
output reg [q_width-1:0] data_o, //輸出
output reg [r_width-1:0] data_r //餘數
);
//--------------------------------------------------------------------------------
// 注意這裡使用了流水線操作,輸出資料的位寬決定了流水線的級數,級數=q_width
reg [d_width-1:0] D [q_width:1]; //儲存依次輸入進來的被開方資料
reg [q_width-1:0] Q_z [q_width:1]; //儲存每一級流水線的實驗值
reg [q_width-1:0] Q_q [q_width:1]; //由實驗值與真實值的比較結果确定的最終值
reg valid_flag [q_width:1]; //表示此時寄存器D中對應位置的資料是否有效
//--------------------------------------------------------------------------------
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
D[q_width] <= 0;
Q_z[q_width] <= 0;
Q_q[q_width] <= 0;
valid_flag[q_width] <= 0;
end
else if(i_vaild)
begin
D[q_width] <= data_i; //被開方資料
Q_z[q_width] <= {1'b1,{(q_width-1){1'b0}}}; //實驗值設定,先将最高位設為1
Q_q[q_width] <= 0; //實際計算結果
valid_flag[q_width] <= 1;
end
else
begin
D[q_width] <= 0;
Q_z[q_width] <= 0;
Q_q[q_width] <= 0;
valid_flag[q_width] <= 0;
end
end
//-------------------------------------------------------------------------------
// 疊代計算過程,流水線操作
//-------------------------------------------------------------------------------
generate
genvar i;
//i=3,2,1
for(i=q_width-1;i>=1;i=i-1)
begin:U
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
D[i] <= 0;
Q_z[i] <= 0;
Q_q[i] <= 0;
valid_flag[i] <= 0;
end
//在上一時鐘周期将資料讀入并設定資料有效,下一個周期開始比較資料
else if(valid_flag[i+1])
begin
//根據根的實驗值最高位置為1後的平方值與真實值的大小比較結果,
//确定最高位是否應該為1以及将次高位的指派為1,準備開始下一次比較!!!
//注意,這裡最後是給Q_z[i]和Q_q[i]指派,相當于把上一周期的資料處理後
//移到了寄存器的下一個位置,而Q_z[i+1]和Q_q[i+1]則負責接收新的資料
if(Q_z[i+1]*Q_z[i+1] > D[i+1])
begin
//如果實驗值的平方過大,那麼就将最高位置為0,次高位置1,
//并将資料從位置i+1移至下一個位置i,而i+1的位置用于接收下一個輸入的資料
Q_z[i] <= {Q_q[i+1][q_width-1:i],1'b1,{{i-1}{1'b0}}};
Q_q[i] <= Q_q[i+1];
end
else
begin
Q_z[i] <= {Q_z[i+1][q_width-1:i],1'b1,{{i-1}{1'b0}}};
Q_q[i] <= Q_z[i+1];
end
D[i] <= D[i+1];
valid_flag[i] <= 1;
end
else
begin
valid_flag[i] <= 0;
D[i] <= 0;
Q_q[i] <= 0;
Q_z[i] <= 0;
end
end
end
endgenerate
//--------------------------------------------------------------------------------
// 計算餘數與最終平方根
//--------------------------------------------------------------------------------
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
data_o <= 0;
data_r <= 0;
o_vaild <= 0;
end
else if(valid_flag[1])
begin
if(Q_z[1]*Q_z[1] > D[1])
begin
data_o <= Q_q[1];
data_r <= D[1] - Q_q[1]*Q_q[1];
o_vaild <= 1;
end
else
begin
data_o <= {Q_q[1][q_width-1:1],Q_z[1][0]};
data_r <= D[1] - {Q_q[1][q_width-1:1],Q_z[1][0]}*{Q_q[1][q_width-1:1],Q_z[1][0]};
o_vaild <= 1;
end
end
else
begin
data_o <= 0;
data_r <= 0;
o_vaild <= 0;
end
end
endmodule
Q_q會比Q_z晚更新一個周期,因為它需要利用Q_z與真實值比較後的結果選擇要不要更新。
以8位資料為例,在i=1時,此時實驗值Q_z已經進行了4次更新,3次比較,是以Q_q更新了3次。是以還需要最後一次Q_z比較,才可以确定最後的結果。最後一次比較的時候,Q_z的最低位已經被置1,而Q_q的最低位還沒有更新。最後一次比較就不更新Q_z和Q_q了,而是直接将正确值指派給data_o。由以上可知,開方結果比輸入資料延遲
n/2+1
個時鐘周期。
既然更新了4次Q_z,那麼資料寄存器D就要至少能夠儲存4個輸入資料。
三、設計仿真
`define d_w 16
`define q_w `d_w / 2
`define r_w `q_w + 1
//--------------------------------------------------------------------------------
module tb_sqrt;
//--------------------------------------------------------------------------------
// Inputs
reg clk;
reg rst_n;
reg i_vaild;
reg [`d_w-1:0] data_i;
// Outputs
wire o_vaild;
wire [`q_w-1:0] data_o;
wire [`r_w-1:0] data_r;
//--------------------------------------------------------------------------------
// Instantiate the Unit Under Test (UUT)
sqrt
#(
.d_width (`d_w),
.q_width (`q_w),
.r_width (`r_w)
)
uut
(
.clk ( clk ),
.rst_n ( rst_n ),
.i_vaild ( i_vaild ),
.data_i ( data_i ),
.o_vaild ( o_vaild ),
.data_o ( data_o ),
.data_r ( data_r )
);
//--------------------------------------------------------------------------------
initial begin
// Initialize Inputs
clk = 0;
rst_n = 0;
// Wait 10 ns for global reset to finish
#10;
rst_n = 1;
// Add stimulus here
end
always #5 clk = ~ clk ;
reg [`d_w-1:0] cnt ;
//--------------------------------------------------------------------------------
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
i_vaild <= 0;
data_i <= 0;
cnt <= 0;
end
else if(cnt < 10)
begin
i_vaild <= 1;
data_i <= {$random} % {`d_w{1'b1}};
cnt <= cnt + 1;
end
else
begin
i_vaild <= 0;
data_i <= 0;
cnt <= cnt;
end
end
//--------------------------------------------------------------------------------
endmodule
可以看出,和在第二部分分析的一緻,開方結果比輸入延遲了
n/2+1
個時鐘周期,n為輸入資料位寬。