數位除法器設計與實現 - 基於FPGA的移位
數位除法器設計與實現 - 基於FPGA的移位除法算法
前言
在數位電路設計中,除法運算是一個重要但相對複雜的算術操作。本文將介紹一個基於FPGA的32位元除法器設計,採用移位除法算法實現。這個設計不僅具有良好的可擴展性,還提供了完整的測試驗證平台。
一、設計目標
- 實現32位元無號數除法運算
- 支援可參數化的資料位寬
- 提供完整的控制機制
- 確保運算結果的準確性
二、設計架構
2.1 模組說明
-
div_go_tick: 啟動信號處理模組
- 功能:產生單一時鐘週期的觸發脈衝
- 避免多週期觸發問題
-
division: 除法器核心模組
- 功能:執行移位除法運算
- 提供商數和移位計數輸出
三、完整程式碼
3.1 訊號邊緣檢測模組
// *********************************************************************************
// 訊號邊緣檢測模組
// 功能:檢測輸入訊號的上升邊緣,產生一個時鐘週期的脈衝
// 應用:用於除法器的啟動控制
// *********************************************************************************
module div_go_tick (
input clk, // 時鐘輸入
input nRst, // 低電位有效的重置信號
input iSignal, // 輸入訊號
output oSignal // 輸出脈衝信號
);
reg [1:0] rvSignal_d, rvSignal_q;
always @(posedge clk or negedge nRst) begin
if(!nRst)
rvSignal_q <= 2'b00;
else
rvSignal_q <= rvSignal_d;
end
always @(*) begin
rvSignal_d = {rvSignal_q[0], iSignal};
end
assign oSignal = (rvSignal_q == 2'b01) ? 1'b1 : 1'b0;
endmodule
3.2 可參數化的除法器模組
module division#(
parameter DATA_WIDTH = 32 // 資料位寬參數,預設32位元
)
(
input clk, // 時脈輸入
input nRst, // 低電位有效的重置信號
input [DATA_WIDTH-1:0] ivDivisor, // 除數輸入
input [DATA_WIDTH-1:0] ivDividend, // 被除數輸入
output [DATA_WIDTH-1:0] ovQuotient, // 商數輸出
output [7:0] ovShift, // 移位計數輸出
input iGoTick, // 開始運算觸發信號
output oDone // 運算完成信號
);
// 常數定義
localparam DATA_WIDTH_ = DATA_WIDTH - 1;
// 內部暫存器宣告
reg [DATA_WIDTH-1:0] rvDivisor_d, rvDivisor_q;
reg [DATA_WIDTH-1:0] rvQuotientShift_d, rvQuotientShift_q;
reg [DATA_WIDTH-1:0] rvRemainder_d, rvRemainder_q;
wire [DATA_WIDTH-1:0] wvRemainder;
reg [DATA_WIDTH-1:0] rvQuotient_d, rvQuotient_q;
reg [7:0] rvShift_d, rvShift_q;
// 控制信號暫存器
reg rFlag_d, rFlag_q;
reg [7:0] rvProcessCnt_d, rvProcessCnt_q;
wire addProcessCnt, endProcessCnt;
reg [7:0] rvShiftCnt_d, rvShiftCnt_q;
reg [DATA_WIDTH-1:0] rvDivisorShift_d, rvDivisorShift_q;
reg rProcessEn_d, rProcessEn_q;
wire [DATA_WIDTH-1:0] wvCompare;
reg rDone_d, rDone_q;
//=============================================================================
// 第一階段:初始對齊
// 通過移位操作找到正確的起始位置
//=============================================================================
// 比較邏輯:被除數減去移位後的除數
assign wvCompare = ivDividend - rvDivisorShift_q;
// 除數移位暫存器
always @(posedge clk or negedge nRst) begin
if(!nRst)
rvDivisorShift_q <= 8'h0;
else
rvDivisorShift_q <= rvDivisorShift_d;
end
// 除數移位控制邏輯
always @(*) begin
if(endProcessCnt)
rvDivisorShift_d = 8'h0;
else if(iGoTick)
rvDivisorShift_d = ivDivisor;
else if(wvCompare[DATA_WIDTH-1] == 1'b0) // 如果比較結果為正
rvDivisorShift_d = rvDivisorShift_q << 1; // 繼續左移
else
rvDivisorShift_d = rvDivisorShift_q;
end
// 移位計數器邏輯
always @(posedge clk or negedge nRst) begin
if(!nRst)
rvShiftCnt_q <= 8'h0;
else
rvShiftCnt_q <= rvShiftCnt_d;
end
// 移位計數控制
always @(*) begin
if(iGoTick || !rFlag_q)
rvShiftCnt_d = 8'h0;
else if(wvCompare[DATA_WIDTH-1] == 1'b0)
rvShiftCnt_d = rvShiftCnt_q + 1'b1;
else
rvShiftCnt_d = rvShiftCnt_q;
end
//=============================================================================
// 處理控制邏輯
//=============================================================================
// 處理啟用控制
always @(posedge clk or negedge nRst) begin
if(!nRst)
rProcessEn_q <= 1'b0;
else
rProcessEn_q <= rProcessEn_d;
end
always @(*) begin
if(iGoTick || endProcessCnt)
rProcessEn_d = 1'b0;
else if(wvCompare[DATA_WIDTH-1] == 1'b1) // 當比較結果為負時開始處理
rProcessEn_d = 1'b1;
else
rProcessEn_d = rProcessEn_q;
end
// 運算狀態標誌控制
always @(posedge clk or negedge nRst) begin
if(!nRst)
rFlag_q <= 1'b0;
else
rFlag_q <= rFlag_d;
end
always @(*) begin
if(endProcessCnt)
rFlag_d = 1'b0;
else if(iGoTick)
rFlag_d = 1'b1;
else
rFlag_d = rFlag_q;
end
//=============================================================================
// 第二階段:執行除法運算
//=============================================================================
// 餘數計算
assign wvRemainder = rvRemainder_q - rvDivisor_q;
// 處理計數器控制
assign addProcessCnt = rProcessEn_q;
assign endProcessCnt = addProcessCnt && (rvProcessCnt_q == DATA_WIDTH);
// 處理計數器邏輯
always @(posedge clk or negedge nRst) begin
if(!nRst)
rvProcessCnt_q <= 8'h0;
else
rvProcessCnt_q <= rvProcessCnt_d;
end
always @(*) begin
if(endProcessCnt)
rvProcessCnt_d = 8'h0;
else if(addProcessCnt)
rvProcessCnt_d = rvProcessCnt_q + 1'b1;
else
rvProcessCnt_d = rvProcessCnt_q;
end
// 最終輸出賦值
assign ovQuotient = rvQuotient_q;
assign ovShift = rvShift_q;
assign oDone = rDone_q;
endmodule
3.3 測試平台
// *********************************************************************************
// 測試平台
// *********************************************************************************
`timescale 1ns/1ps
module division_tb;
localparam DATA_WIDTH = 32; // 設定資料位寬為32位元
// 時鐘和控制信號
reg clk; // 系統時鐘
reg nRst; // 低電位有效的重置信號
reg [DATA_WIDTH-1:0] divisor1; // 除數輸入
reg [DATA_WIDTH-1:0] dividend1; // 被除數輸入
wire [DATA_WIDTH-1:0] quotient1; // 商數輸出
wire [7:0] shift1; // 移位計數輸出
reg go; // 啟動信號
wire goTick; // 邊緣檢測後的啟動脈衝
// 邊緣檢測模組實例化
div_go_tick div_go_tick_inst (
.clk (clk),
.nRst (nRst),
.iSignal (go), // 輸入啟動信號
.oSignal (goTick) // 輸出脈衝信號
);
// 除法器模組實例化
division #(
.DATA_WIDTH(DATA_WIDTH)
) DUT (
.clk (clk),
.nRst (nRst),
.ivDivisor (divisor1),
.ivDividend (dividend1),
.ovQuotient (quotient1),
.ovShift (shift1),
.iGoTick (goTick)
);
// 時鐘產生器:週期為20ns (50MHz)
initial
clk = 1'b0;
always #10 clk = ~clk;
// 系統重置任務
task SYSTEM_RESET;
begin
nRst = 1'b0; // 啟動重置
repeat(100) @(posedge clk); // 等待100個時鐘週期
nRst = 1'b1; // 釋放重置
end
endtask
// 測試程序
initial begin
// 初始化和系統重置
SYSTEM_RESET;
go = 0;
#1000;
// 測試案例1: 300 ÷ 300 = 1
divisor1 = 300;
dividend1 = 300;
#100;
go = 1;
#100;
go = 0;
// 測試案例2: 13 ÷ 3 = 4.333...
#1000;
divisor1 = 3;
dividend1 = 13;
#100;
go = 1;
#100;
go = 0;
// 測試案例3: 280 ÷ 256 = 1.09375
#1000;
divisor1 = 256;
dividend1 = 280;
#100;
go = 1;
#100;
go = 0;
// 測試案例4: 83 ÷ 19 = 4.368421...
#1000;
divisor1 = 19;
dividend1 = 83;
#100;
go = 1;
#100;
go = 0;
// 等待運算完成
#300000;
// 結束模擬
$stop;
end
endmodule
四、波形分析
4.1 實際波形結果
波形說明:
-
啟動控制信號
- iSignal 觸發後產生 goTick 脈衝
- 單一時鐘週期的控制信號
-
除法運算過程
- 初始對齊階段
- 移位除法運算階段
- 結果輸出階段
-
關鍵時序點
- 運算開始時間點
- 商數產生時間點
- 完成信號產生時間點
五、結論
本設計實現了一個穩定可靠的除法器,具有:
- 良好的可擴展性
- 準確的運算結果
- 完整的控制機制
- 清晰的時序關係
六、未來優化方向
- 增加管線級數提高運算效率
- 添加除數為零檢查
- 支援有號數運算
- 優化關鍵路徑降低延遲