`resetall
`timescale 1 ns / 1 ps

`celldefine

module HPPLL (CLKIN, FB, MCLK, NCLK, LOCK, INTFB);

input CLKIN, FB;

output MCLK, NCLK, LOCK, INTFB;

parameter DIV0=1, DIV1=1, DIV2=1, DIV3=1, DISABLED_GSR=1,
          MCLKMODE="BYPASS", NCLKMODE="BYPASS", VCOTAP=4;

wire CLKINB, FBB, MCLK, NCLK, LOCK, INTFB;

reg MCLKREG, NCLKREG, LOCKREG, INTFBREG;

reg first_time, first_time2, first_time3, first_time4; 
reg first_time5, first_time6, first_time7, first_time8;
reg new_int_first_time, fb_first_time;

reg false_int, false_mck, false_nck, new_int;

reg clock_valid, clock_invalid, set_reset;

reg mn_duty_clk, duty_mnclk, pshift_mclk, duty_lock;

reg vir_mclk, vir_nclk, vir_lock, vir_intfb;
reg final_mclk, final_nclk, final_lock, final_intfb;

reg clkfb_per_valid, intfb_out, intfb_du_out, lock_all; 

realtime next_clock_edge, last_clock_edge;
realtime next_clock_edge1, last_clock_edge1;
realtime t_in_clk, t_in_clk1, t_in_clk2, t_in_clk3;
realtime t_vco, ha_vco;
realtime t_fb_clk, t_intfb_clock, ha_intfb_clk;
realtime false_t_int, false_t1_int, mclock_delay, mnclock_duty;
realtime first_int_edge, first_fb_edge;
realtime total_fb_delay, virtual_delay1;
realtime real_delay, real_delay1, real_delay2;

real ext_divider, net_divide;

integer index_mclk, index_nclk, index1, index2, ratio1;


 `ifdef GSR_SIGNAL
  wire GSR = `GSR_SIGNAL;
 `else
  pullup (weak1) (GSR);
  `endif

 `ifdef PUR_SIGNAL
  wire PUR = `PUR_SIGNAL;
 `else
  pullup (weak1) (PUR);
 `endif

  always @ (GSR or PUR ) begin
    if (DISABLED_GSR == 0) begin
      set_reset = GSR & PUR ;
    end
    else if (DISABLED_GSR == 1)
      set_reset = PUR;
  end

buf U1 (MCLK, MCLKREG);
buf U2 (NCLK, NCLKREG);
buf U3 (LOCK, LOCKREG);
buf U4 ( INTFB, INTFBREG);
buf U5 (CLKINB, CLKIN);
buf U6 (FBB, FB);

// INITIALIZE all the internal signals to 0 at the start
// of the simulation cycle.

initial begin
lock_all = 0;
vir_lock = 0;
clock_valid = 0;
clock_invalid = 1;
final_lock = 0;
final_mclk = 0;
final_nclk = 0;
vir_mclk = 0;
vir_nclk = 0;
vir_lock = 0;
vir_intfb = 0;
clkfb_per_valid = 0;
duty_lock = 0;
mn_duty_clk = 0;
duty_mnclk = 0;
intfb_out = 0;
intfb_du_out = 0;
false_mck = 0;
false_nck = 0;
false_int = 0;
MCLKREG = 0;
NCLKREG = 0;
INTFBREG = 0;
LOCKREG = 0;
end

// Generate False Clocks

  initial
   begin
    first_time = 1'b0;
    #1;
    first_time = 1'b1;
    false_int = 1'b0;
   end

  always @(first_time) 
   begin
   if (first_time == 1'b1)
     forever begin
     #5 false_int = ~false_int;
   end
   else
     forever begin
     #5 false_int = ~false_int;
     end 
   end	

// Generate false mclk and false nclk

  initial 
   begin
    first_time2 = 1'b0;
    #1;
    first_time2 = 1'b1;
    index_mclk = 1'b0;
   end

  always @(new_int) 
   begin
    if (first_time2 == 1 && new_int == 1)
      begin
      false_mck = 1;
      first_time2 = 0;
      index_mclk =0;
      end 
    else
    index_mclk = index_mclk + 1;
    if (index_mclk == DIV2)
      begin
      false_mck = ~false_mck;
      index_mclk = 0;
      end
   end


// P3

  initial 
   begin
    first_time3 = 1'b0;
    #1;
    first_time3 = 1'b1;
    index_nclk = 1'b0;
   end

  always @(new_int) 
   begin
    if (first_time3 == 1 && new_int == 1)
      begin
      false_nck = 1;
      first_time3 = 0;
      index_nclk=0;
      end
    else
    index_nclk = index_nclk + 1;
    if (index_nclk == DIV3)
      begin
      false_nck = ~false_nck;
      index_nclk = 0;
      end
   end

// P4 

  always @(set_reset or false_int) 
   begin
    if (set_reset == 0)
      begin
      new_int=0;
      end
    else if (first_time == 1 && false_int == 1)
      begin
      new_int = 1;
      first_time = 0;
      end
    else 
    new_int = false_int;
   end
  

// Measure input clock period

  initial begin
   last_clock_edge = 0.0;
   next_clock_edge = 0.0;
   t_in_clk = 0.0;
   t_in_clk1 = 0.0;
   t_in_clk2 = 0.0;
   t_in_clk3 = 0.0;

  end

  always @(posedge CLKINB)
   begin
   last_clock_edge=next_clock_edge;
   next_clock_edge=$realtime;

    if (last_clock_edge > 0) 
	begin
        t_in_clk <= next_clock_edge - last_clock_edge;
        t_in_clk1 <= t_in_clk;
        t_in_clk2 <= t_in_clk1;
        t_in_clk3 <= t_in_clk2;
	end


    if (t_in_clk > 0) 
        begin
	 if (t_in_clk == t_in_clk3)
	 clock_valid <= 1;
//         else
//         clock_valid <= 0;
        end

    if (t_in_clk > 0) 
        begin
	 if (t_in_clk == t_in_clk1)
         clock_invalid <= 0;
//         else
//         clock_invalid <= 1;
        end


    if (t_in_clk > 0) 
        begin
        t_vco = ((t_in_clk * DIV0) / DIV1);
        ha_vco = (t_vco / 2 );
        real_delay2 = ((t_in_clk * VCOTAP) / 8.0);
        real_delay = real_delay1 + real_delay2;
        end                
   end


// Measure Feedback Clock Period

  initial begin
   last_clock_edge1 = 0.0;
   next_clock_edge1 = 0.0;
   false_t1_int = 5.0;
   first_time5 = 1'b1;
   //#1;
   //first_time5 = 1'b1;
  end

  always @(posedge FBB)
   begin
    last_clock_edge1=next_clock_edge1;
    next_clock_edge1=$realtime;

     if (last_clock_edge1 > 0) 
      begin
       if (first_time5 == 1)
       t_fb_clk = next_clock_edge1 - last_clock_edge1;
       first_time5 = 0;
      end

     if (last_clock_edge1 > 0) 
      begin
       clkfb_per_valid=1;
      end

   end


// Measure the feedback delay value

  initial
   begin
   first_int_edge = 0.0;
   first_fb_edge = 0.0;
   virtual_delay1 = 0.0;
    new_int_first_time = 1'b0;
    fb_first_time = 1'b0;
    #1;
    new_int_first_time = 1'b1;
    fb_first_time = 1'b1;
   end

always @(posedge new_int)
  begin
  if (new_int_first_time == 1)
  first_int_edge=$realtime;
  new_int_first_time = 1'b0; 
  end

always @(posedge FBB)
  begin
  if (fb_first_time == 1)
  first_fb_edge=$realtime;
  fb_first_time = 1'b0;
  end

always @(FBB)
  begin
  total_fb_delay = first_fb_edge - first_int_edge;
//  end

if ( (total_fb_delay > 0) && (t_in_clk > 0) )
   begin
   ratio1 = total_fb_delay / t_in_clk;
   virtual_delay1 = total_fb_delay - (t_in_clk * ratio1);
   real_delay1 = t_in_clk - virtual_delay1 ; 
   end
end


// Assume Internal dedicated feedback
// Generate internal-feedback clock period

  always @(INTFB)
   begin
    false_t_int = (2 * false_t1_int);

    if (false_t_int > 0)
     begin
     ext_divider = t_fb_clk/false_t_int;
     net_divide = (DIV1 * ext_divider);
     end

    if (net_divide >0)
     begin
     t_intfb_clock = ((t_in_clk * DIV0) / (net_divide));
     ha_intfb_clk = t_intfb_clock / 2.0;
     end
   end

// Generate all CLOCKs in DELAY MODE
// P8

  initial begin
   first_time4 = 1'b0;
   #1;
   first_time4 = 1'b1;
  end

  always @(first_time4) 
   begin
    if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY")
      begin
       if (first_time4 == 1)
        begin
        wait (clock_valid == 1) intfb_out <= 1;
	first_time4 <= 0;
	end
       else 
       if (clock_valid == 1)
         forever begin
         #ha_intfb_clk intfb_out = ~intfb_out;
         end
      end
   end
	

// P11

  always begin
   @(intfb_out)
   begin
   if (set_reset == 1)
     begin
     vir_intfb <= #real_delay intfb_out;
     end
   else
     begin
     vir_intfb <= 0;
     end
   end
  end

// P9
  initial begin
   first_time6 = 1'b1;
   index1 = 1'b0;
   index2 = 1'b0;
  end

  always @(vir_intfb) 
   begin
    if (first_time6 == 1)
     begin
       if (vir_intfb == 1)
  	begin
  	vir_mclk <= 1;
  	vir_nclk <= 1;
  	vir_lock <= 1;
  	first_time6 = 0;
  	index1 = 0;
  	index2 = 0;  
  	end //vir_intfb
     end
    else
     begin
     index1 = index1+1;
     index2 = index2+1;
       if (index1 == DIV2)
        begin
        vir_mclk <= ~vir_mclk;
        index1 = 0;
        end

     if (index2 == DIV3)
       begin
       vir_nclk <= ~vir_nclk;
       index2 = 0;
       end

     end
   end


// Generate all CLOCKs in DUTYCYCLE and PHSHIFT MODE
// P12

  initial begin
   first_time7 = 1'b0;
   #1;
   first_time7 = 1'b1;
  end

  always @(first_time7) 
   begin
   if (first_time7 == 1'b1)
    begin
     wait (clock_valid == 1) intfb_du_out <= 1;
     mn_duty_clk <= 1;
     first_time7 <= 0;
    end
   else
    if (clock_valid == 1) 
     forever begin
     #ha_vco intfb_du_out <= ~intfb_du_out;
     mn_duty_clk <= ~mn_duty_clk;
     end 
   end


// Generate LOCK signal

  always @(set_reset or vir_lock or clock_invalid or clock_valid) 
   begin
    if (set_reset == 0) 
     lock_all <= 0;
    else
     if (clock_invalid == 1)
      lock_all <= 0;
     else
      if (clock_valid == 1)
      lock_all <= vir_lock;
   end
     
// Calculate DUTYCYCLE values and create clocks

  always @(posedge mn_duty_clk) 
   begin
    if (VCOTAP > 0) 
     begin
     mnclock_duty = ((t_vco * VCOTAP) / 8);
     end

    if (mnclock_duty > 0) 
     begin
     duty_lock <= 1;
     duty_mnclk <= 1;
     duty_mnclk <= #mnclock_duty 0;
     end
   end


// Calculate DELAY values and create clocks
//Phaseshift

  always @(mn_duty_clk)
   begin
    mclock_delay = ((t_vco * VCOTAP) / 8);
    pshift_mclk <= #mclock_delay mn_duty_clk;
   end


// Select clocks with appropriate modes

//PHSHIFT Mode

  always @(pshift_mclk) 
   begin
   if (MCLKMODE == "PHSHIFT" && NCLKMODE == "PHSHIFT") 
    begin
    final_mclk <= pshift_mclk;
    end
   end

  always @(mn_duty_clk) 
   begin
   if (MCLKMODE == "PHSHIFT" && NCLKMODE == "PHSHIFT") 
    begin
    final_nclk <= mn_duty_clk;
    end
   end

  always @(duty_lock)
   begin
   if (MCLKMODE == "PHSHIFT" && NCLKMODE == "PHSHIFT")
    begin
    final_lock <= duty_lock;
    end
   end

  always @(intfb_du_out) 
   begin
   if (MCLKMODE == "PHSHIFT" && NCLKMODE == "PHSHIFT") 
    begin
    final_intfb <= intfb_du_out;
    end
   end

//BYPASS Mode

  always @(CLKINB) 
   begin
   if (MCLKMODE == "BYPASS" && NCLKMODE == "BYPASS") 
    begin
    //final_mclk <= CLKIN;
    //final_nclk <= CLKIN;
    MCLKREG <= CLKINB;
    NCLKREG <= CLKINB;
    INTFBREG <= CLKINB;
    LOCKREG <= 1;
    end
   end

// DELAY Mode 

  always @(vir_mclk) 
   begin
   if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY")
    final_mclk <= vir_mclk;
   end

  always @(vir_nclk) 
   begin
   if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY") 
    final_nclk <= vir_nclk;
   end

  always @(vir_intfb) 
   begin
   if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY") 
    final_intfb <= vir_intfb;
   end

  always @(lock_all) 
   begin
   if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY")
    final_lock = lock_all;
   end


// Duty Mode

  always @(duty_mnclk) 
   begin
    if (MCLKMODE == "DUTYCYCLE" && NCLKMODE == "DUTYCYCLE")
    begin
    final_mclk <= duty_mnclk;
    final_nclk <= duty_mnclk;
    end
   end

  always @(duty_lock)
   begin
    if (MCLKMODE == "DUTYCYCLE" && NCLKMODE == "DUTYCYCLE")
    final_lock <= duty_lock;
   end

  always @(intfb_du_out) 
   begin
    if (MCLKMODE == "DUTYCYCLE" && NCLKMODE == "DUTYCYCLE")
    final_intfb <= intfb_du_out;
   end 

  always @(duty_mnclk) 
   begin
    if (MCLKMODE == "DUTYCYCLE" && NCLKMODE == "DUTYCYCLE")
    final_mclk <= duty_mnclk;
   end


// Functionality Section

  initial begin
   first_time8=1'b0;
   #1;
   first_time8=1'b1;
  end 

  always @(first_time8 or set_reset) 
   begin
   if (first_time8 == 1'b1)
    begin
     if (set_reset == 0)
      begin
      MCLKREG = 0;
      NCLKREG = 0;
      LOCKREG = 0;
      INTFBREG = 0;
      end
    end
   end

  always begin
   @(final_mclk)
    if (set_reset == 1 && clock_valid == 1)
    MCLKREG <= final_mclk;
  end

  always begin
   @(final_nclk)
    if (set_reset == 1 && clock_valid == 1)
    NCLKREG <= final_nclk;
  end

  always begin
   @(final_intfb)
    if (set_reset == 1 && clock_valid == 1)
    INTFBREG <= final_intfb;
  end


  always begin
   @(false_mck or final_mclk)
    if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY") 
     begin
      if (set_reset == 1 && clock_valid == 0) 
      MCLKREG <= false_mck;
      else if (set_reset == 1 && clock_valid == 1)
      MCLKREG <= final_mclk;
     end
  end

  always begin
   @(false_nck or final_nclk)
    if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY" ) 
     begin
      if (set_reset == 1 && clock_valid == 0) 
      NCLKREG <= false_nck;
      else if (set_reset == 1 && clock_valid == 1)
      NCLKREG <= final_nclk;
    end
  end

  always begin
   @(new_int)
    if (set_reset == 1 && clock_valid == 0) 
     begin
      if (MCLKMODE == "DELAY" && NCLKMODE == "DELAY" )
      INTFBREG <= new_int;
     end
  end

  always begin
   @(final_intfb)
    if (set_reset == 1 && clock_valid == 1) 
     begin
     INTFBREG <= final_intfb;
     end
  end


  always begin
   @(final_lock or clock_valid)
    if (clock_valid == 1)
    begin
     if (MCLKMODE != "BYPASS" && NCLKMODE != "BYPASS")
    LOCKREG <= final_lock;
    end
  end

   specify

      specparam MDLYLH   = 0:0:0,  MDLYHL   = 0:0:0 ;
      specparam NDLYLH   = 0:0:0,  NDLYHL   = 0:0:0 ;
      specparam LKDLYLH  = 0:0:0,  LKDLYHL  = 0:0:0 ;
      specparam INTFBDLYLH = 0:0:0, INTFBDLYHL = 0:0:0;


      (CLKIN => MCLK) = (MDLYLH, MDLYHL);
      (CLKIN => NCLK) = (NDLYLH, NDLYHL);
      (CLKIN => LOCK) = (LKDLYLH, LKDLYHL);
      (CLKIN => INTFB) = (INTFBDLYLH, INTFBDLYHL);

   endspecify

endmodule

`endcelldefine

