📄 fracn09.pl
字号:
# default parameters$tolerance = 1.0e-7; # default allowable relative tolerance on fout (+/-)$entity_name = "fracn"; # default name of entity in generated VHDL$architecture_name = "rtl"; # default name of architecture in generated VHDL$low_jitter_controller = 1; # on by default$convergence_check = 1; # on by default$tabstop = 4; # size of indent in generated code$verbose = -t STDOUT; # on by default iff connected to tty$std_logic_arith = 0; # off by default (default to lib IEEE.numeric_std)$std_logic_unsigned = 0; # off by default (default to lib IEEE.numeric_std)# process command line argumentsuse vars qw(@argv_orginal);@argv_orginal = @ARGV; # save for later...do { use Getopt::Std; # for command line parsing... my %opts; getopt('aefhHit', \%opts); foreach (keys %opts) { #print "$_\t" . (defined $opts{$_} ? $opts{$_} : "undefined") . "\n"; SWITCH : { if (/^a$/) { $architecture_name = $opts{$_}; last SWITCH; } if (/^e$/) { $entity_name = $opts{$_}; last SWITCH; } if (/^f$/) { $base_name = $opts{$_}; last SWITCH; } if (/^t$/) { $tolerance = $opts{$_}; last SWITCH; } if (/^i$/) { $tabstop = $opts{$_}; last SWITCH; } if (/^s$/) { $std_logic_arith = 1; last SWITCH; } if (/^u$/) { $std_logic_unsigned = 1; last SWITCH; } if (/^g$/) { $low_jitter_controller = 0; last SWITCH; } if (/^c$/) { $convergence_check = 0; last SWITCH; } if (/^v$/) { $verbose = not $verbose; last SWITCH; } if (/^x$/) { last SWITCH; } # magic ignored option! if (/^h$/i) { usage_exit; last SWITCH; } # help usage_exit("unknown option '$_'\n"); } }};usage_exit("Wrong number of arguments\n") if ($#ARGV != 1);# more global variablesuse vars qw($fin $fout $desired_ratio $min_ratio $max_ratio);use vars qw($floor_max_ratio $floor_min_ratio $ceil_min_ratio);$fin = $ARGV[0] + 0; # input clock frequency (Hz)$fout = $ARGV[1] + 0; # desired output frequency (Hz)# Check for bad inputusage_exit("Bad architecture name option\n") if ((not defined $architecture_name) or ($architecture_name !~ /^[a-z][a-z0-9_]+$/i));usage_exit("Bad entity name option\n") if ((not defined $entity_name) or ($entity_name !~ /^[a-z][a-z0-9_]+$/i));# output file names$base_name = $entity_name if (not defined $base_name);$vhdl_name = "$base_name.vhd";$verilog_name = "$base_name.v";usage_exit("Indent size must be a number\n") unless ($tabstop > 0 or $tabstop eq "0");usage_exit("Can't use std_logic_arith and std_logic_unsigned at the same time\n") if ($std_logic_arith and $std_logic_unsigned);# check for input that would cause numeric problems.usage_exit("frequencies must be > 0Hz\n") if ($fin <= 0 or $fout <= 0);usage_exit("Fin/Fout ratio too small\n") if ($fin/$fout < 1);#usage_exit("Fin/Fout ratio too small\n") if ($fin/$fout < 2); # can't do /1 prescaler?!usage_exit("Fin/Fout ratio too big\n") if (($fin/$fout > 1e12) and $convergence_check);usage_exit("tolerance must be > 0\n") if ($tolerance <= 0);usage_exit("tolerance too tight (try something like 0.0000001, or use the -c switch)\n") if (($tolerance < 1e-15) and $convergence_check);usage_exit("tolerance too loose (try something like 0.0001)\n") if (($tolerance >= 1) and $convergence_check);$desired_ratio = ($fin / $fout);$min_ratio = ($fin / ($fout * (1 + $tolerance)));$max_ratio = ($fin / ($fout * (1 - $tolerance)));#print "trying ratios between $min_ratio and $max_ratio\n";# get the integer upper and lower bounds$floor_max_ratio = floor($max_ratio);$floor_min_ratio = floor($min_ratio);$floor_min_ratio = 1 if ($floor_min_ratio == 0); # fix for ratios close to 1$ceil_min_ratio = ceil($min_ratio);die "numeric problems" if ($floor_max_ratio <= 0 or $floor_min_ratio <= 0);if (not $low_jitter_controller) { warning("Warning: the controller enabled by the \"minimum_jitter\" generic has", " not been implemented due to the -g command line switch.");}if ($std_logic_unsigned) { warning("Warning: std_logic_unsigned library has been used", " due to the -u command line switch.");}if ($std_logic_arith) { warning("Warning: std_logic_arith library has been used", " due to the -s command line switch.");}############### Design the prescaler (choose a value for n) ####################print "designing prescaler\n" if ($verbose);use vars qw($n); # more global variables$n = $floor_min_ratio;# check to see if a straight integer divider will doif ($floor_max_ratio >= $ceil_min_ratio) { warning("Warning: a fractional-N divider is not needed."); if ($floor_max_ratio > $ceil_min_ratio) { warning(" Integer dividers $ceil_min_ratio to $floor_max_ratio are ok."); $n = $floor_max_ratio; } else { warning(" Integer divider $floor_max_ratio is ok."); $n = $floor_max_ratio; }}#print "/$n,/" . ($n + 1) . " prescaler\n";############## Design the controller (choose values for a and b) ###############print "designing controller\n" if ($verbose);use vars qw($a $b $ratio $fout_achieved $error_achieved); # more global variables$a = 1; # number of /n cycles$b = 0; # number of /(n+1) cyclesdo { my $iterations_remaining = $convergence_check ? 1e7 : 1e12; # try different values of a and b until the ratio falls between min_ratio and # max_ratio while (1) { die "No convergence - try adjusting tolerance or use -c switch\n" . "Current values are a=$a b=$b" unless (--$iterations_remaining); $ratio = ($a * $n + $b * ($n + 1))/($a + $b); if ($ratio < $min_ratio) { # too small $b++; next; } if ($ratio > $max_ratio) { # too big $a++; next; } # just right, Goldilocks last; }};$fout_achieved = $fin/$ratio; # Hz$error_achieved = $fout_achieved - $fout; # Hzif (abs($error_achieved / $fout) > $tolerance) { warning("Warning: Dual Modulus Prescaler design did not meet frequency tolerance target.");}############ Work out the jitter performance ###################################use vars qw($bad_jitter $good_jitter $controller_table); # more global variables# calculate jitter performance, for "high jitter" controller architecture$bad_jitter = $a * ($ratio - $n) / $fin;#$bad_jitter = $b * ($n + 1 - $ratio) / $fin; # should be the sameprint "calculating controller table\n" if ($verbose);do { # work out distribution of /n,/n+1 for better jitter performance # (used in "low jitter" architecture) my $used_a = 0; my $used_b = 0; my $desired_trajectory = $a / ($a + $b); # The array controller_table contains the distribution of /n, /n+1 # (changed from an array to a string in version 0.05 to save space.) $controller_table = ""; # work out first entry (be careful, sometimes $b may be 0) if ( $a > $b ) { (substr $controller_table, 0, 1) = '1'; $used_a++; } else { (substr $controller_table, 0, 1) = '0'; $used_b++; } # work out rest of table for "maximum interleaving" (which gives minimum jitter) for (my $index = 1; $index < $a + $b; $index++) { if ( $used_a / $index > $desired_trajectory ) { (substr $controller_table, $index, 1) = '0'; $used_b++; } else { (substr $controller_table, $index, 1) = '1'; $used_a++; } } die "Numeric problem in jitter reducer" unless ($used_a == $a and $used_b == $b);};print "estimating jitter\n" if ($verbose);do { my $total_outputs = 0; my $total_input_clocks = 0; my $max_pos_error = 0; my $max_neg_error = 0; # now measure the jitter for (my $index = 0; $index < $a + $b; $index++) { $total_input_clocks += (substr $controller_table, $index, 1) ? $n : $n + 1; $total_outputs++; my $phase_error = $total_input_clocks - ($ratio * $total_outputs); $max_pos_error = $phase_error if ( $max_pos_error < $phase_error ); $max_neg_error = $phase_error if ( $max_neg_error > $phase_error ); } $good_jitter = ($max_pos_error - $max_neg_error) / $fin;};############### Design the recursive controller ################################print "designing recursive controller\n" if ($verbose);# The modulus control signal for the prescaler can be generated by another# fractional-N divider, which in turn can have its modulus control signal# generated by yet another fractional-N divider, and so on.# We stop when we don't need another fractional-N divider, and can just use# a fixed divider.# The particular arrangement we use also produces the minimum possible jitter.use vars qw($stage @n_array @m_array @a_array @b_array @i_array); # more global variablesuse vars qw($ff_count_recursive_controller); # more global variables$stage = 0;# Stage is (roughly) the number of recursive stages needed:# Stage 0 is the prescaler we've already designed# Stages 1 .. $stage are the recursive dual modulus dividers# Stage $stage + 1 is the fixed divider at the end$n_array[$stage] = $n; # this stage divides by n or n+1$m_array[$stage] = 'X'; # Controls duty cycle of modulus control output from this stage.$a_array[$stage] = $a; # This stage requires its modulus control input to be low for this number of cycles$b_array[$stage] = $b; # This stage requires its modulus control input to be high for this number of cycles$i_array[$stage] = 'X'; # Invert the output of this stage#print "n$stage=$n\tm$stage=\ta$stage=$a\tb$stage=$b\ti$stage=\n";while ($b_array[$stage] > 1.0001 and $a_array[$stage] > 1.0001) { my $n1 = $n_array[$stage]; my $a1 = $a_array[$stage]; my $b1 = $b_array[$stage]; my $n2; my $m2; my $a2; my $b2; my $i2; my $determinant; die "negative count" if ($n1 < 0 or $a1 < 0 or $b1 < 0); $i2 = ($a1 < $b1) ? 1 : 0; if ( $i2 ) { $n2 = floor(($a1 + $b1) / ($a1)); $m2 = ceil($b1 / $a1); # we have a1, b1, and we know how to determine a1, b1 from a2, b2, # so invert the 2x2 matrix to work out a2, b2 from a1, b1. $determinant = ($n2 - $m2 + 1); die "singular matrix" if ($determinant == 0); $a2 = ($m2 * $a1 - ($n2 + 1 - $m2) * $b1) / $determinant; $b2 = ((1 - $m2) * $a1 + ($n2 + 1 - $m2) * $b1) / $determinant; } else { $n2 = floor(($a1 + $b1) / ($b1)); $m2 = ceil($a1 / $b1); $determinant = ($m2 - $n2 - 1); die "singular matrix" if ($determinant == 0); $a2 = (($n2 + 1 - $m2) * $a1 - $m2 * $b1) / $determinant; $b2 = (($m2 - $n2 - 1) * $a1 + ($m2 - 1) * $b1) / $determinant; } $stage++; $n_array[$stage] = $n2; $m_array[$stage] = $m2; $a_array[$stage] = $a2; $b_array[$stage] = $b2; $i_array[$stage] = $i2; #print "n$stage=$n2\tm$stage=$m2\ta$stage=$a2\tb$stage=$b2\ti$stage=$i2\n"; die "too many stages in recursive controller" if ($stage > 15 and $convergence_check);}# fill in the parameters for the last (non-fractional) stage$n_array[$stage + 1] = $a_array[$stage] + $b_array[$stage] - 1;$m_array[$stage + 1] = $a_array[$stage];$a_array[$stage + 1] = 'X';$b_array[$stage + 1] = 'X';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -