/// Bean Counter Testbench Using Macro for Module Name

`ifdef BEANMOD
`else
 `define BEANMOD beancount
`endif

`ifdef clock_period
`else
 `define clock_period 7
`endif

`ifdef BEANTESTMOD
`else
 `define BEANTESTMOD beantest
`endif



module `BEANTESTMOD();

   integer beantime;         // Time for bean to move from pd1 to pd2.
   integer beans, beans1;    // Number of beans simulated by testbench.
   reg 	   pd1, pd2, clk;
   wire [15:0] cnt;
   integer     errors;
   integer     last_error;      // Difference between executed and actual.
   reg         reset;

   // Instantiate a bean counter.
   `BEANMOD bc(cnt,pd1,pd2,clk,reset);

   initial
     begin:INIT
        integer inter_clump;    // Time between beans.
        integer min_bean_time;  // Fastest bean speed.
        integer max_bean_time;  // Slowest bean speed.
        integer oldbeantime;

        // Initialize simulation variables.
	min_bean_time = 10 * `clock_period;  // Fastest bean speed.
	max_bean_time = 10 * min_bean_time;  // Slowest bean speed.
	beantime      = min_bean_time;
        oldbeantime   = 0;
        beans         = 0;                   // Testbench bean count.
        errors        = 0; 
        last_error    = 0;

        // Initialize simulated hardware.
	pd1 = 0;
	pd2 = 0;
	clk = 0;
        
        // Generate reset signal.
        reset = 0;
        #1;
        reset = 1;
        # (2 * `clock_period);
        reset = 0;
        # (2 * `clock_period);

        // Simulate at least 50 beans.
        while( beans < 50 )
          begin
             // End of previous clump reaches pd1.
             pd1 <= 0;

             // Choose amount of time between bean clumps.
             inter_clump = ($random>>1) % ( 5 * max_bean_time );
             # inter_clump;

             // Beginning of new clump reaches pd1;
             pd1 <= 1;

             // Choose bean speed, amount of time for one bean to pass
             // a point. (E.g., pd1.)
             beantime = min_bean_time +
                        ($random>>1) % ( max_bean_time - min_bean_time );

             // Prevent new clump from smashing into previous clump
             // between photodetectors.
             if( inter_clump + beantime < oldbeantime )
               beantime = oldbeantime + inter_clump ;
             oldbeantime = beantime;

             begin:CLUMP
                forever
                  begin
                     // Bean arrives at pd1. 
                     # beantime;
                     // Bean arrives at pd2.
                     beans = beans + 1;
                     if( $random & 'b1 ) disable CLUMP;
                  end
             end
             
          end // while ( beans < 50 )

        // Wait for last bean, plus time for circuit to count it.
        # ( 2 * beantime );

        if( errors === 0 )
          $display("Beans counted properly.\n");
        else
          $display("*** ERROR: bean count off by %d.\n",
                   beans - cnt);

        $stop;

     end // block: INIT


   // Clock for bean counter circuit.
   always #`clock_period clk = ~clk;

   // Make sure bean counter count agrees with testbench .

   always @ ( beans )  beans1 <= #(beantime * 0.75 ) beans;
   
   always @( beans1 )
     if( cnt - beans1 !== last_error )
       begin
          errors = errors + 1;
          last_error = cnt - beans1;
          if( errors <= 5 )
            $display("Wrong bean count, %d should be %d.\n", cnt,beans1);
          if( errors == 5 )
            $display("Ignoring future errors.\n");
          $stop;
       end

   // Set pd2 to lag pd1 by a beantime.
   always @( posedge pd1 ) pd2 <= #beantime  1;  
   always @( negedge pd1 ) pd2 <= #beantime  0;

   
endmodule // beantest