From 30f2bc41498723b7439be037ebb19266e99d156a Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 5 Aug 2018 12:19:41 -0700 Subject: [PATCH 1/2] Clock Manager: Add API to measure LPC43xx clock inputs against IRC oscillator. --- firmware/application/clock_manager.cpp | 24 ++++++++++++++++++++++++ firmware/application/clock_manager.hpp | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index c8b75cce..0cbf462f 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -422,6 +422,30 @@ void ClockManager::disable_gp_clkin_source() { clock_generator.disable_output(clock_generator_output_mcu_clkin); } +void ClockManager::start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel) { + // Measure a clock input for 480 cycles of the LPC43xx IRC. + LPC_CGU->FREQ_MON = LPC_CGU_FREQ_MON_Type { + .RCNT = 480, + .FCNT = 0, + .MEAS = 0, + .CLK_SEL = toUType(clk_sel), + .RESERVED0 = 0 + }; + LPC_CGU->FREQ_MON.MEAS = 1; +} + +void ClockManager::wait_For_frequency_monitor_measurement_done() { + // FREQ_MON mechanism fails to finish if there's no clock present on selected input?! + while(LPC_CGU->FREQ_MON.MEAS == 1); +} + +uint32_t ClockManager::get_frequency_monitor_measurement_in_hertz() { + // Measurement is only as accurate as the LPC43xx IRC oscillator, + // which is +/- 1.5%. Measurement is for 480 IRC clcocks. Scale + // the cycle count to get a value in Hertz. + return LPC_CGU->FREQ_MON.FCNT * 25000; +} + void ClockManager::enable_xtal_oscillator() { LPC_CGU->XTAL_OSC_CTRL.BYPASS = 0; LPC_CGU->XTAL_OSC_CTRL.ENABLE = 1; diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index f1398529..de36d374 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -66,6 +66,8 @@ public: void set_reference_ppb(const int32_t ppb); + uint32_t get_frequency_monitor_measurement_in_hertz(); + private: I2C& i2c0; si5351::Si5351& clock_generator; @@ -75,6 +77,8 @@ private: void enable_gp_clkin_source(); void disable_gp_clkin_source(); + void start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel); + void wait_For_frequency_monitor_measurement_done(); void enable_xtal_oscillator(); void disable_xtal_oscillator(); From 88afee26d7bd708edc4e6a6be20475030d553255 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sun, 5 Aug 2018 12:21:25 -0700 Subject: [PATCH 2/2] Clock Manager: Detect Si5351 CLKIN present, measure frequency, and use if approximately 10MHz. --- firmware/application/clock_manager.cpp | 28 ++++++++++++++++++++++---- firmware/application/clock_manager.hpp | 2 ++ firmware/application/si5351.hpp | 9 +++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index 0cbf462f..fcddba0f 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -269,11 +269,23 @@ void ClockManager::init() { clock_generator.enable_fanout(); clock_generator.set_pll_input_sources(si5351_pll_input_sources); - const bool use_clkin = false; + const auto clkin_present = !clock_generator.clkin_loss_of_signal(); + auto clkin_valid = false; + + if( clkin_present ) { + // Measure Si5351B CLKIN frequency against LPC43xx IRC oscillator + set_gp_clkin_to_clkin_direct(); + start_frequency_monitor_measurement(cgu::CLK_SEL::GP_CLKIN); + wait_For_frequency_monitor_measurement_done(); + const auto clkin_frequency = get_frequency_monitor_measurement_in_hertz(); + + // CLKIN is required to be 10MHz. FREQ_MON measurement is accurate to 1.5% + // due to LPC43xx IRC oscillator precision. + clkin_valid = (clkin_frequency >= 9850000) && (clkin_frequency <= 10150000); + } + clock_generator.set_clock_control( - use_clkin ? - si5351_clock_control_clkin - : si5351_clock_control_xtal + clkin_valid ? si5351_clock_control_clkin : si5351_clock_control_xtal ); clock_generator.write(si5351_pll_a_xtal_reg); @@ -422,6 +434,14 @@ void ClockManager::disable_gp_clkin_source() { clock_generator.disable_output(clock_generator_output_mcu_clkin); } +void ClockManager::set_gp_clkin_to_clkin_direct() { + clock_generator.set_clock_control( + clock_generator_output_mcu_clkin, + { ClockControl::CLK_IDRV_2mA | ClockControl::CLK_SRC_CLKIN | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_On } + ); + enable_gp_clkin_source(); +} + void ClockManager::start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel) { // Measure a clock input for 480 cycles of the LPC43xx IRC. LPC_CGU->FREQ_MON = LPC_CGU_FREQ_MON_Type { diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index de36d374..5d4799ee 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -77,6 +77,8 @@ private: void enable_gp_clkin_source(); void disable_gp_clkin_source(); + void set_gp_clkin_to_clkin_direct(); + void start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel); void wait_For_frequency_monitor_measurement_done(); diff --git a/firmware/application/si5351.hpp b/firmware/application/si5351.hpp index 58104f82..c7465d71 100644 --- a/firmware/application/si5351.hpp +++ b/firmware/application/si5351.hpp @@ -310,6 +310,10 @@ public: while(device_status() & 0x80); } + bool clkin_loss_of_signal() { + return (device_status() >> 4) & 1; + } + void enable_fanout() { write_register(Register::FanoutEnable, 0b11010000); } @@ -373,6 +377,11 @@ public: update_all_clock_control(); } + void set_clock_control(const size_t n, const ClockControl::Type clock_control) { + _clock_control[n] = clock_control; + write_register(Register::CLKControl_Base + n, _clock_control[n]); + } + void enable_clock(const size_t n) { _clock_control[n] &= ~ClockControl::CLK_PDN_Mask; write_register(Register::CLKControl_Base + n, _clock_control[n]);