module: butterfly_dac
inputs: double in double_interp clk
outputs: double out
parameters:
  int n_elements
  int mm_seed
  double per_mm
classes:
  Vector dac_vec();
  IntVector data_vec();
  IntMatrix data_state();
  IntMatrix swap_state();
  IntMatrix connection_matrix();
  Rand dac_rand_mm("gauss");
  EdgeDetect clkedge();
static_variables:
  int n_stages
  int n_units
  int n_half
  int n_quarter
  int p
  int q
  int r
  int u
  int v
  int count
  int xor_eval1
  int xor_eval2
  int in0
  int in1
  int dac_index
init:
  n_stages = (int) ceil(log10(n_elements)/log10(2)); 
  n_units = (int) ceil(pow(2,n_stages));
  n_half = n_units>>1;  
  n_quarter = n_units>>2;
  printf("n_stages = %d\n",n_stages);
  printf("n_units = %d\n",n_units);
  printf("n_half = %d\n",n_half);
  printf("n_quarter = %d\n",n_quarter);
  dac_vec.set_length(n_units);
  data_vec.set_length(n_units);
  data_state.set_size(n_units,n_stages);
  swap_state.set_size(n_half,n_stages);
  connection_matrix.set_size(n_units,n_stages);
  if (mm_seed > 0)
  {
     dac_rand_mm.set_seed(mm_seed);
  }
  int m;
  for (m=0;m<n_units;m++)
  {
     dac_vec.set_elem(m,1+(per_mm/100)*dac_rand_mm.inp());
  }
  int a;
  int b;
  int index_1;
  int index_2;
  int index_3;
  int index_4;
  int shift_factor;
  int mod_factor;
  int bit_mask;
  for (b=0;b<n_stages;b++)
  {
     shift_factor = 1<<b;
     mod_factor = 1<<(b+1);
     bit_mask = ~(mod_factor-1);
     for (a=0;a<n_quarter;a++)
     {
	index_1 = (a<<2);
	index_4 = (a<<2)+3;
	if (b==0) 
	{
	   index_2 = (a<<2)+1;
	   index_3 = (a<<2)+2;
	}
	else if (b==1)
	{
	   index_2 = (a<<2)+2;
	   index_3 = (a<<2)+1;
	}
	else 
	{
	   index_2 = ((a<<2)+1+shift_factor)%mod_factor+(((a<<2)+1)&bit_mask); 
	   index_3 = ((a<<2)+2+shift_factor)%mod_factor+(((a<<2)+2)&bit_mask);
	}
	connection_matrix.set_elem((a<<2),b,index_1);
	connection_matrix.set_elem((a<<2)+1,b,index_2);
	connection_matrix.set_elem((a<<2)+2,b,index_3);
	connection_matrix.set_elem((a<<2)+3,b,index_4);
	printf("Connection Matrix(%d,%d) = %d\n",(a<<2),b,index_1);
	printf("Connection Matrix(%d,%d) = %d\n",(a<<2)+1,b,index_2);
	printf("Connection Matrix(%d,%d) = %d\n",(a<<2)+2,b,index_3);
	printf("Connection Matrix(%d,%d) = %d\n",(a<<2)+3,b,index_4);
     }
  }
code:
  if (clkedge.inp(clk))
  {
     out = 0;
     count = (int) floor(0.5+in);
     for (r=0;r<n_units;r++)
     {
        if (count > 0)
           data_state.set_elem(r,0,1);
        else
           data_state.set_elem(r,0,0);
	count--;
     }
     for (p=0;p<n_stages;p++)
     {
        for (q=0;q<n_half;q++)
        {
            in0 = data_state.get_elem(2*q,p);
            in1 = data_state.get_elem(2*q+1,p);
            xor_eval1 = abs(in0-swap_state.get_elem(q,p));
            xor_eval2 = abs(in1-xor_eval1);
            swap_state.set_elem(q,p,xor_eval2);
            if(xor_eval1)
            {
              data_vec.set_elem(2*q,in1);
              data_vec.set_elem(2*q+1,in0);
            }
            else
            {
              data_vec.set_elem(2*q,in0);
              data_vec.set_elem(2*q+1,in1);
            }
	    if (p<(n_stages-1))
	    {
	       data_state.set_elem(connection_matrix.get_elem(2*q,p+1),p+1,data_vec.get_elem(2*q));
	       data_state.set_elem(connection_matrix.get_elem(2*q+1,p+1),p+1,data_vec.get_elem(2*q+1));
	    }
	    else
	    {
              if (data_vec.get_elem(2*q))
                 out += dac_vec.get_elem(2*q);
              else
                 out -= dac_vec.get_elem(2*q);
	      if (data_vec.get_elem(2*q+1))
		 out += dac_vec.get_elem(2*q+1);
	      else
		 out -= dac_vec.get_elem(2*q+1);
	    } 	    
        }
     }
  }
