module: tdc_mphase_gro
parameters: double t_delta int num_elements double phase_noise double f_offset double one_over_f_corner double mismatch int clock_delays
inputs:  double_interp enable_edge double_interp disable_edge
outputs:  double out
static_variables: 
  double vco_phase_prev
  int phase_wraps
  double f_osc
  double noise_var
  int last_transition_element
  int last_transition_element_prev
  double disable_time
  int i;
  double temp_out;
  double gro_noise;
classes:  
  OneOverfPlusWhiteNoise noise_gro();
  Delay enable_delay(2.5);
  Delay disable_delay(2.5);
  And enable_and();
  And disable_and();
  Reg enable_reg()
  Vco vco("fc + Kv*x","fc,Kv,Ts",0,1,Ts)
  List mm_list()
  List phase_list()
  List out_fifo_list()
  Rand rand_mm("gauss")
  Rand rand_noise("gauss")
  EdgeDetect enable_pos_edge()
  EdgeDetect disable_pos_edge()
init:
  double list_input;
  double phase_acc;
  double mm_sum;
  phase_acc = 0;
  f_osc = 1/t_delta/num_elements/2;

  // Create a table for GRO phase to state mappping, including mismatch
  for (i = 0; i < num_elements; i++) {
    list_input = 1.0+mismatch/100.0*rand_mm.inp();
    if (list_input < 0) list_input = -1*list_input;
    mm_list.inp(list_input);
  }
  mm_sum = mm_list.mean()*num_elements;
  for (i = 0; i < num_elements-1; i++) {
    phase_list.inp(PI*mm_list.read()/mm_sum + phase_acc);
    phase_acc = PI*mm_list.read()/mm_sum + phase_acc;
  }
  phase_list.inp(PI);
  for (i = 0; i < num_elements-1; i++) {
    phase_list.inp(PI*mm_list.read()/mm_sum + phase_acc);
    phase_acc = PI*mm_list.read()/mm_sum + phase_acc;
  }
  
  // Initialize variables for code section
  noise_var = f_offset*f_offset/(f_osc*f_osc)*pow(10.0,phase_noise/10.0);
  noise_gro.set(one_over_f_corner,3.3,10,Ts);
  last_transition_element_prev = 0;
  phase_wraps = 0;
  vco_phase_prev = 0;
  out = 0;

  // Initialize output FIFO buffer 
  for (i = 1; i < clock_delays; i++) {
    out_fifo_list.inp(0.0);    
  }

  // Check to make sure that simulation time is small enough
  if (Ts > 0.5/f_osc) {
     printf("ERROR: Simulation time is set too high!\n");
     printf("tdc_mphase_gro requires that Ts < 0.5/Fosc.\n");
     printf("In this case, set Ts < %e\n",0.5/f_osc);
     exit(1);
  }

code:
  // Create the internal enable signal by pulsing a register's set and reset
  enable_reg.inp(1,1,enable_and.inp(enable_edge,-1*enable_delay.inp(enable_edge)),disable_and.inp(disable_edge,-1*disable_delay.inp(disable_edge)));

  // Gate the oscillator with the internal enable signal, including noise
  
  if (enable_reg.out > -1)
    vco.inp(f_osc*((1+enable_reg.out)/2+sqrt(noise_var/Ts)*noise_gro.inp()));

  // Accumulate the phase wraps
  if (vco.phase - vco_phase_prev < -3.14) phase_wraps = phase_wraps + 1;

  // Accumulator for measuring disable time 
  if (disable_pos_edge.inp(disable_edge)) disable_time = 0;
  disable_time = disable_time + Ts;

  // Calculate output on rising enable edge
  if (enable_pos_edge.inp(enable_edge)) {
    phase_list.reset();
    last_transition_element = 0;
    for (i = 1; i < 2*num_elements; i++) {
      if (vco.phase > phase_list.read()) last_transition_element = i;
    }
    temp_out = 2*num_elements*phase_wraps + last_transition_element - last_transition_element_prev;

    // Buffer and retrieve TDC output with FIFO list
    out_fifo_list.inp(temp_out);
    out_fifo_list.reset();
    out = out_fifo_list.read();
    out_fifo_list.remove_first_entry();

    phase_wraps = 0;
    last_transition_element_prev = last_transition_element;
  }

  vco_phase_prev = vco.phase;

// End of tdc_mphase_gro