天天看點

verilog 整數開方算法實作(逐次逼近法)

本文對原文的代碼做了一些注釋和微調,為的是更加友善了解!!!

一、逐次逼近算法

逐次逼近算法流程如圖 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個周期。

verilog 整數開方算法實作(逐次逼近法)

二、算法實作

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

           
verilog 整數開方算法實作(逐次逼近法)

可以看出,和在第二部分分析的一緻,開方結果比輸入延遲了

n/2+1

個時鐘周期,n為輸入資料位寬。

繼續閱讀