/* compile with "cc bbsim.c -o bbsim -lm" */ /* this is a 2nd-order BB loop simulator program */ /* pipe stdout to autoplot (ap) to graph */ /* Rick Walker (walker@opus.hpl.hp.com), 1998 */ #include #include #include #include #include /* for prbs.c */ #define PLEN 23 /* shift register length */ #define TAP 18 /* xor tap location */ double gauss(); #define NUM_CYCLES 40000 #define MAX_LATENCY 128 int main(argc,argv) int argc; char **argv; { int c; int i; extern char *optarg; extern int optind; extern int opterr; char *progname; int cycle; time_t time_val; double vco_phase=0.0; double rms_sum_vco=0.0; double data_phase=0.0; double o_data_phase=0.0; double rms_sum_data=0.0; int dir; int pd_output; /* intermediate variable for phase detector */ int pd_delay[MAX_LATENCY]; /* array for introducing latency into p.d. */ int pd_count; /* counter for p.d latency array */ double loop_filter=0.0; double amp=0.0; double period=100.0; double psi=1.0; /* (2*BETA*TAU)/tupdate */ double jitter=0.0; /* data jitter temp variable */ double djitter=1.0; /* data rms input jitter */ double vjitter=0.0; /* vco rms jitter */ double vcojitter=0.0; /* vco jitter running integral */ double bangbang=1.0; /* vco phase advance/retard per frame */ double tdensity=1.0; /* transition probability */ int transition=0; double npoints=4000.0; /* number of points calculated */ double offset=0.0; /* initial phase offset */ double cycletime=1.0; /* initial normalized update time */ double mismatch=0.0; /* vco mismatch in % of BB */ /* mismatches != 0.0 turn off loop integrator */ double x,y,z; double pdel, fdel; double window; /* triangular windowing fxn for xfer gain */ double real,imag; /* dft scratch variables */ int random=0; /* flag to plot input jitter */ int errflag=0; int verbose=0; int titleflag=0; int plot_error=0; int statonly=0; int tristate=0; int do_windowing=0; int interpolate=1; /* how many times to subcompute tupdate */ int latency=0; /* number of bits phase detect latency */ int maxzero,zerorun; int maxone,onerun; int olddir; time(&time_val); /* will be used as seed for rand48 */ opterr = 1; progname = argv[0]; while ((c = getopt(argc,argv,"a:b:c:ef:i:j:l:m:n:o:p:q:rstvx:wz")) != EOF) switch (c) { case 'a': amp = atof(optarg); break; case 'c': cycletime= atof(optarg); break; case 'b': bangbang = atof(optarg); break; case 'e': plot_error++; break; case 'f': period = atof(optarg); break; case 'i': interpolate = (int) atof(optarg); break; case 'j': djitter = atof(optarg); break; case 'l': latency = (int) atof(optarg); if (latency < 0 || latency > MAX_LATENCY-1) { fprintf(stderr,"ERROR: -l out of range (0-15)\n"); exit(1); } break; case 'm': mismatch = atof(optarg); if (mismatch < -1.0 || mismatch > 1.0) { fprintf(stderr,"ERROR: -m out of range\n"); exit(1); } break; case 'n': npoints = atof(optarg); break; case 'o': offset=atof(optarg); break; case 'p': psi = atof(optarg); break; case 'q': vjitter = atof(optarg); break; case 'r': random++; break; case 's': statonly++; break; case 't': titleflag++; break; case 'v': verbose++; break; case 'x': tdensity = atof(optarg); if (tdensity < 0.0) { fprintf(stderr,"ERROR: -x out of range\n"); exit(1); } break; case 'w': do_windowing++; break; case 'z': tristate++; break; case '?': errflag++; } if (errflag) { fprintf(stderr, "usage: %s [options]\n",progname); fprintf(stderr, " -a \n"); fprintf(stderr, " -b \n"); fprintf(stderr, " -c (default is normalized to 1)\n"); fprintf(stderr, " -e (plot phase error rather than phase)\n"); fprintf(stderr, " -f (in update times)\n"); fprintf(stderr, " -i (interpolate data points by n)\n"); fprintf(stderr, " -j (rms)\n"); fprintf(stderr, " -l (bit-times)\n"); fprintf(stderr, " -m \n"); fprintf(stderr, " -n \n"); fprintf(stderr, " -o (in bb units)\n"); fprintf(stderr, " -p (2*Beta*Tau/tupdate)\n"); fprintf(stderr, " -q (vco phase noise)\n"); fprintf(stderr, " -r (plot data phase also)\n"); fprintf(stderr, " -s (statistics only)\n"); fprintf(stderr, " -t (output tplot title)\n"); fprintf(stderr, " -v (verbose flag)\n"); fprintf(stderr, " -w (do windowed dft freq & gain calc.)\n"); fprintf(stderr, " -x \n"); fprintf(stderr, " (if xarg < 1 interpreted as probability)\n"); fprintf(stderr, " (if xarg > 1 interpreted as run length)\n"); fprintf(stderr, " -z (tristate during non-transition times)\n"); exit(2); } if (titleflag) { printf("title options: "); } else { printf("# "); } for (i=1; i< argc; i++) { printf(" %s", argv[i]); } printf("\n"); if (titleflag) { printf("title BB loop response\n"); printf("yscale 1 jitter [normalized to BB]\n"); printf("xscale 1 timestep [normalized to bit time] #\n"); } srand48((long) time_val); /* reseed generator */ /* this next statement sets up steady state phase conditions */ /* plus any needed delta offset from command line */ vco_phase = offset+amp-bangbang/2.0; dir=0; pd_output=0; /* initialize pd latency array buffer */ for (pd_count=0; pd_count<=latency; pd_count++) { pd_delay[pd_count]=0; } pd_count=0; maxzero = zerorun=0; maxone = onerun=0; /* for windowing dft analysis */ real = imag = 0.0; jitter=0.0; vcojitter=0.0; for (cycle=1; cycle<= (int) npoints; cycle++) { o_data_phase = data_phase; vcojitter += gauss()*vjitter; /* running integral for VCO phase noise */ jitter = gauss()*djitter + vcojitter; /* add in gaussian data phase noise */ data_phase = jitter + amp*cos(2.0*M_PI*((double) cycle)/period); if (!statonly) { for (x=0.0/interpolate; x<1.0; x+=1.0/interpolate) { pdel=(dir+mismatch)*bangbang*x; if (mismatch == 0.0) { fdel=(((dir+mismatch)*x*x+2.0*loop_filter*x)*bangbang/psi); } else { fdel=0.0; } if (plot_error) { /* plot phase error */ printf("%g %g\n", (((double) cycle)-1.0+x)*cycletime, o_data_phase+(data_phase-o_data_phase)*x-(vco_phase+pdel+fdel)); /* data_phase-(vco_phase+pdel+fdel)); */ } else { /* plot actual phase trajectory */ printf("%g %g\n", (((double) cycle)-1.0+x)*cycletime, vco_phase+pdel+fdel); } } } vco_phase += (dir+mismatch)*bangbang; if (mismatch == 0.0) { /* turn off integrator when mismatched */ vco_phase += 2.0*loop_filter*bangbang/psi; vco_phase += (dir+mismatch)*bangbang/psi; } fflush(stdout); olddir=dir; if (tdensity >= 1.0) { /* run length spec */ if (cycle%((int) tdensity) == 0) { transition = 1; } else { transition = 0; } } else if (tdensity < 1.0) { /* probabilistic defininition */ if (drand48() <= tdensity) { /* got a transition? */ transition = 1; } else { transition = 0; } } if (transition) { /* got a transition? */ if (vco_phase >= data_phase) { pd_output = -1; } else { pd_output = 1; } } else { /* nope */ if (tristate) { pd_output = 0; /* tristate */ } else { ; /* leave direction alone */ } } pd_delay[pd_count] = pd_output; /* fprintf(stderr,"1: %d, %d\t", pd_count, pd_output); */ /* complicated incrementing because (pd_count%0) dumps core */ pd_count++; if (pd_count >latency) { pd_count=0; } /* fprintf(stderr,"2: %d, %d\n", pd_count, pd_delay[pd_count]); */ dir=pd_delay[pd_count]; loop_filter += (double) dir; if (olddir==dir) { if (dir == 1) { onerun++; } else { zerorun++; } } else { if (zerorun > maxzero) { maxzero=zerorun; } if (onerun > maxone) { maxone=onerun; } zerorun=0; onerun=0; } rms_sum_vco += vco_phase*vco_phase; rms_sum_data += data_phase*data_phase; if (do_windowing) { if (cycle > ((int) npoints)/2 ) { window = 1.0-fabs(((double)cycle-3.0*npoints/4.0)/(npoints/4.0)); real+=cos(2.0*M_PI*((double) cycle)/period)*window*vco_phase; imag+=sin(2.0*M_PI*((double) cycle)/period)*window*vco_phase; } } } if (random && !statonly ) { srand48((long) time_val); /* seed generator */ printf("pen\n"); jitter=0.0; vcojitter=0.0; for (cycle=1; cycle<= (int) npoints; cycle++) { vcojitter += gauss()*vjitter; /* running integral for VCO phase noise */ jitter = gauss()*djitter + vcojitter; /* add in gaussian data phase noise */ data_phase = jitter + amp*cos(2.0*M_PI*((double) cycle)/period); printf("%g %g\n", ((double) cycle)*cycletime, data_phase); if (tdensity < 1.0) { drand48(); /* simply called to keep in sync */ } } /* windowing function */ for (cycle=1; cycle<= (int) npoints; cycle++) { if (cycle > ((int) npoints)/2 ) { window = (((double)cycle-3.0*npoints/4.0)/(npoints/4.0)); /* printf("%d %g\n", cycle, 1.0-fabs(window)); */ } } } if (do_windowing) { /* triangular window normalization */ x = npoints/8.0; printf("# DFT: %g\t%g\t%g\t%g\n", period, amp, sqrt(real*real+imag*imag)/x, 360.0*atan2(imag,real))/(2.0*M_PI); } printf("# STAT: %g\t%g\t%d\t%d\t%g\t%g\n", djitter, psi, maxone, maxzero, sqrt(rms_sum_data/(npoints-1)), sqrt(rms_sum_vco/(npoints-1)) ); fflush(stdout); exit(0); } double gauss() { static int iset=0; static float gset; double fac,r,v1,v2; double drand48(); if(iset == 0) { /* we don't have an extra deviate handy, so */ do { v1=2.0*drand48()-1.0; /* pick two uniform numbers in the */ v2=2.0*drand48()-1.0; /* square from -1 to +1 in each axis */ r=v1*v1+v2*v2; /* see if they are in unit circle */ } while (r >= 1.0); /* and if not, try again... */ fac=sqrt(-2.0*log(r)/r); /* now make the box-muller transformation to get two normal */ /* deviates. return one and save the other for next time */ gset=v1*fac; iset=1; /* set flag */ return v2*fac; } else { /* we have extra deviate handy */ iset=0; /* so unset flag */ return gset; /* and return it */ } } int prbs23() /* 2^23-1 sequence generator */ { static int bitval; static int reg[PLEN] ={ 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 }; static int loop,temp; bitval = reg[0]; if (reg[PLEN-1] == reg[TAP-1]) /* do xor */ temp = 0; else temp = 1; for(loop = 0; loop <= PLEN-1; loop++) reg[PLEN-loop-1]=reg[PLEN-loop-2]; reg[0] = temp; return(bitval); } /* cyclical pattern generator */ int patgen(index, str) int *index /* index into argument string */; char *str; /* pattern string ** 0=low, (any other char)=high, ** [try to use '1' for high... ** when EOS is hit, the output cycles over ** from the beginning. */ { *index = (++*index%(strlen(str))); if(str[*index] == '0') { return(0); } else { return(1); } }