/*********************************************************
   MODULE:              tbclk

   FILE NAME:           tbclk.v
   $Revision: 1.1 $
   $Date: Wed Nov 13 17:09:12 2002 $
   $Source: /home/synclpa/tools/syncinc/syncdata/neptune/9003/server_vault/Projects/orspii/design/toptest/local_sysb_files/testbench/tbclk.v.rca $
   $Locker: $
   AUTHOR:              Joe Schulingkamp

   CODE TYPE:           Behavioral

   DESCRIPTION:         Clock Generation module for testbenches
			(parameterized).  Example calls:

	** Use modify_clock task to change clock parameters
	** Use show_clock task to display clock parameters
	** Use the methods below for INSTANTIATED DEFAULTS


	50 MHz clock, default rising and falling edge (rising at zero, 50% duty cycle):

		tbclk #(20.0) clk_inst1 (My_CE, Clock1);


	50 MHz clock, rising at 3 ns, default falling edge (50% duty cycle):

		tbclk #(20.0,3.0) clk_inst2 (My_CE, Clock2);


	Period, rising and falling edge defined by macros:

		tbclk #(`CLK3_PER,`CLK3_RE,`CLK3_FE) clk_inst3 (My_CE, Clock3);


	50 MHz clock, rising at 10 ns, falling at 0 ns (inverted clock):

		tbclk #(20.0,10.0,0.0) clk_inst4 (My_CE, Clock4);


	All defaults (1 MHz, rising at 0 ns, falling at 500 ns):

		tbclk clk_inst5 (My_CE, Clock5);

$KeysEnd$
*********************************************************/

module tbclk (Clock_Enable,System_Clock);

    parameter p_clk_per = 1000.0;			// 1 MHz default
    parameter p_redge   =  0.0;				// Start immediately
    parameter p_fedge   = p_redge + (p_clk_per/2.0);	// 50% duty cycle default

    input Clock_Enable;
    output System_Clock;

    reg iSystem_Clock;
    reg clock_disable_state;

    real clock_period;
    real rising_edge;
    real falling_edge;

    integer pause_clock;

    initial begin

	clock_period = p_clk_per;
	rising_edge  = p_redge;
	falling_edge = p_fedge;

	pause_clock  = 1'b0;		// used by modify_clock task only

	clock_disable_state = 1'b1;	// initial state before clock ever runs

	if (rising_edge > falling_edge)		// Initial clock states
		iSystem_Clock = ~clock_disable_state;
	else
		iSystem_Clock = clock_disable_state;
     end


   assign System_Clock = iSystem_Clock;


    // once the clock is enabled, change the disable state
    always @(posedge Clock_Enable) clock_disable_state = 1'b0;

    always #(clock_period) begin : CLKGEN
	if (Clock_Enable && !pause_clock) begin
	    iSystem_Clock <= #(falling_edge) 1'b0;
	    iSystem_Clock  <= #(rising_edge) 1'b1;
	end
	else begin
	    if (rising_edge > falling_edge)
		iSystem_Clock <= ~clock_disable_state;
	    else
		iSystem_Clock <= clock_disable_state;
	end
    end    

task modify_clock;
input new_per;
input new_redge;
input new_fedge;

real new_per;
real new_redge;
real new_fedge;

begin

    pause_clock = 1'b1;		// pause the clock
    #(clock_period);		// wait at the old period for the pause to work

    if (new_per > 0) begin	// quietly stay with old period if <= 0
	clock_period = new_per;
    end

    if (new_redge >= 0) begin	// quietly stay with old rising edge if negative
	rising_edge  = new_redge;
    end

    if (new_fedge >= 0) begin	// quietly stay with old rising edge if negative
	falling_edge = new_fedge;
    end

    if (rising_edge > clock_period)
	$display("\n%m:  WARNING - Rising Edge (%4.2f) > Clock Period (%4.2f)!\n",rising_edge,clock_period);

    if (falling_edge > clock_period)
	$display("\n%m:  WARNING - Falling Edge (%4.2f) > Clock Period (%4.2f)!\n",falling_edge,clock_period);

    pause_clock = 1'b0;		// restart the clock

end
endtask


task show_clock;

real clk_freq, clk_duty;

begin

    clk_freq = 1000.0/clock_period;
    clk_duty = ((falling_edge - rising_edge) * 100.0)/clock_period;

    $display("\n\n\tCLOCK INFORMATION FOR %m:");
    $display("\t\tClock Frequency = %4.2f MHz",clk_freq);
    $display("\t\tClock Duty Cycle = %4.2f%% (high)",clk_duty);
    $display("\t\tClock Rising/Falling Edges = %4.2f/%4.2f\n",rising_edge,falling_edge);

end
endtask

endmodule
