/// LSU EE 3755 Fall 2012
//
//  Code for Homework 2

`timescale 1ns/10ps

/// Mixed Ripple / Carry Lookahead Adder
//
//
module adder_r4_c3(sum,a,b);
   input [11:0]  a, b;
   output [12:0] sum;

   wire [2:0]  P, G, carry;
   wire [2:0]  CO; // Unused.

   // Each 4-bit ripple adder computes a piece of the sum.
   //
   ripple_4_block ad0(sum[3:0],  CO[0], a[3:0],  b[3:0],  carry[0]);
   ripple_4_block ad1(sum[7:4],  CO[1], a[7:4],  b[7:4],  carry[1]);
   ripple_4_block ad2(sum[11:8], CO[2], a[11:8], b[11:8], carry[2]);

   // Use carry lookahead logic to compute carry in signals for ripple
   // adders.
   //

   gen_prop_4 gp0(G[0], P[0], a[3:0],  b[3:0]);
   gen_prop_4 gp1(G[1], P[1], a[7:4],  b[7:4]);
   gen_prop_4 gp2(G[2], P[2], a[11:8], b[11:8]);
   
   assign      carry[0] = 1'b0;

   assign      carry[1] = G[0];

   assign      carry[2] = G[0] & P[1]
                        | G[1];

   assign      sum[12] = G[0] & P[1] & P[2]
                       | G[1] & P[2]
                       | G[2];

endmodule


/// Two Level Carry Lookahead Adder
//
//
module adder_c4_c3(sum,a,b);
   input [11:0]  a, b;
   output [12:0] sum;

   wire [2:0]  P, G, carry;

   // Each 4-bit carry lookahead adder computes a piece of the sum and
   // also computes generate and propagate signals.
   //
   cla_4_block ad0(sum[3:0],  G[0], P[0], a[3:0],  b[3:0],  carry[0]);
   cla_4_block ad1(sum[7:4],  G[1], P[1], a[7:4],  b[7:4],  carry[1]);
   cla_4_block ad2(sum[11:8], G[2], P[2], a[11:8], b[11:8], carry[2]);

   // Use generate and propagate to compute carry in signals.
   //
   assign      carry[0] = 1'b0;

   assign      carry[1] = G[0];

   assign      carry[2] = G[0] & P[1]
                        | G[1];

   assign      sum[12] = G[0] & P[1] & P[2]
                       | G[1] & P[2]
                       | G[2];

endmodule

/// Four Bit CLA
//
module cla_4_block(sum,gout,pout,a,b,cin);
   input [3:0]  a, b;
   input        cin;
   output [3:0] sum;
   output       gout, pout;

   wire [3:0]   carry, p, g;

   assign carry[0] = cin;

   assign carry[1] = cin  & p[0]
                   | g[0];

   assign carry[2] = cin  & p[0] & p[1]
                   | g[0] & p[1]
                   | g[1];

   assign carry[3] = cin  & p[0] & p[1] & p[2]
                   | g[0] & p[1] & p[2]
                   | g[1] & p[2]
                   | g[2];

   assign gout = g[0] & p[1] & p[2] & p[3]
               | g[1] & p[2] & p[3]
               | g[2] & p[3]
               | g[3];

   assign pout = p[0] & p[1] & p[2] & p[3];

   cla_slice s0(sum[0],g[0],p[0],a[0],b[0],carry[0]);
   cla_slice s1(sum[1],g[1],p[1],a[1],b[1],carry[1]);
   cla_slice s2(sum[2],g[2],p[2],a[2],b[2],carry[2]);
   cla_slice s3(sum[3],g[3],p[3],a[3],b[3],carry[3]);

endmodule


/// Compute Generate and Propagate Signals
//
//  This module computes generate and propagate signals for a
//  group of four bits.
//
module gen_prop_4(G,P,a,b);
   output G;
   output P;
   input [3:0] a, b;

   wire [3:0]  g = a & b;
   wire [3:0]  p = a | b;

   assign G = g[0] & p[1] & p[2] & p[3]
            | g[1] & p[2] & p[3]
            | g[2] & p[3]
            | g[3];

   // assign P = p == 4'hf;   // Method E
   assign P = p[0] & p[1] & p[2] & p[3];  // Method G

endmodule

module ripple_4_block(sum,co,a,b,cin);
   output [3:0] sum;
   output       co;
   input [3:0] a, b;
   input       cin;

   wire [2:0]  carry;

   bfa bfa0(sum[0],carry[0],a[0],b[0],cin);
   bfa bfa1(sum[1],carry[1],a[1],b[1],carry[0]);
   bfa bfa2(sum[2],carry[2],a[2],b[2],carry[1]);
   bfa bfa3(sum[3],co,a[3],b[3],carry[2]);

endmodule

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

   assign sum = a ^ b ^ cin;
   assign cout = a & b | b & cin | a & cin;

endmodule

module cla_slice(sum,g,p,a,b,cin);
   input a, b, cin;
   output sum, g, p;

   assign g = a & b;
   assign p = a | b;
   assign sum = a ^ b ^ cin;
endmodule



// cadence translate_off
module test_adders();
   wire [12:0] sum_r, sum_2, sum_f;
   reg [12:0] shadow_sum;
   reg [11:0]  a, b;
   integer    i;

   adder_r4_c3 c1(sum_f,a,b);
   adder_c4_c3 c2(sum_2,a,b);
   assign     sum_r = sum_f;

   initial begin

      for(i=0; i<100; i=i+1) begin

         a = $random;
         b = $random;
         shadow_sum = a + b;
         #1;
         if( sum_2 != shadow_sum || sum_2 != sum_r || sum_2 != sum_f ) begin
            $display("Wrong sum: %h + %h = %h != %h or %h or %h\n",
                     a, b, shadow_sum, sum_2, sum_f, sum_r);
            $stop;
         end
      end

      $display("Tests completed.");

   end

endmodule
// cadence translate_on