3. 定点乘法器设计
任务说明
设计一个参数化定点乘法器RTL模块,支持有符号与无符号两种操作数模式,输出位宽自动扩展以避免截断,并提供溢出标志。输入为两个N位操作数,输出为2N位乘积结果,同时输出1位溢出指示(当结果无法由N位表示时置位)。
端口定义
module fixed_mult #(
parameter N = 8 // 操作数位宽
)(
input wire clk,
input wire rst_n,
input wire sign_en, // 1:有符号乘法, 0:无符号乘法
input wire [N-1:0] a,
input wire [N-1:0] b,
input wire valid_in,
output reg [2*N-1:0] product, // 完整乘积
output reg overflow, // 溢出标志
output reg valid_out
);
实现方案
乘法器采用组合逻辑完成核心乘法运算,在时钟上升沿寄存结果。有符号模式将输入转换为补码后执行乘法;无符号模式直接相乘。溢出判定规则:
- 无符号模式:乘积高N位非零即溢出
- 有符号模式:乘积超出N位有符号数范围[-2^(N-1), 2^(N-1)-1]即溢出
RTL代码
module fixed_mult #(
parameter N = 8
)(
input wire clk,
input wire rst_n,
input wire sign_en,
input wire [N-1:0] a,
input wire [N-1:0] b,
input wire valid_in,
output reg [2*N-1:0] product,
output reg overflow,
output reg valid_out
);
// 内部信号
wire [2*N-1:0] prod_uns; // 无符号乘积
wire [2*N-1:0] prod_sgn; // 有符号乘积
wire [2*N-1:0] prod_sel; // 选择后的乘积
wire ovf_uns; // 无符号溢出
wire ovf_sgn; // 有符号溢出
wire ovf_sel; // 选择后的溢出
// 无符号乘法
assign prod_uns = a * b;
// 有符号乘法(将输入扩展为2N位后相乘)
wire signed [2*N-1:0] a_sgn = {{N{a[N-1]}}, a};
wire signed [2*N-1:0] b_sgn = {{N{b[N-1]}}, b};
assign prod_sgn = a_sgn * b_sgn;
// 根据模式选择乘积
assign prod_sel = sign_en ? prod_sgn : prod_uns;
// 溢出检测
// 无符号:高N位非零
assign ovf_uns = (prod_uns[2*N-1:N] != 0);
// 有符号:结果超出N位有符号范围
// 检查高N位是否全为符号扩展位(全0或全1)
wire [N-1:0] high_part = prod_sgn[2*N-1:N];
wire sign_bit = prod_sgn[2*N-1];
wire [N-1:0] sign_ext = {N{sign_bit}};
assign ovf_sgn = (high_part != sign_ext);
assign ovf_sel = sign_en ? ovf_sgn : ovf_uns;
// 时序输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
product <= 0;
overflow <= 0;
valid_out <= 0;
end else begin
product <= prod_sel;
overflow <= ovf_sel;
valid_out <= valid_in;
end
end
endmodule
测试平台
module tb_fixed_mult;
parameter N = 8;
reg clk, rst_n, sign_en, valid_in;
reg [N-1:0] a, b;
wire [2*N-1:0] product;
wire overflow, valid_out;
fixed_mult #(.N(N)) dut (
.clk(clk), .rst_n(rst_n), .sign_en(sign_en),
.a(a), .b(b), .valid_in(valid_in),
.product(product), .overflow(overflow), .valid_out(valid_out)
);
initial clk = 0;
always #5 clk = ~clk;
// 测试向量
initial begin
rst_n = 0; sign_en = 0; valid_in = 0; a = 0; b = 0;
#10 rst_n = 1;
// 测试1:无符号 200 * 3 = 600 (0x258)
@(posedge clk);
sign_en = 0; a = 8'd200; b = 8'd3; valid_in = 1;
@(posedge clk);
valid_in = 0;
// 预期 product=16'd600, overflow=0
$display("Test1: product=%0d overflow=%b", product, overflow);
// 测试2:无符号 200 * 200 = 40000 (溢出)
@(posedge clk);
sign_en = 0; a = 8'd200; b = 8'd200; valid_in = 1;
@(posedge clk);
valid_in = 0;
// 预期 product=40000, overflow=1
$display("Test2: product=%0d overflow=%b", product, overflow);
// 测试3:有符号 -100 * 2 = -200
@(posedge clk);
sign_en = 1; a = -8'sd100; b = 8'sd2; valid_in = 1;
@(posedge clk);
valid_in = 0;
// 预期 product=-200 (补码), overflow=0
$display("Test3: product=%0d overflow=%b", $signed(product), overflow);
// 测试4:有符号 -100 * -100 = 10000 (溢出)
@(posedge clk);
sign_en = 1; a = -8'sd100; b = -8'sd100; valid_in = 1;
@(posedge clk);
valid_in = 0;
// 预期 product=10000, overflow=1
$display("Test4: product=%0d overflow=%b", $signed(product), overflow);
#20 $finish;
end
initial begin
$monitor("t=%0t sign_en=%b a=%0d b=%0d product=%0d overflow=%b valid_out=%b",
$time, sign_en, a, b, product, overflow, valid_out);
end
endmodule
验证方法
使用ModelSim或Vivado运行上述测试平台,观察打印输出。判定标准:
- Test1输出product=600, overflow=0
- Test2输出product=40000, overflow=1
- Test3输出product=-200, overflow=0
- Test4输出product=10000, overflow=1
若四个测试用例的product与overflow均与预期一致,则验证通过。
综合与落地
该模块可直接用于ASIC或FPGA综合。综合时设置参数N为目标位宽(如16、32)。时序路径为:输入a/b → 乘法器 → 溢出比较 → 输出寄存器,关键路径延迟由乘法器决定。若需更高吞吐率,可插入流水级拆分乘法运算。