【25秋招】数字IC复习-乘法器

乘除法基本原理

乘法器和除法器其实都有很多种设计思路,主要复习了一下最基本的一些设计思路。

乘法

主要是进行移位相加:
在这里插入图片描述

除法
一种实现思路:其中a的高位为余数,低位为商
在这里插入图片描述

牛客verilog-流水线乘法器

牛客的VL56,实现一个4bit的流水线乘法器

1.移位相加法

正好前几天复习看了菜鸟教程,于是用相似的思路写了如下代码:

module multi_pipe#(
	parameter size = 4
)(  input 						clk 		,   
	input 						rst_n		,
	input	[size-1:0]			mul_a		,
	input	[size-1:0]			mul_b		,
 	output	reg	[size*2-1:0]	mul_out		
);
	wire [size-1:0] zero_d;
	assign zero_d = 'd0;

	wire [size*2-1:0] pipe_a [size-1:0];
	wire [size-1:0] pipe_b [size-1:0];
	wire [size*2-1:0] add_reg [size-1:0];

	multi_step#(
		.size(size)
	) multi_step_inst0 (
		.clk		(clk),
		.rst_n		(rst_n),
		.step_a_i	({zero_d,mul_a}),
		.step_b_i	(mul_b),
		.pipe_in	('d0),
		.step_a_o	(pipe_a[0]),
		.step_b_o	(pipe_b[0]),
		.pipe_out	(add_reg[0])
	);

	genvar i;
	generate
		for(i=0; i<size-1; i=i+1) begin: pipe_step
			multi_step#(
				.size(size)
			) multi_step_inst (
				.clk		(clk),
				.rst_n		(rst_n),
				.step_a_i	(pipe_a[i]),
				.step_b_i	(pipe_b[i]),
				.pipe_in	(add_reg[i]),
				.step_a_o	(pipe_a[i+1]),
				.step_b_o	(pipe_b[i+1]),
				.pipe_out	(add_reg[i+1])
			);
		end
	endgenerate

	always@(*) begin
		mul_out = add_reg[size-1];
	end
endmodule

module multi_step#(
	parameter size = 4
)(  input			 		clk,
	input 			  	    rst_n,
	input 	   [size*2-1:0] step_a_i,
	input 		 [size-1:0] step_b_i,
	input  	   [size*2-1:0] pipe_in,
	output reg [size*2-1:0] step_a_o,
	output   reg [size-1:0] step_b_o,
	output reg [size*2-1:0] pipe_out
);
	wire [size*2-1:0] step_add;
	assign step_add = step_b_i[0] ? step_a_i : 'd0;

	always@(posedge clk or negedge rst_n) begin
		step_a_o <= step_a_i << 1;
		step_b_o <= step_b_i >> 1;
	end

	always@(posedge clk or negedge rst_n) begin
		if(!rst_n)
			pipe_out <= 'd0;
		else
			pipe_out <= pipe_in + step_add;
	end
endmodule

仿真结果:(这里是8bit的乘法)
在这里插入图片描述

2.加法树法

用上面的方法发现和牛客的时序不一样(不得不吐槽,牛客的verilog题描述实在是太不清楚了,有的时序也不给),于是就改用加法树流水的方式写了一个,其实本质上还是在进行移位相加。
代码如下:(这里同样是8bit)

`timescale 1ns/1ns
module multi_pipe(
    input [7:0]mul_a ,
    input [7:0]mul_b ,
    input clk,
    output reg [15:0]mul_out
);

    wire [15:0] temp_a [7:0];
    reg [15:0] pipe_add_0 [3:0];
    reg [15:0] pipe_add_1 [1:0];

    genvar i;
    generate
        for(i=0; i<8; i=i+1) begin:shift_a
            assign temp_a[i] = mul_b[i] ? {{(8-i){1'b0}},mul_a,{i{1'b0}}} : 'd0;
        end
    endgenerate

    integer j;
    always@(posedge clk) begin
        for(j=0; j<4; j=j+1)
            pipe_add_0[j] <= temp_a[2*j] + temp_a[2*j+1];
    end

    always@(posedge clk) begin
        pipe_add_1[0] <= pipe_add_0[0] + pipe_add_0[1];
        pipe_add_1[1] <= pipe_add_0[2] + pipe_add_0[3];
    end

    always@(posedge clk) begin
        mul_out <= pipe_add_1[0] + pipe_add_1[1];
    end
endmodule

仿真结果如下:
在这里插入图片描述

3.对比

明显用加法树设计的延迟更小,三个周期就能得到结果,虽然这两种方法后续都能每个周期得到一个结果,但是使用加法树的方法要更加省面积,前一种方法用加法链的方式,级数高了之后消耗的寄存器资源是比较大的。这一点通过vivado综合结果也能看出来。
第一种乘法器综合结果(8bit):
在这里插入图片描述
第二种乘法器综合结果(8bit):
在这里插入图片描述

有符号乘法器

首先,对于verilog的带符号运算,需要知道的是,如果表达式中有一个无符号数,则所有的操作数都会被强行转换为无符号数,这在涉及到有符号数运算时尤其需要注意。
由于之前项目中需要进行有符号数的乘法,这里还将加法树流水的乘法器改为了有符号数,实际上就是先将有符号数变无符号数(即补码变原码),然后在进行无符号数乘法,最后输出时再转换为有符号数。
代码如下:

module multi_pipe_signed(
    input [7:0]mul_a ,
    input [7:0]mul_b ,
    input clk,
    output reg [15:0]mul_out
);
	//保存符号位
    wire sign;
    reg sign_reg_0,sign_reg_1;
    assign sign = mul_a[7] ^ mul_b[7];
    //转换为原码
    wire [6:0] unsigned_a,unsigned_b;
    assign unsigned_a = mul_a[7] ? ~mul_a[6:0]+1 : mul_a[6:0];
    assign unsigned_b = mul_b[7] ? ~mul_b[6:0]+1 : mul_b[6:0];
    //加法树流水线
    wire [13:0] temp_a [6:0];
    reg [13:0] pipe_add_0 [3:0];
    reg [13:0] pipe_add_1 [1:0];

    genvar i;
    generate
        for(i=0; i<7; i=i+1) begin:shift_a
            assign temp_a[i] = unsigned_b[i] ? {{(7-i){1'b0}},unsigned_a,{i{1'b0}}} : 'd0;
        end
    endgenerate

    always@(posedge clk) begin
        pipe_add_0[0] <= temp_a[0] + temp_a[1];
        pipe_add_0[1] <= temp_a[2] + temp_a[3];
        pipe_add_0[2] <= temp_a[4] + temp_a[5];
        pipe_add_0[3] <= temp_a[6];
        sign_reg_0 <= sign;
    end

    always@(posedge clk) begin
        pipe_add_1[0] <= pipe_add_0[0] + pipe_add_0[1];
        pipe_add_1[1] <= pipe_add_0[2] + pipe_add_0[3];
        sign_reg_1 <= sign_reg_0;
    end

    wire [13:0] pipe_out;
    assign pipe_out = pipe_add_1[0] + pipe_add_1[1];
    always@(posedge clk) begin
        mul_out <= sign_reg_1 ? {2'b11,~pipe_out+1} : {2'b00,pipe_out};
    end
endmodule

仿真结果:
在这里插入图片描述
综合结果:
在这里插入图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值