1. 1bit全加器

1bit全加器电路结构如下所示,其中,a和b为相加的数,而cin为进位输入,s为本位加的结果,cout为进位输出。

可以用下面组合逻辑代码写1bit全加器。

module 1bit_adder (
    input   a   ,
    input   b   ,
    input   cin ,

    output  s   ,
    output  cout
);

wire    p ;     
wire    g ;     

assign  p = a | b ;
assign  g = a & b ;

assign  s = a ^ b ^ cin ;
assign  cout = (p & cin) | g ; 
    
endmodule

其中p为进位传递,若cin为1,并且a、b中有一个为1,那么p将cin进位传递到进位输出;g为产生进位,是a和b加的过程中产生的进位,那就是a和b都为1情况下。当a、b和cin中有奇数个1时,本位计算结果s就为1,可用异或逻辑。这种方法在后面超前进位加法器中也用到了。

在Vivado中综合出的电路如下图。

或者直接通过下面代码写全加器。

module gold_adder_1bit(
        input       a   ,
        input       b   ,
        input       cin ,
        output      cout,
        output      sum 
    );

    assign {cout, sum} = a + b + cin ;
endmodule

Vivado综合工具会自动优化,上述代码综合出的电路如下。

将上述用组合逻辑搭建的超前进位加法器作为测试,而将直接综合成加法器的代码作为gold模块。通过特殊情况赋值和随机产生方式,生成测试激励,将测试结果与gold生成的结果比对,如果出现错误,自动停止,对应的testbench代码如下:

module adder_1bit_tb(

    );

    reg         a   ;
    reg         b   ;
    reg         cin ;
    wire        test_s   ;
    wire        test_cout;

    wire        gold_sum ;
    wire        gold_cout;

    reg         clk ;

    always #5 clk = ~clk ;

    integer i ;

    initial begin
        clk = 0 ;
        a = 0 ; b = 0 ; cin = 0 ;
        repeat(3) @ (posedge clk) ;
        a = 1 ; b = 1 ; cin = 0 ;
        repeat(3) @ (posedge clk) ;
        a = 1 ; b = 0 ; cin = 1 ;
        repeat(3) @ (posedge clk) ;
        a = 0 ; b = 1 ; cin = 1 ;
        repeat(3) @ (posedge clk) ;
        a = 1 ; b = 1 ; cin = 1 ;
        repeat(3) @ (posedge clk) ;
        for (i = 0 ; i<= 100 ; i = i+1begin
            a = $random();
            b = $random();
            cin = $random();
            @ (posedge clk) ;
        end
        #20 ;
        $stop ;
    end

    reg     test_error = 0  ;
    
    always @(posedge clk ) begin
        #1 ;
        if((test_s != gold_sum) || (test_cout != gold_cout)) begin
            test_error  <=  1'b1 ;
            #10;
            $stop ;
        end
    end

    adder_1bit u_adder_1bit (
        .a      (a      )   ,
        .b      (b      )   ,
        .cin    (cin    )   ,
        .s      (test_s   )   ,
        .cout   (test_cout)   
    );

    gold_adder_1bit u_gold_adder_1bit (
        .a      (a      )   ,
        .b      (b      )   ,
        .cin    (cin    )   ,
        .sum    (gold_sum )   ,
        .cout   (gold_cout)   
    );
endmodule

2. 行波进位加法器(Ripple Adder)

可以通过1bit全加器搭建8bit全加器,将每个bit都使用全加器加,将进位串行,这种1bit级联起来的加法器也叫行波进位加法器。与我们手工算一个加法一样,先算低位,再算高位。

可直接用1bit行波进位方法搭,verilog代码如下,也可直接把1bit行波进位加法器例化为子模块,例化8个子模块。

module adder_8bit(
    input   [7:0]   a   ,
    input   [7:0]   b   ,
    input           cin ,
    output  [7:0]   sum ,
    output          cout
    );

wire    [7:0]   p ;     
wire    [7:0]   g ;  
wire    [7:0]   c ;   

assign  c[0] = cin ;
assign  p[0] = a[0] | b[0] ;
assign  g[0] = a[0] & b[0] ;
assign  sum[0] = a[0] ^ b[0] ^ c[0] ;
assign  c[1] = (p[0] & c[0]) | g[0] ; 

assign  p[1] = a[1] | b[1] ;
assign  g[1] = a[1] & b[1] ;
assign  sum[1] = a[1] ^ b[1] ^ c[1] ;
assign  c[2] = (p[1] & c[1]) | g[1] ; 

assign  p[2] = a[2] | b[2] ;
assign  g[2] = a[2] & b[2] ;
assign  sum[2] = a[2] ^ b[2] ^ c[2] ;
assign  c[3] = (p[2] & c[2]) | g[2] ; 

assign  p[3] = a[3] | b[3] ;
assign  g[3] = a[3] & b[3] ;
assign  sum[3] = a[3] ^ b[3] ^ c[3] ;
assign  c[4] = (p[3] & c[3]) | g[3] ; 

assign  p[4] = a[4] | b[4] ;
assign  g[4] = a[4] & b[4] ;
assign  sum[4] = a[4] ^ b[4] ^ c[4] ;
assign  c[5] = (p[4] & c[4]) | g[4] ; 

assign  p[5] = a[5] | b[5] ;
assign  g[5] = a[5] & b[5] ;
assign  sum[5] = a[5] ^ b[5] ^ c[5] ;
assign  c[6] = (p[5] & c[5]) | g[5] ; 

assign  p[6] = a[6] | b[6] ;
assign  g[6] = a[6] & b[6] ;
assign  sum[6] = a[6] ^ b[6] ^ c[6] ;
assign  c[7] = (p[6] & c[6]) | g[6] ; 

assign  p[7] = a[7] | b[7] ;
assign  g[7] = a[7] & b[7] ;
assign  sum[7] = a[7] ^ b[7] ^ c[7] ;
assign  cout = (p[7] & c[7]) | g[7] ; 

endmodule

综合结果如下,可以看到这种通过1bit级联方法搭建的8bit加法器,延时会很大,因为必须算完低bit位才能算高bit位。因此,这种形式的加法器性能很差。

将上述8bit行波进位加法器作为测试,而将直接综合成8bit加法器的代码作为gold模块。通过随机产生方式,生成测试激励,将测试结果与gold生成的结果比对,如果出现错误,自动停止,对应的testbench代码如下:

module adder_8bit_tb(

    );

    reg   [7:0]      a          ;
    reg   [7:0]      b          ;
    reg              cin        ;
    wire  [7:0]      test_s     ;
    wire             test_cout  ;

    wire  [7:0]      gold_sum   ;
    wire             gold_cout  ;

    reg         clk ;

    always #5 clk = ~clk ;

    integer i ;

    initial begin
        clk = 0 ;
        a = 0 ; b = 0 ; cin = 0 ;
        for (i = 0 ; i<= 1000 ; i = i+1begin
            if(i<=200begin
                a = a + 1 ;
                b = b + 3 ;
                cin = $random ;
            end
            else if(i<=400begin
                a = a + 2 ;
                b = b + 1 ;
                cin = $random ;             
            end
            else begin
                a = $random();
                b = $random();
                cin = $random();             
            end
            @ (posedge clk) ;   
        end
        #20 ;
        $stop ;
    end

    reg     test_error = 0  ;
    
    always @(posedge clk ) begin
        #1 ;
        if((test_s != gold_sum) || (test_cout != gold_cout)) begin
            test_error  <=  1'b1 ;
            $display("ERROR!!!") ;
            #10;
            $stop ;
        end
    end

    adder_8bit u_adder_8bit (
        .a          (a      )  ,        
        .b          (b      )  ,        
        .cin        (cin    )  ,        
        .sum        (test_s )  ,        
        .cout       (test_cout)             
    );

    gold_adder_8bit u_gold_adder_8bit (
        .a      (a      )   ,
        .b      (b      )   ,
        .cin    (cin    )   ,
        .sum    (gold_sum )   ,
        .cout   (gold_cout)   
    );
endmodule

其中,gold_adder_8bit模块代码如下:

module gold_adder_8bit(
        input       [7:0]       a   ,
        input       [7:0]       b   ,
        input                   cin ,
        output                  cout,
        output      [7:0]       sum 
    );

    assign {cout, sum} = a + b + cin ;

endmodule

3. Carry-Select Adder

既然行波进位加法器延时大,那么可以通过下面方法进行电路结构上的优化。如下图所示,首先定义一个4bit的行波进位加法器,将这个4bit行波进位加法器封装起来,以这个4bit加法器去优化更高bit的加法器。

Carry-Select Adder优化电路如下图所示,以8bit加法为例,低位4bit按照封装的加法器运算,但是对高位4bit来说,将低位进位为0和1的情况都计算了一遍,因为c[4]不上为0就是为1,那么可以不管低位4bit有没有产生进位输出,将高位4bit在c[4]为0和1的情况都进行加法运算,然后依据c[4]的结果对高位4bit的sum和cout都进行选择。

这种方法可以看到3各4bit加法器都是并行运算的,可以同时进行运算,运算后通过c[4]结果选择高位4bit的sum和cout。就不用像行波进位加法器那样必须得等低bit的进位,高bit才能开始运算。

Carry-Select Adder 8bit加法器设计代码如下,其中Ripple_Adder_4bit为4bit输入的行波进位加法器。

module Carry_Select_Adder(
        input   [7:0]   a   ,
        input   [7:0]   b   ,
        input           cin ,
        output  [7:0]   sum ,
        output          cout
    );

    wire    c4 ;
    wire    [3:0]   sum7_4_0 ;
    wire            c8_0 ;
    wire    [3:0]   sum7_4_1 ;
    wire            c8_1 ;

    assign sum[7:4] = (c4==1) ? sum7_4_1 : sum7_4_0 ;
    assign cout = (c4==1) ? c8_1 : c8_0 ;

    Ripple_Adder_4bit u0_Ripple_Adder_4bit (
        .a          (a[3:0]      )  ,        
        .b          (b[3:0]      )  ,        
        .cin        (cin         )  ,        
        .sum        (sum[3:0]    )  ,        
        .cout       (c4          )             
    );

    Ripple_Adder_4bit u1_Ripple_Adder_4bit (
        .a          (a[7:4]      )  ,        
        .b          (b[7:4]      )  ,        
        .cin        (1'b0        )  ,        
        .sum        (sum7_4_0    )  ,        
        .cout       (c8_0        )             
    );

    Ripple_Adder_4bit u2_Ripple_Adder_4bit (
        .a          (a[7:4]      )  ,        
        .b          (b[7:4]      )  ,        
        .cin        (1'b1        )  ,        
        .sum        (sum7_4_1    )  ,        
        .cout       (c8_1        )             
    );
endmodule

Vivado综合出来的电路结果如下图所示。