/// EE 4755 - Digital Design Using HDLs
//

 /// Classroom Code Examples
 //
 //  Covered 21, 23 September 2015

 //  Simple Sequential Behavioral Code
 //  Counters.
 //  BCD Counter

// Time-stamp: <25 September 2015, 15:33:30 CDT, koppel@sky.ece.lsu.edu>


//////////////////////////////////////////////////////////////////////////////


module register
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input wire [width-1:0] data,
     input wire clk );

   always_ff @( posedge clk ) val = data;

endmodule

module register_en
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input wire enable,
     input wire [width-1:0] data,
     input wire clk );

   always_ff @( posedge clk iff enable ) val = data;

endmodule

module register_sync_reset
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input wire reset,
     input wire [width-1:0] data,
     input wire clk );

   always_ff @( posedge clk ) if ( reset ) val = 0; else val = data;

endmodule

module register_async_reset
  #( int width = 16 )
   ( output logic [width-1:0] val,
     input wire reset,
     input wire [width-1:0] data,
     input wire clk );

   always_ff @( posedge clk or posedge reset )
     if ( reset ) val = 0; else val = data;

endmodule




module count
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     input wire clk );

   initial c = 0;

   always @( posedge clk ) c++;

endmodule

module count_reset
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     input wire reset,
     input wire clk );

   always_ff @( posedge clk ) if ( reset ) c = 0; else c++;

endmodule


module count_thd
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     output logic over_th,
     input wire [bits-1:0] threshold,
     input wire clk );

   always_ff @( posedge clk )
     begin
        c++;
        over_th = c > threshold;
     end

   // No delay constraints.
//  over_th_reg/CLK   setup                    0  +265    4521 R
//  count_thd    126      16272         0       16272    <none> (D)
   // 100 ps delay target
//   over_th_reg/CLK   setup                    0  +266    2107 R
//   count_thd    235      28356         0       28356    <none> (D)

endmodule

module count_thd_alt2
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     output logic over_th,
     input wire [bits-1:0] threshold,
     input wire clk );

   always_ff @( posedge clk )
     begin
        over_th = c > threshold;
        c++;
     end

      // No delay constraints.
// over_th_reg/CLK   setup                    0  +277    4272 R
//   count_thd_alt2     92      15928         0       15928    <none> (D)
      // 100 ps delay target
//   c_reg[12]/CLK   setup                    0  +276    1355 R
//   count_thd_alt2    191      24492         0       24492    <none> (D)



endmodule

module count_thd_alt
  #( int bits = 16 )
   ( output logic [bits-1:0] c,
     output logic over_th,
     input wire [bits-1:0] threshold,
     input wire clk );

   always_ff @( posedge clk ) c++;

   always_comb over_th = c > threshold;

      // No delay constraints.
//   over_th        out port                      +0    3973 F
//   count_thd_alt     91      15544         0       15544    <none> (D)
   // 100 ps delay target
//   c_reg[15]/CLK   setup                    0  +276    1340 R
//   count_thd_alt    189      23160         0       23160    <none> (D)



endmodule



module count_limit
  #( int max_val = 9,
     int bits = $clog2(max_val) )
   ( output logic [bits-1:0] c,
     input wire reset,
     input wire clk );

   always_ff @( posedge clk ) begin

      if ( reset || c == max_val ) c = 0; else c++;

   end

endmodule

`ifdef classroom_live
module bcd_count
  #( int num_digits = 4 )
   ( output wire [num_digits-1:0][3:0] count,
     input wire clk );

   count_limit_en #(9) cl0( count[0], 1'b1, clk );
   assign  en1 = count[0] == 9;
   count_limit_en #(9) cl1( count[1], en1,  clk );
   assign  en2 = count[1] == 9 && en1;
   count_limit_en #(9) cl2( count[2], en2,  clk );

endmodule
`endif



module count_limit_en
  #( int max_val = 9,
     int bits = $clog2(max_val) )
   ( output logic [bits-1:0] c,
     input wire reset,
     input wire enable,
     input wire clk );

   always_ff @( posedge clk )
     if ( reset ) c = 0;
     else if ( enable ) begin
        if ( c == max_val ) c = 0; else c++;
     end

endmodule


module bcd_count
  #( int num_digits = 4 )
   ( output wire [num_digits-1:0][3:0] count,
     input wire reset,
     input wire clk );

   wire    nines[num_digits:-1];

   assign  nines[-1] = 1;

   for ( genvar d=0; d<num_digits; d++ )
     begin
        count_limit_en #(9) cl0( count[d], reset, nines[d-1], clk );
        assign nines[d] = nines[d-1] && count[d] == 9;
     end
endmodule



// cadence translate_off

module testbench;

   localparam int width = 4;
   localparam int num_cyc = 115;

   logic      clk;
   logic      reset;
   bit        done;
   int cycle;

   localparam int max_muts = 4;

   wire [width-1:0] cnt[max_muts];
   localparam       int num_digits = 3;
   wire [num_digits-1:0] [3:0] bcd;

   count #(width) cm1(cnt[0], clk);
   count_reset #(width) cm2(cnt[1], reset, clk);
   count_limit #(9) cm3(cnt[2], reset, clk);
   bcd_count #(num_digits) cm4(bcd,reset,clk);

   initial begin
      clk = 0;
      cycle = 0;
      done = 0;
      repeat ( 2 * num_cyc ) #10 cycle += clk++;
      done = 1;
   end

   initial begin

      reset = 1;
      @( posedge clk ); @( negedge clk );
      reset = 0;

      do @ ( negedge clk ) begin

         $write("Cyc %4d  cnt %2d  %2d  %2d  %3x\n",
                cycle, cnt[0], cnt[1], cnt[2], bcd);

      end while ( !done );

   end

endmodule

// cadence translate_on