Skip to content

Instantly share code, notes, and snippets.

@ChuckM
Created February 23, 2015 02:13
Show Gist options
  • Save ChuckM/8c63afdcd0a31070146f to your computer and use it in GitHub Desktop.
Save ChuckM/8c63afdcd0a31070146f to your computer and use it in GitHub Desktop.
Perl code to calculate PLL constants
#!/usr/bin/env perl
#
# This is me doodling around with the various restrictions on PLL
# values in the STM32F Series Cortex-M chips to find the "best"
# settings for someone using the internal 16Mhz clock. Not sure
# why you would use one over the other exactly.
#
# According to the spec, the closer the "input" frequency to the
# PLL is to 2Mhz the less jitter it has, with an HSI of 16Mhz
# and PLLM set to /8 you get exactly 2Mhz, but you can go all
# the way down to /16 to get a 1Mhz input clock, still be in spec
# and that opens up a couple additional frequencies.
# use strict; use warnings;
use Getopt::Long;
use Data::Dumper;
my $clock_in;
my $clock_name;
my $clock_max;
GetOptions(
"freq=i" => \$clock_in,
"name=s" => \$clock_name,
"max=i" => \$clock_max,
);
$clock_in = $clock_in // 16;
$clock_name = $clock_name // "HSI";
$clock_max = $clock_max // 180;
my %usb;
#
# These are the VCO frequencies for which there is an
# integer divider for PLLQ to get 48Mhz for USB OTG FS
#
for (my $i = 192; $i < 433; $i += 1) {
if (($i % 48) == 0) {
$usb{$i} = $i / 48;
}
}
#
# Compute every way to generate a VCO frequency between
# 192Mhz and 432Mhz, and pull out the ones that are
# integer multiples of 1Mhz. Note that having PLLM
# be '8' is preferable as it gives an 'optimum' 2Mhz
# frequency input into the PLL.
#
my %freqs;
for (my $pllm = 2; $pllm < 64; $pllm += 1) {
my $vco_in = $clock_in / $pllm;
next if (($vco_in < 1.0) or ($vco_in > 2.0));
for (my $plln = 96; $plln < 433; $plln += 1) {
my $vco = $vco_in * $plln;
next if ($vco < 192.0); # too slow
next if ($vco > 432.0); # too fast
next if defined $freqs{int $vco}; # already got best one
#
# Save the combination of pllm and plln that gives us
# an integral number of Mhz frequency
#
if ((int $vco) == $vco) {
$freqs{int $vco}->{pllm} = $pllm;
$freqs{int $vco}->{plln} = $plln;
}
}
}
my %pllp_val = ( 2 => 0, 4 => 1, 6 => 2, 8 => 3 );
#
# Now pick out all system clock speeds that are less than or equal
# to the maximum allowed System Clock.
#
# This is 120Mhz for the F40x/F41x, and 180Mhz for the F42x/F43x
#
my %sysclocks;
my $src = ($clock_name =~ /HSE/) ? 1 : 0;
for (my $vco = 192; $vco < 433; $vco += 1) {
next if not defined $usb{$vco};
foreach my $pllp (2, 4, 6, 8) {
my $clk = $vco / $pllp;
next if $clk > $clock_max;
my $const = $freqs{$vco}->{pllm} << 0 |
$freqs{$vco}->{plln} << 6 |
$pllp_val{$pllp} << 16 |
$src |
$usb{$vco} << 24;
push @{$sysclocks{$clk}->{combo}}, { pllm => $freqs{$vco}->{pllm},
plln => $freqs{$vco}->{plln},
pllp => $pllp,
pllq => $usb{$vco},
const => $const,
src => $src,
vco => $vco };
}
}
#
# print out a table of system clocks and their respective PLL Values
#
printf " SYSCLK : VCO : PLLM : PLLN : PLLP : PLLQ\n";
printf "---------+-------+------+------+------+------+\n";
foreach my $k (sort { $a <=> $b} keys %sysclocks) {
foreach my $clk (@{$sysclocks{$k}->{combo}}) {
printf " %3d Mhz : %4d : %4d : %4d : %4d : %4d : 0x%08x\n",
$k, $clk->{vco}, $clk->{pllm}, $clk->{plln}, $clk->{pllp}, $clk->{pllq},
$clk->{const};
}
}
print "#define RCC_PLLCFGR_BITS(pllp, plln, pllq, pllm, src) (\\\n";
print "\t\t((pllp & RCC_PLLCFGR_PLLP_MASK) << RCC_PLLCFGR_PLLP_SHIFT) |\\\n";
print "\t\t((plln & RCC_PLLCFGR_PLLN_MASK) << RCC_PLLCFGR_PLLN_SHIFT) |\\\n";
print "\t\t((pllq & RCC_PLLCFGR_PLLQ_MASK) << RCC_PLLCFGR_PLLQ_SHIFT) |\\\n";
my $mask = 0xf << 24 | 0x3 << 16 | 0x1ff << 6 | 0x3f;
printf "#define HSI_PLLCFGR_PLL_MASK\t0x%08x\n", $mask;
my %clocks;
foreach my $k (sort { $a <=> $b} keys %sysclocks) {
foreach my $clk (@{$sysclocks{$k}->{combo}}) {
next if defined $clocks{$k};
printf "#define RCC_%dMHZ_%s_%dMHZ_PLL\t\t%s\t/* VCO = %d Mhz */\n", $clock_in, $clock_name, $k,
use_macro($clk), $clk->{vco};
$clocks{$k} = 1;
}
}
sub use_macro {
my ($s) = @_;
return "RCC_PLLCFGR_BITS($s->{pllp}, $s->{plln}, $s->{pllq}, $s->{pllm}, $s->{src})";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment