// Binary Full Adders, With Timing

// Binary Full Adder Explicit Structural Description

`timescale 1ns/1ns

`define not_time 2
`define or_time 3
`define and_time_1 3
`define and_time_0 1
`define bfa_time (`and_time_1 + `or_time + `not_time )

`define bfa_explicit
//`define bfa_behavioral

module my_and2(x,a,b);
   output x;
   input  a,b;

   and #(`and_time_1,`and_time_0) (x,a,b);
   
endmodule // my_and3

   
module my_and3(x,a,b,c);
   output x;
   input  a,b,c;

   and #(`and_time_1,`and_time_0) (x,a,b,c);
   
endmodule // my_and3

`ifdef bfa_explicit   

module bfat(sum,cout,a,b,c);
   input a,b,c;
   output sum,cout;

   or #(`or_time) or1(sum,term001,term010,term100,term111);
   or #(`or_time) or2(cout,ab,bc,ac);

   my_and3 and1(term001,na,nb,c);
   my_and3 and2(term010,na,b,nc);
   my_and3 and4(term100,a,nb,nc);
   my_and3 and7(term111,a,b,c);

   not #(`not_time) (na,a); // Anonymous instantiation of inverters.
   not #(`not_time) (nb,b); // (Can only instantiate primitives anonymously.)
   not #(`not_time) (nc,c);

   my_and2 one(ab,a,b);
   my_and2 two(bc,b,c);
   my_and2 three(ac,a,c);

endmodule // bfat

`endif // ifdef bfa_explicit

`ifdef bfa_implicit

// Binary Full Adder Implicit Structural Description

module bfat(sum,cout,a,b,c);
   input a,b,c;
   output sum,cout;
   wire sum;
   wire #(`bfa_time) sumi;
   wire cout;

   assign sum = sumi;

   assign sumi = 
          ~a & ~b &  c |
          ~a &  b & ~c |
           a & ~b & ~c |
           a &  b &  c;

   assign #(`bfa_time) cout = a & b | b & c | a & c;

endmodule // bfat

`endif // ifdef bfa_implicit


// Binary Full Adder Behavioral Description

`ifdef bfa_behavioral

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

   integer wholesum;

   specify
      (a,b,cin *> sum,cout) = (`bfa_time);
   endspecify
   

   always @( a or b or cin )
     begin
        wholesum = a + b + cin;
        sum = wholesum[0];
        cout = wholesum[1];
     end

endmodule // bfat

`endif // ifdef behavioral 


module radder8(sum,cout,a,b,cin);
   input a, b, cin;
   output sum, cout;
   wire [7:0] a, b, sum;
   wire [6:0] c;
   parameter  delay = `bfa_time * 8;

   // Delay is maximum of path delay below and delay in bfat's.
   // This means delay parameter can be set to a value lower than
   //  the actual delay, leading to incorrect operation.
   specify
      (a,b,cin *> sum,cout) = delay;
   endspecify

   bfat bfa7(sum[7],cout,a[7],b[7],c[6]);
   bfat bfa6(sum[6],c[6],a[6],b[6],c[5]);
   bfat bfa5(sum[5],c[5],a[5],b[5],c[4]);
   bfat bfa4(sum[4],c[4],a[4],b[4],c[3]);
   bfat bfa3(sum[3],c[3],a[3],b[3],c[2]);
   bfat bfa2(sum[2],c[2],a[2],b[2],c[1]);
   bfat bfa1(sum[1],c[1],a[1],b[1],c[0]);
   bfat bfa0(sum[0],c[0],a[0],b[0],cin);
   
endmodule // radder8 


// Testbenches

module testbfat();

   integer realsum;
   integer i;
   wire    bfsum,bfcarry;
   wire    a,b,c;

   assign  {c,b,a} = i[2:0];

   bfat bfa3(bfsum,bfcarry,a,b,c);

   initial
     begin
        for(i=0; i<8; i=i+1)
        begin
           realsum = i[0] + i[1] + i[2];
           // Won't work without a delay: realsum = a + b + c 
           # ( `bfa_time );
           if( realsum !== {bfcarry,bfsum} )
             $display("Error in %d + %d + %d = %d %d\n",
                      a,b,c,bfcarry,bfsum);
           # 4;  // Delay so waveform easier to read.
        end
        $display("Finished testing.\n");
     end

endmodule

module testra();

   integer i,j, realsum;
   integer errors;
   
   wire [7:0] sum;
   wire       cout;
   
   radder8  myadder(sum,cout,i[7:0],j[7:0],1'b0);

   initial
     begin
        errors = 0;
        for(i=0; i<256; i=i+1)
        for(j=0; j<256; j=j+1)
        begin
           realsum = i + j;
           // Determine delay by looking at adder's delay parameter.
           # myadder.delay;
           if( realsum !== {cout,sum} )
             begin
                $display("Wrong sum:%d + %d = %d.",i,j,{cout,sum});
                errors = errors + 1;
                if( errors > 4 ) $stop;
             end
        end 
     end 

endmodule // testra