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

基本算法

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

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

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容