全并行流水线移位相加乘法器

基本算法

与分时复用的移位相加类似,取消分时复用,使用面积换时间,使用流水线设计,流水线填满后可以一个时钟周期计算出一个结果

  • 分别计算乘数的移位结果,并与被乘数对应位相与
  • 使用加法树将结果相加

RTL代码

移位部分

固定移位单元代码如下,当被乘数第n位为1时,输出乘数移位向左移位n位的结果

module shift_unit #(
    parameter WIDTH = 4,
    parameter SHIFT_NUM = 0
)(
    input clk,    // Clock
    input rst_n,  // Asynchronous reset active low
    input shift_valid,
    input shift_mask,
    input [WIDTH - 1:0]shift_din,

    output reg [2 * WIDTH - 1:0]shift_dout
);

wire [2 * WIDTH - 1:0]shift_din_ext;
assign shift_din_ext = {(WIDTH)'(0),shift_din};

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        shift_dout <= 'b0;
    end else if((shift_valid == 1'b1) && (shift_mask == 1'b1)) begin
        shift_dout <= shift_din_ext << SHIFT_NUM;
    end else begin
        shift_dout <= 'b0;
    end
end

endmodule

移位器代码如下,使用生成语句生成位宽个移位器

module parallel_shifter #(
    parameter WIDTH = 4
)(
    input clk,    // Clock
    input rst_n,  // Asynchronous reset active low

    input mult_valid,
    input [WIDTH - 1:0]mult1,mult2,

    output [(WIDTH ** 2) * 2 - 1:0]shift_dout
);

genvar a;
generate
    for (a = 0; a < WIDTH; a = a + 1) begin:shifter_layer
        shift_unit #(
            .WIDTH(WIDTH),
            .SHIFT_NUM(a)
        ) u_shift_unit (
            .clk(clk),    // Clock
            .rst_n(rst_n),  // Asynchronous reset active low
            .shift_valid(mult_valid),
            .shift_mask(mult2[a]),
            .shift_din(mult1),

            .shift_dout(shift_dout[a * 2 * WIDTH +: 2 * WIDTH])
        );
    end
endgenerate

endmodule

加法部分

加法部分使用加法树,可以实现流水线操作,以下为加法数单层代码

module adder_layer #(
    parameter ADDER_NUM = 4,
    parameter ADDER_WIDTH = 8
)(
    input clk,    // Clock
    input rst_n,  // Asynchronous reset active low
    input [ADDER_NUM * ADDER_WIDTH * 2 - 1:0]adder_din,

    output [ADDER_NUM * (ADDER_WIDTH + 1) - 1:0]adder_dout
);

genvar i;
generate
    for(i = 0;i < ADDER_NUM;i = i + 1) begin:adder_layer_gen
        wire [ADDER_WIDTH - 1:0]add1 = adder_din[2 * i * ADDER_WIDTH +: ADDER_WIDTH];
        wire [ADDER_WIDTH - 1:0]add2 = adder_din[(2 * i + 1) * ADDER_WIDTH +: ADDER_WIDTH];
        wire [ADDER_WIDTH:0]sum = add1 + add2;
        reg [ADDER_WIDTH:0]sum_reg;
        always @ (posedge clk or negedge rst_n) begin
            if(~rst_n) begin
                sum_reg <= 'b0;
            end else begin
                sum_reg <= sum;
            end
        end
        assign adder_dout[i * (ADDER_WIDTH + 1) +: ADDER_WIDTH + 1] = sum_reg;
    end
endgenerate

endmodule

以下为加法树代码

module adder_tree #(
    parameter LAYER_NUM = 4,
    parameter MIN_ADDER_WIDTH = 8
)(
    input clk,    // Clock
    input rst_n,  // Asynchronous reset active low

    input [(2 ** LAYER_NUM) * MIN_ADDER_WIDTH - 1:0]adder_din,
    output [LAYER_NUM + MIN_ADDER_WIDTH - 1:0]adder_dout
);

genvar i;
generate
    for(i = LAYER_NUM;i > 0;i = i - 1)begin:adder_layer_def
        wire [(2 ** i) * (MIN_ADDER_WIDTH + LAYER_NUM - i) - 1:0]layer_din;
        wire [2 ** (i - 1) * (MIN_ADDER_WIDTH + LAYER_NUM - i + 1) - 1:0]layer_dout;
        if(i == LAYER_NUM) begin
            assign layer_din = adder_din;
        end else begin
            assign layer_din = adder_layer_def[i + 1].layer_dout;
        end
        adder_layer # (
            .ADDER_NUM(2 ** (i - 1)),
            .ADDER_WIDTH(MIN_ADDER_WIDTH + LAYER_NUM - i)
        ) u_adder_layer (
            .clk(clk),    // Clock
            .rst_n(rst_n),  // Asynchronous reset active low
            .adder_din(layer_din),
            .adder_dout(layer_dout)
        );
    end
endgenerate

assign adder_dout = adder_layer_def[1].layer_dout;
endmodule

顶层

顶层组合了加法器和移位器,代码如下

module shift_adder #(
    parameter LOG2_WIDTH = 2
)(
    input clk,    // Clock
    input rst_n,  // Asynchronous reset active low

    input [2 ** LOG2_WIDTH - 1:0]mult1,mult2,
    input din_valid,

    output [(2 ** LOG2_WIDTH) * 2 - 1:0]dout
);

parameter WIDTH = 2 ** LOG2_WIDTH;

wire [(WIDTH ** 2) * 2 - 1:0]shift_dout;
parallel_shifter #(
    .WIDTH(WIDTH)
) u_parallel_shifter (
    .clk(clk),    // Clock
    .rst_n(rst_n),  // Asynchronous reset active low

    .mult_valid(din_valid),
    .mult1(mult1),
    .mult2(mult2),

    .shift_dout(shift_dout)
);

wire [LOG2_WIDTH + 2 * WIDTH:0]adder_dout;
adder_tree #(
    .LAYER_NUM(LOG2_WIDTH),
    .MIN_ADDER_WIDTH(2 * WIDTH)
) u_adder_tree (
    .clk(clk),    // Clock
    .rst_n(rst_n),  // Asynchronous reset active low

    .adder_din(shift_dout),
    .adder_dout(adder_dout)
);
assign dout = adder_dout[WIDTH * 2 - 1:0];

endmodule

测试

测试平台使用sv语法完成,因该乘法器完成一次运算的时间固定因此无输出有效信号,找到固定延迟后与使用*计算出的结果比较即可

module mult_tb (
);

parameter LOG2_WIDTH = 2;
parameter WIDTH = 2 ** LOG2_WIDTH;

logic clk,rst_n;
logic multiplier_valid;
logic [WIDTH - 1:0]multiplier1;
logic [WIDTH - 1:0]multiplier2;

logic [2 * WIDTH - 1:0]product;

shift_adder #(
    .LOG2_WIDTH(LOG2_WIDTH)
) dut (
    .clk(clk),    // Clock
    .rst_n(rst_n),  // Asynchronous reset active low

    .mult1(multiplier1),
    .mult2(multiplier2),
    .din_valid(multiplier_valid),

    .dout(product)
);

initial begin
    clk = 1'b0;
    forever begin
        #50 clk = ~clk;
    end
end

initial begin
    rst_n = 1'b1;
    #5 rst_n = 1'b0;
    #10 rst_n = 1'b1;
end

initial begin
    {multiplier_valid,multiplier1,multiplier2} = 'b0;
    repeat(100) begin
        @(negedge clk);
        multiplier1 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
        multiplier2 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
        multiplier_valid = 1'b1;
    end
    $stop();
end

reg [WIDTH - 1:0]mult11,mult12,mult13;
reg [WIDTH - 1:0]mult21,mult22,mult23;
reg [2 * WIDTH - 1:0]exp;

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        {mult11,mult12,mult13,mult21,mult22,mult23} <= 'b0;
    end else begin
        mult13 <= mult12;
        mult12 <= mult11;
        mult11 <= multiplier1;

        mult23 <= mult22;
        mult22 <= mult21;
        mult21 <= multiplier2;
    end
end

initial begin
    exp = 'b0;
    forever begin
        @(negedge clk);
        exp = mult13 * mult23;
        if(exp == product) begin
            $display("successful");
        end else begin
            $display("fail");
        end
    end
end
endmodule

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏智能计算时代

IBM Watson提供的认知计算服务介绍

Cognitive Service Introduction Twitter:@huiwenhan Weibo:@huiwenhan Agenda Wats...

3578
来自专栏生信宝典

一文教会你查找基因的启动子、UTR、TSS等区域以及预测转录因子结合位点

本文授权转载自科研小助手(ID:SciRes)斜体小一号字体为生信宝典的备注或校正。

10.8K3
来自专栏Android点滴积累

Android高效内存2:让图片占用尽可能少的内存

Android高效内存:让图片占用尽可能少的内存 一、让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336    小图:220 * 16...

30411
来自专栏GIS讲堂

基于openlayers实现聚类统计展示

在前面的博文中讲述过基于Arcgis for js如何实现聚类统计展示,在本文中讲述如何基于openlayers实现聚类统计的效果,Arcgis for js聚...

1402
来自专栏TechBox

两个宏快速计算九宫格X、Y坐标

2452
来自专栏机器人网

别让接线这件小事,拉开你与工程师的差距

导线与导线的连接、线头与接线桩的连接,事情小,责任大。本文图文并茂,让你清清楚楚看懂! 导线与导线的连接 导线的连接情况有:单股铜芯导线的直线连接、T字形连接;...

3437
来自专栏用户2442861的专栏

Python-OpenCV 处理图像(二):滤镜和图像运算

喜欢自拍的人肯定都知道滤镜了,下面代码尝试使用一些简单的滤镜,包括图片的平滑处理、灰度化、二值化等:

2291
来自专栏GIS讲堂

ArcGIS Image Server简介以及OL2中的加载

本文讲述Arcgis Image Server相关以及在OL2中如何加载Arcgis Server发布的影像服务。

1152
来自专栏瓜大三哥

优化策略之Opt_design

opt_design [-retarget] [-propconst] [-sweep] [-bram_power_opt] [-remap]

3026
来自专栏蜕变

#哆啦A梦

write('by dongdong', font=("Bradley Hand ITC", 30, "bold"))

1100

扫码关注云+社区

领取腾讯云代金券