From c6c0a5652e59f02e46c5580bf72e6e23125036a5 Mon Sep 17 00:00:00 2001 From: AdamLaurie Date: Thu, 20 Jan 2022 18:53:18 +0000 Subject: [PATCH 1/2] initial stab at getting pi4 working (tested and working with DCF77 - seems to select regular oscillator, see comments in code). apologies for moving code around - i'm not a c++ guy and that was the easiest way to get it to compile. i also don't understand why i'm getting warnings! enjoy! --- gpio.cc | 182 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 95 insertions(+), 87 deletions(-) diff --git a/gpio.cc b/gpio.cc index c250870..aad88b1 100644 --- a/gpio.cc +++ b/gpio.cc @@ -102,91 +102,6 @@ uint32_t GPIO::RequestInput(uint32_t inputs) { return inputs; } -// BCM2835-ARM-Peripherals.pdf, page 105 onwards. -double GPIO::StartClock(double requested_freq) { - // Figure out best clock source to get closest to the requested - // frequency with MASH=1. We check starting from the highest frequency to - // find lowest jitter opportunity first. - - static const struct { int src; double frequency; } kClockSources[] = { - { 5, 1000.0e6 }, // PLLC - { 6, 500.0e6 }, // PLLD - { 7, 216.0e6 }, // HDMI <- this can be problematic if monitor connected - { 1, 19.2e6 }, // regular oscillator - }; - - int divI = -1; - int divF = -1; - int best_clock_source = -1; - double smallest_error_so_far = 1e9; - for (size_t i = 0; i < sizeof(kClockSources)/sizeof(kClockSources[0]); ++i) { - double division = kClockSources[i].frequency / requested_freq; - if (division < 2 || division > 4095) continue; - int test_divi = (int) division; - int test_divf = (division - test_divi) * 1024; - double freq = kClockSources[i].frequency / (test_divi + test_divf/1024.0); - double error = fabsl(requested_freq - freq); - if (error >= smallest_error_so_far) continue; - smallest_error_so_far = error; - best_clock_source = i; - divI = test_divi; - divF = test_divf; - } - - if (divI < 0) - return -1.0; // Couldn't find any suitable clock. - - assert(divI >= 2 && divI < 4096 && divF >= 0 && divF < 4096); - - StopClock(); - - const uint32_t ctl = CLK_CMGP0_CTL; - const uint32_t div = CLK_CMGP0_DIV; - const uint32_t src = kClockSources[best_clock_source].src; - const uint32_t mash = 1; // Good approximation, low jitter. - - clock_reg_[div] = CLK_PASSWD | CLK_DIV_DIVI(divI) | CLK_DIV_DIVF(divF); - usleep(10); - - clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_MASH(mash) | CLK_CTL_SRC(src); - usleep(10); - - clock_reg_[ctl] |= CLK_PASSWD | CLK_CTL_ENAB; - - EnableClockOutput(true); - -#if 0 - // There have been reports of different clock source frequencies. This - // helps figuring out which source was picked. - fprintf(stderr, "Choose clock %d at %gHz / %.3f = %.3f\n", - kClockSources[best_clock_source].src, - kClockSources[best_clock_source].frequency, - divI + divF/1024.0, - kClockSources[best_clock_source].frequency / (divI + divF/1024.0)); -#endif - - return kClockSources[best_clock_source].frequency / (divI + divF/1024.0); -} - -void GPIO::StopClock() { - const uint32_t ctl = CLK_CMGP0_CTL; - clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_KILL; - - // Wait until clock confirms not to be busy anymore. - while (clock_reg_[ctl] & CLK_CTL_BUSY) { - usleep(10); - } - EnableClockOutput(false); -} - -void GPIO::EnableClockOutput(bool on) { - if (on) { - ALT0_GPIO(4); // Pinmux GPIO4 into outputting clock. - } else { - INP_GPIO(4); - } -} - // We are not interested in the _exact_ model, just good enough to determine // What to do. enum RaspberryPiModel { @@ -241,8 +156,7 @@ static RaspberryPiModel DetermineRaspberryModel() { case 0x11: /* Pi 4 */ // A first test did not seem to work. Maybe the registers changed ? - fprintf(stderr, "Note: Frequency generation is known to not work on Pi4; " - "Use older Pis for now.\n"); + fprintf(stderr, "Note: Frequency generation is experimental on Pi4.\n"); return PI_MODEL_4; default: /* a bunch of versions representing Pi 3 */ @@ -300,3 +214,97 @@ bool GPIO::Init() { return gpio_port_ != MAP_FAILED && clock_reg_ != MAP_FAILED; } + + +// BCM2835-ARM-Peripherals.pdf, page 105 onwards. +double GPIO::StartClock(double requested_freq) { + // Figure out best clock source to get closest to the requested + // frequency with MASH=1. We check starting from the highest frequency to + // find lowest jitter opportunity first. + double f_pllc, f_plld, f_hdmi, f_regular; + switch (GetPiModel()) { + case PI_MODEL_1: + case PI_MODEL_2: + case PI_MODEL_3: f_pllc = 1000.0e6; f_plld = 500.0e6; f_hdmi = 216.0e6; f_regular= 19.2e6; break; + case PI_MODEL_4: f_pllc = 3170.0e5; f_plld = 880.0e5; f_hdmi = 216.0e6; f_regular= 5.401750e7; break; + } + + static const struct { int src; double frequency; } kClockSources[] = { + { 5, f_pllc }, // PLLC (note on pi4 3170.0e5 is the closest I can get - but output on scope is 158.xx KHz) + { 6, f_plld }, // PLLD (all over the place on pi4 but in the ballpark) + { 7, f_hdmi }, // HDMI <- this can be problematic if monitor connected (could not find a valid value on pi4) + { 1, f_regular }, // regular oscillator + }; + + int divI = -1; + int divF = -1; + int best_clock_source = -1; + double smallest_error_so_far = 1e9; + for (size_t i = 0; i < sizeof(kClockSources)/sizeof(kClockSources[0]); ++i) { + double division = kClockSources[i].frequency / requested_freq; + if (division < 2 || division > 4095) continue; + int test_divi = (int) division; + int test_divf = (division - test_divi) * 1024; + double freq = kClockSources[i].frequency / (test_divi + test_divf/1024.0); + double error = fabsl(requested_freq - freq); + if (error >= smallest_error_so_far) continue; + smallest_error_so_far = error; + best_clock_source = i; + divI = test_divi; + divF = test_divf; + } + + if (divI < 0) + return -1.0; // Couldn't find any suitable clock. + + assert(divI >= 2 && divI < 4096 && divF >= 0 && divF < 4096); + + StopClock(); + + const uint32_t ctl = CLK_CMGP0_CTL; + const uint32_t div = CLK_CMGP0_DIV; + const uint32_t src = kClockSources[best_clock_source].src; + const uint32_t mash = 1; // Good approximation, low jitter. + + clock_reg_[div] = CLK_PASSWD | CLK_DIV_DIVI(divI) | CLK_DIV_DIVF(divF); + usleep(10); + + clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_MASH(mash) | CLK_CTL_SRC(src); + usleep(10); + + clock_reg_[ctl] |= CLK_PASSWD | CLK_CTL_ENAB; + + EnableClockOutput(true); + +#if 0 + // There have been reports of different clock source frequencies. This + // helps figuring out which source was picked. + fprintf(stderr, "Choose clock %d at %gHz / %.3f = %.3f\n", + kClockSources[best_clock_source].src, + kClockSources[best_clock_source].frequency, + divI + divF/1024.0, + kClockSources[best_clock_source].frequency / (divI + divF/1024.0)); +#endif + + return kClockSources[best_clock_source].frequency / (divI + divF/1024.0); +} + +void GPIO::StopClock() { + const uint32_t ctl = CLK_CMGP0_CTL; + clock_reg_[ctl] = CLK_PASSWD | CLK_CTL_KILL; + + // Wait until clock confirms not to be busy anymore. + while (clock_reg_[ctl] & CLK_CTL_BUSY) { + usleep(10); + } + EnableClockOutput(false); +} + +void GPIO::EnableClockOutput(bool on) { + if (on) { + ALT0_GPIO(4); // Pinmux GPIO4 into outputting clock. + } else { + INP_GPIO(4); + } +} + From e8acea4124490eb85e436ee917fcb29af0223033 Mon Sep 17 00:00:00 2001 From: AdamLaurie Date: Fri, 21 Jan 2022 10:10:04 +0000 Subject: [PATCH 2/2] rationalise frequency numbers and get rid of compilation warnings --- gpio.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gpio.cc b/gpio.cc index 1cf512c..eace981 100644 --- a/gpio.cc +++ b/gpio.cc @@ -226,13 +226,14 @@ double GPIO::StartClock(double requested_freq) { switch (GetPiModel()) { case PI_MODEL_1: case PI_MODEL_2: - case PI_MODEL_3: f_pllc = 1000.0e6; f_plld = 500.0e6; f_hdmi = 216.0e6; f_regular= 19.2e6; break; - case PI_MODEL_4: f_pllc = 3170.0e5; f_plld = 880.0e5; f_hdmi = 216.0e6; f_regular= 5.401750e7; break; + case PI_MODEL_3: + default: f_pllc = 1000.0e6; f_plld = 500.0e6; f_hdmi = 216.0e6; f_regular= 19.2e6; break; + case PI_MODEL_4: f_pllc = 317.0e6; f_plld = 317.0e6; f_hdmi = 216.0e6; f_regular= 54.0175e6; break; } static const struct { int src; double frequency; } kClockSources[] = { - { 5, f_pllc }, // PLLC (note on pi4 3170.0e5 is the closest I can get - but output on scope is 158.xx KHz) - { 6, f_plld }, // PLLD (all over the place on pi4 but in the ballpark) + { 5, f_pllc }, // PLLC (note on pi4 317.0e6 is the closest I can get - but output on scope is 158.43 KHz) + { 6, f_plld }, // PLLD (note on pi4 317.0e6 is the closest I can get - but output on scope is 183.37 KHz) { 7, f_hdmi }, // HDMI <- this can be problematic if monitor connected (could not find a valid value on pi4) { 1, f_regular }, // regular oscillator };