From 52239d320a743d8bbbae3d0f1f2992db69f29a92 Mon Sep 17 00:00:00 2001 From: stafur Date: Sat, 7 Mar 2026 19:39:53 -0500 Subject: [PATCH] Next praline clean up (#3077) * Cleaned up #ifndef PRALINE and updated logic to being with #ifdef PRALINE entries where possible to make logic flow for PRALINE code execution pipeline clearer. Cleaned up compiletime warnings for PRALINE related codebase updates. * Addressed comments provided by copilot during PR review. Combed through frequency definitions for consistency between PLL A and PLL B register definitions for CLKs 0-7. Ensured CLK3/LK6 <- SMA PORTs and CLK7 <- not utiliized are disabled during core development phase to support root cause analysis of any spectral artifacts. Updated MCU frequency to 40MHz to ensure audio harmonics are outside FM radio band range (< 80 MHz, >120MHz) and added comments clarifying choice of 40 over 10 MHz for potential future root cause analysis in other bands where audio may be expected as needed. Added CLK6 and CLK7 to Clocks Status View Debug display. Moved CLK defintions and PLL instantiations for components that are most RF sensitive to PLL A. Left others in PLL B. That is move FPGA CLK1 to PLL B, while moving CLK2, CLK4, and CLK5 to PLL A. * Cleaned up PLL A and B XTAL reference checks relative to 800 MHz. --- .../application/apps/analog_audio_app.cpp | 6 +- firmware/application/apps/ui_debug.cpp | 8 +- firmware/application/apps/ui_debug.hpp | 18 +- firmware/application/clock_manager.cpp | 244 +++++++++++++----- firmware/application/hw/si5351.hpp | 18 +- firmware/application/portapack.cpp | 25 +- firmware/application/rf_path.cpp | 6 +- firmware/baseband/proc_am_audio.hpp | 8 +- .../boards/PORTAPACK_APPLICATION/board.cpp | 55 ++-- firmware/common/baseband_sgpio.cpp | 18 +- 10 files changed, 277 insertions(+), 129 deletions(-) diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index 0f6a5af52..a1f4e224e 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -203,15 +203,15 @@ SPECOptionsView::SPECOptionsView( #ifdef PRALINE PralineOptionsView::PralineOptionsView(Rect parent_rect, const Style* style) { + set_style(style); set_parent_rect(parent_rect); add_children({&label_sr, &options_sr, &label_dc, &options_dc, &label_qi, &options_qi, &label_qs, &options_qs}); - // 1. READ the current state from hardware + // READ the current state from hardware fpga_reg_1 = radio::debug::fpga::register_read(1); - uint32_t fpga_reg_2 = radio::debug::fpga::register_read(2); - // 2. INITIALIZE UI WIDGETS based on read bits + // INITIALIZE UI WIDGETS based on read bits options_dc.set_by_value((fpga_reg_1 & 0x01) ? 1 : 0); // Bit 0 options_qi.set_by_value((fpga_reg_1 & 0x02) ? 1 : 0); // Bit 1 options_qs.set_by_value((fpga_reg_1 & 0x04) ? 1 : 0); // Bit 2 diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index 45b132b46..6ede43f6c 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -2593,7 +2593,9 @@ PralineClockDebugView::PralineClockDebugView(NavigationView& nav) {&t2_id, &t2_ma, &t2_mode, &t2_src, &t2_ph, &t2_st}, {&t3_id, &t3_ma, &t3_mode, &t3_src, &t3_ph, &t3_st}, {&t4_id, &t4_ma, &t4_mode, &t4_src, &t4_ph, &t4_st}, - {&t5_id, &t5_ma, &t5_mode, &t5_src, &t5_ph, &t5_st}} { + {&t5_id, &t5_ma, &t5_mode, &t5_src, &t5_ph, &t5_st}, + {&t6_id, &t6_ma, &t6_mode, &t6_src, &t6_ph, &t6_st}, + {&t7_id, &t7_ma, &t7_mode, &t7_src, &t7_ph, &t7_st}} { add_children({&text_title, &text_lbl_pll, &text_pll_status, &text_lbl_afe, &text_afe_rate, &text_lbl_n, &text_n_val, &text_header, @@ -2603,6 +2605,8 @@ PralineClockDebugView::PralineClockDebugView(NavigationView& nav) &t3_id, &t3_ma, &t3_mode, &t3_src, &t3_ph, &t3_st, &t4_id, &t4_ma, &t4_mode, &t4_src, &t4_ph, &t4_st, &t5_id, &t5_ma, &t5_mode, &t5_src, &t5_ph, &t5_st, + &t6_id, &t6_ma, &t6_mode, &t6_src, &t6_ph, &t6_st, + &t7_id, &t7_ma, &t7_mode, &t7_src, &t7_ph, &t7_st, &button_refresh, &button_done}); button_refresh.on_select = [this](Button&) { this->refresh(); }; @@ -2633,7 +2637,7 @@ void PralineClockDebugView::refresh() { uint8_t output_en = portapack::clock_manager.si5351_read_register(3); const char* ma_lookup[] = {"2m", "4m", "6m", "8m"}; - for (size_t i = 0; i < 6; i++) { + for (size_t i = 0; i < 8; i++) { uint8_t ctrl = portapack::clock_manager.si5351_read_register(16 + i); // mA (Bits 1:0) diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index 0160bb15f..413434a63 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -1339,7 +1339,7 @@ class PralineClockDebugView : public View { Text t2_ph{{160, 120, 48, 16}, "-"}; Text t2_st{{208, 120, 32, 16}, "-"}; - // C3: SG_CLK (Switching Regulator/Logic Sync) + // C3: SG_CLK PORT 1 (Switching Regulator/Logic Sync) Text t3_id{{0, 136, 24, 16}, "C3:"}; Text t3_ma{{32, 136, 24, 16}, "-"}; Text t3_mode{{64, 136, 48, 16}, "-"}; @@ -1363,6 +1363,22 @@ class PralineClockDebugView : public View { Text t5_ph{{160, 168, 48, 16}, "-"}; Text t5_st{{208, 168, 32, 16}, "-"}; + // C6: SG_CLK PORT 2 (Switching Regulator/Logic Sync) + Text t6_id{{0, 184, 24, 16}, "C6:"}; + Text t6_ma{{32, 184, 24, 16}, "-"}; + Text t6_mode{{64, 184, 48, 16}, "-"}; + Text t6_src{{112, 184, 32, 16}, "-"}; + Text t6_ph{{160, 184, 48, 16}, "-"}; + Text t6_st{{208, 184, 32, 16}, "-"}; + + // C7: UNUSED_CLK (Internal Expansion) + Text t7_id{{0, 200, 24, 16}, "C7:"}; + Text t7_ma{{32, 200, 24, 16}, "-"}; + Text t7_mode{{64, 200, 48, 16}, "-"}; + Text t7_src{{112, 200, 32, 16}, "-"}; + Text t7_ph{{160, 200, 48, 16}, "-"}; + Text t7_st{{208, 200, 32, 16}, "-"}; + Button button_refresh{{8, 260, 100, 24}, "Refresh"}; Button button_done{{132, 260, 100, 24}, "Done"}; }; diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index c3aa25257..b9412a2c0 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -35,14 +35,14 @@ using namespace lpc43xx; extern "C" { #include "fpga_bridge.h" } -// Need access to ssp1_arbiter from radio namespace +// Need access to ssp1_arbiter from radio namespace for FPGA related radio method dependencies. #include "radio.hpp" #endif constexpr uint32_t si5351_vco_f = 800000000; #ifdef PRALINE -constexpr uint32_t si5351_vco_afe_f = 800000000; // Optimal for 3.072 MHz sample frequencies commonly used by apps +constexpr uint32_t si5351_vco_afe_f = 800000000; // If necessary may be changed to 768 MHz for optimal for 3.072 MHz sample frequencies commonly used by apps #endif constexpr si5351::Inputs si5351_inputs{ @@ -67,23 +67,43 @@ constexpr si5351::PLL si5351_pll_xtal_25m{ .c = 1, }; -// PLL A registers (Base 26) -constexpr auto si5351_pll_a_xtal_reg = si5351_pll_xtal_25m.reg(0); - #ifdef PRALINE +// Define pll_a from 25MHz clock for stable PLL A, and AFE locked reference // PLL A: 800 MHz VCO (32x Multiplier for jitter-free 3.072 MHz sampling) -constexpr si5351::PLL si5351_pll_xtal_800m{ - .f_in = si5351_inputs.f_xtal, - .a = 32, +constexpr si5351::PLL si5351_pll_a_afe_800m{ + .f_in = si5351_inputs.f_xtal, // 25,000,000 Hz + .a = 32, // Multiplier: 25 * 32 = 800 .b = 0, .c = 1, }; -constexpr auto si5351_pll_a_800_reg = si5351_pll_xtal_800m.reg(0); // Base 26 +// PLL A: registers (Base 34) 800 MHz VCO (For jitter-free AFE sampling frequencies) +constexpr auto si5351_pll_a_800_reg = si5351_pll_a_afe_800m.reg(0); // Base 26 -// PLL B: registers (Base 34) 800 MHz VCO (32x Multiplier for stable Digital/SGPIO bus) -constexpr auto si5351_pll_b_800_reg = si5351_pll_xtal_25m.reg(1); // Base 34 -constexpr auto si5351_pll_b_xtal_reg = si5351_pll_xtal_25m.reg(1); +static_assert(si5351_pll_a_afe_800m.f_vco() == si5351_vco_f, "PLL A XTAL frequency wrong"); +static_assert(si5351_pll_a_afe_800m.p1() == 3584, "PLL A XTAL P1 wrong"); +static_assert(si5351_pll_a_afe_800m.p2() == 0, "PLL A XTAL P2 wrong"); +static_assert(si5351_pll_a_afe_800m.p3() == 1, "PLL A XTAL P3 wrong"); +// Define pll_b 25MHz clock for stable PLL B +// PLL B: 800 MHz VCO (32x Multiplier for (For stable Digital/SGPIO bus) +constexpr si5351::PLL si5351_pll_b_800m{ + .f_in = si5351_inputs.f_xtal, // 25,000,000 Hz + .a = 32, // Multiplier: 25 * 32 = 800 + .b = 0, + .c = 1, +}; +// PLL B: registers (Base 34) 800 MHz VCO (For stable Digital/SGPIO bus) +constexpr auto si5351_pll_b_800_reg = si5351_pll_b_800m.reg(1); // Base 34 + +static_assert(si5351_pll_b_800m.f_vco() == si5351_vco_f, "PLL B XTAL frequency wrong"); +static_assert(si5351_pll_b_800m.p1() == 3584, "PLL B XTAL P1 wrong"); +static_assert(si5351_pll_b_800m.p2() == 0, "PLL B XTAL P2 wrong"); +static_assert(si5351_pll_b_800m.p3() == 1, "PLL B XTAL P3 wrong"); + +#else + +// PLL A registers (Base 26) +constexpr auto si5351_pll_a_xtal_reg = si5351_pll_xtal_25m.reg(0); static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong"); static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong"); static_assert(si5351_pll_xtal_25m.p2() == 0, "PLL XTAL P2 wrong"); @@ -99,13 +119,6 @@ constexpr si5351::PLL si5351_pll_clkin_10m{ constexpr auto si5351c_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1); constexpr auto si5351a_pll_a_clkin_reg = si5351_pll_clkin_10m.reg(0); -#ifndef PRALINE -static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong"); -static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong"); -static_assert(si5351_pll_xtal_25m.p2() == 0, "PLL XTAL P2 wrong"); -static_assert(si5351_pll_xtal_25m.p3() == 1, "PLL XTAL P3 wrong"); -#endif - static_assert(si5351_pll_clkin_10m.f_vco() == si5351_vco_f, "PLL CLKIN frequency wrong"); static_assert(si5351_pll_clkin_10m.p1() == 9728, "PLL CLKIN P1 wrong"); static_assert(si5351_pll_clkin_10m.p2() == 0, "PLL CLKIN P2 wrong"); @@ -153,6 +166,26 @@ constexpr si5351::MultisynthFractional si5351_ms_afe_10m{ .b = 0, .c = 1, .r_div = 0}; + +// (20 MHz from 800 MHz VCO: 800 / 40 = 20) +constexpr si5351::MultisynthFractional si5351_ms_afe_20m{ + .f_src = si5351_vco_afe_f, + .a = 40, + .b = 0, + .c = 1, + .r_div = 0, +}; + +// (25 MHz from 800 MHz VCO: 800 / 32 = 25) +// Define xtal MultiSynth 25MHz clock for stable PLL A, and AFE locked XTAL reference +constexpr si5351::MultisynthFractional si5351_ms_afe_25m{ + .f_src = si5351_vco_afe_f, // 800,000,000 Hz + .a = 32, // 800 / 32 = 25 MHz + .b = 0, + .c = 1, + .r_div = 0 // No final bit-shifting +}; + // (40 MHz from 800 MHz VCO: 800 / 20 = 40) constexpr si5351::MultisynthFractional si5351_ms_afe_40m{ .f_src = si5351_vco_afe_f, // 800 MHz @@ -170,6 +203,17 @@ constexpr si5351::MultisynthFractional si5351_ms_20m{ .r_div = 0, }; // constexpr auto si5351_ms_20m_reg = si5351_ms_20m.reg(0); + +// (25 MHz from 800 MHz VCO: 800 / 32 = 25) +// Define xtal MultiSynth 25MHz clock for stable PLL B, and XTAL reference +constexpr si5351::MultisynthFractional si5351_ms_25m{ + .f_src = si5351_vco_f, // 800,000,000 Hz + .a = 32, // 800 / 32 = 25 MHz + .b = 0, + .c = 1, + .r_div = 0 // No final bit-shifting +}; + #endif constexpr auto si5351c_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_og_codec); @@ -294,19 +338,21 @@ constexpr ClockControls si5351c_clock_control_common{{ constexpr ClockControls si5351a_clock_control_common{{ #ifdef PRALINE - // CLK0: MAX5864 (ADC) - 4mA, Normal PLLA Integer (Standard for Praline sync) + // CLK0: MAX5864 (ADC) - 4mA, Normal PLLA Integer {ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, - // CLK1: SCT_CLK (iCE40 FPGA) - 6mA, PLLA Normal Integer - {ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, - // CLK2: LPC43xx MCU - 4mA, Normal PLLB (Must be Integer for MCU stability) + // CLK1: SCT_CLK (iCE40 FPGA) - 4mA, Normal PLLB Integer {ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLB, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, - // CLK3: CLKOUT SMA Port P1 - 8mA, Normal PLLB Integer - {ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLB, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, - // CLK4: MAX2831 reference (40 MHz) - Inverted PLLA Integer (Required for mixer lock) + // CLK2: LPC43xx MCU - 2mA, Normal PLLA (Must be Integer for MCU stability) + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, + // CLK3: CLKOUT SMA Port P1 - 2mA, Normal PLLB Integer Power_Off + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLB, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + // CLK4: MAX2831 reference (40 MHz) - 4mA, Invert PLLA Integer (Required for mixer lock) {ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, - // CLK5: RFFC5072 reference (40 MHz) - Inverted PLLA Integer (Required for mixer lock) - {ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, - // CLK6: Not used (disabled) 2mA, Normal PLLB, Power_Off + // CLK5: RFFC5072 reference (40 MHz) - 4mA, Invert PLLA Integer (Required for mixer lock) + {ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On}, + // CLK6: Not used (disabled) 2mA, Normal PLLB Integer, Power_Off + {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLB, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, + // CLK7: Not used (disabled) 2mA, Normal PLLB Integer, Power_Off {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLB, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, #else {ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, @@ -319,9 +365,9 @@ constexpr ClockControls si5351a_clock_control_common{{ {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, // CLK6: Not used {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, -#endif // CLK7: Not used {ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off}, +#endif }}; @@ -461,20 +507,24 @@ void ClockManager::init_clock_generator() { * - Note: Timing for SGPIO data bus; scales to 2x SR in wideband modes. * * CLK2: MCU_CLKIN (LPC43xx MCU External Clock Input) * - Note: Synchronizes MCU processing to the RF clock tree. - * * CLK3: SG_CLK (Switching Regulator/Internal Logic Sync) + * * CLK3: SG_CLK (Switching Regulator/Internal Logic Sync) SMA Port 1 * - Note: Used for internal FPGA logic/gateware synchronization. - * * CLK4: P_CLK (Peripheral/Expansion Clock) + * * CLK4: P_CLK (MAX2831 Peripheral/Expansion Clock) * - Note: Routed to expansion headers for external hardware sync. - * * CLK5: AUX_CLK (Auxiliary reference for secondary logic) + * * CLK5: AUX_CLK (RFFC5371 Auxiliary reference for secondary logic) * - Note: Provides additional timing flexibility for the iCE40 FPGA. - * * CLK6/7: Unused / Power-Down + * * CLK6: SG_CLK (Switching Regulator/Internal Logic Sync) SMA Port 2 + * - Note: Used for internal FPGA logic/gateware synchronization. + * * CLK7: Unused / Power-Down * - State: Disabled (si5351a_ms6_7_off_reg) * - Note: Kept OFF to reduce EMI/RFI near the RF front-end. * * CLKOUT: Optional external clock output on the header. */ - /* Step 1: Write PLL A (800 MHz for RF) and PLL B (800 MHz for Digital) */ - /* Use single-byte writes to debug I2C issues */ + /* Write PLL A (800 MHz based on 25 MHz xtal for RF) and + * PLL B (800 MHZ based on 25 MHz xtalfor Digital) + * Use single-byte writes to debug I2C issues + */ { // Write PLLA (Registers 26-33) /* Write PLL A configuration (Base 26) */ @@ -484,28 +534,31 @@ void ClockManager::init_clock_generator() { } // Write PLLB (Registers 34-41) + /* Write PLL B configuration (Base 34) */ const auto& pll_b = si5351_pll_b_800_reg; for (size_t i = 1; i < pll_b.size(); i++) { clock_generator.write_register(pll_b[0] + i - 1, pll_b[i]); } } - /* Step 2: Write multisynth configurations using single-byte writes */ + /* Write multisynth configurations using single-byte writes */ // These cover all active channels on the Praline board - clock_generator.write_ms_single_byte(0, si5351_ms_afe_40m); // CLK0: PLL A Codec (40 MHz) - clock_generator.write_ms_single_byte(1, si5351_ms_afe_40m); // CLK1: PLL A FPGA Timing (40 MHz) - clock_generator.write_ms_single_byte(2, si5351_ms_40m); // CLK2: PLL B MCU Input (40 MHz) - clock_generator.write_ms_single_byte(3, si5351_ms_10m); // CLK3: PLL B Logic Sync (10 MHz) - clock_generator.write_ms_single_byte(4, si5351_ms_afe_40m); // CLK4: PLL A Second IF (40 MHz) - clock_generator.write_ms_single_byte(5, si5351_ms_afe_40m); // CLK5: PLL A First IF (40 MHz) + clock_generator.write_ms_single_byte(0, si5351_ms_afe_4m); // CLK0: PLL A AFE Codec (4 MHz) + clock_generator.write_ms_single_byte(1, si5351_ms_10m); // CLK1: PLL B SGPIO/FPGA Timing (10 MHz) + clock_generator.write_ms_single_byte(2, si5351_ms_afe_40m); // CLK2: PLL A Audio and MCU Input (40 MHz) + clock_generator.write_ms_single_byte(3, si5351_ms_0_4m); // CLK3: PLL B SMA Port 1 Logic Sync (4 MHz or 10 MHz) + clock_generator.write_ms_single_byte(4, si5351_ms_afe_40m); // CLK4: PLL A MAX2831 Second IF (40 MHz) + clock_generator.write_ms_single_byte(5, si5351_ms_afe_40m); // CLK5: PLL A RFFC5071First IF (40 MHz) + clock_generator.write_ms_single_byte(6, si5351_ms_0_4m); // CLK6: PLL B SMA Port 2 Logic Sync (4 MHz or 10 MHz) + clock_generator.write_ms_single_byte(7, si5351_ms_0_4m); // CLK7: PLL B Unused (4 MHz) - /* Step 3: NOW set clock control registers (AFTER multisynths per HackRF reference) */ + /* NOW set clock control registers (AFTER multisynths per HackRF reference) */ const auto ref_pll_a = ClockControl::MultiSynthSource::PLLA; const auto ref_pll_b = ClockControl::MultiSynthSource::PLLB; const ClockControls si5351_clock_control = ClockControls{{ si5351a_clock_control_common[0].ms_src(ref_pll_a), - si5351a_clock_control_common[1].ms_src(ref_pll_a), - si5351a_clock_control_common[2].ms_src(ref_pll_b), + si5351a_clock_control_common[1].ms_src(ref_pll_b), + si5351a_clock_control_common[2].ms_src(ref_pll_a), si5351a_clock_control_common[3].ms_src(ref_pll_b), si5351a_clock_control_common[4].ms_src(ref_pll_a), si5351a_clock_control_common[5].ms_src(ref_pll_a), @@ -515,6 +568,16 @@ void ClockManager::init_clock_generator() { // clock_generator.set_clock_control(si5351_clock_control); // Use single-byte writes instead of multi-byte clock_generator.set_clock_control_single_byte(si5351_clock_control); + + // Don't write CLKS 3, 6, and 7 multisynth + // Ensure CLK3 clock control has Power_Off + // Verify output is disabled + clock_generator.disable_output(3); + clock_generator.disable_clock(3); + clock_generator.disable_output(6); + clock_generator.disable_clock(6); + clock_generator.disable_output(7); + clock_generator.disable_clock(7); #else if (hackrf_r9) { const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal) @@ -755,6 +818,9 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) { _resampling_n = n; + // === Stop FPGA processing and flush filters === + fpga_debug_register_write(1, 0x00); // Disable FPGA filters (resets CIC accumulators) + // Set FPGA RX decimation register fpga_debug_register_write(2, n); radio::invalidate_spi_config(); @@ -766,6 +832,17 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) { clock_generator.set_ms_frequency(0, afe_rate * 2, si5351_vco_afe_f, 1); clock_generator.set_ms_frequency(1, afe_rate * 2, si5351_vco_afe_f, 0); + // === Reset PLL A for phase alignment === + clock_generator.write_register(si5351::Register::PLLReset, 0x20); + + // Brief delay for PLL lock and clock stability === + // ~1ms at 96MHz = ~96000 cycles, use 10ms for safety + volatile uint32_t delay = 240000; // ~2.5ms + while (delay--); + + // Re-enable FPGA processing with clean state === + fpga_debug_register_write(1, 0x01); + #else /* Codec clock is at sampling frequency, CPLD and SGPIO clocks are at * twice the frequency, and derived from the MS0 synth. So it's only @@ -796,7 +873,7 @@ void ClockManager::set_reference_ppb(const int32_t ppb) { if (reference.source == ReferenceSource::External) { return; } - constexpr uint32_t pll_multiplier = si5351_pll_xtal_800m.a; + constexpr uint32_t pll_multiplier = si5351_pll_a_afe_800m.a; #else constexpr uint32_t pll_multiplier = si5351_pll_xtal_25m.a; #endif @@ -834,15 +911,15 @@ void ClockManager::start_frequency_monitor_measurement(const cgu::CLK_SEL clk_se } void ClockManager::wait_For_frequency_monitor_measurement_done() { - // FREQ_MON mechanism fails to finish if there's no clock present on selected input?! -#ifndef PRALINE - while (LPC_CGU->FREQ_MON.MEAS == 1); -#else +// FREQ_MON mechanism fails to finish if there's no clock present on selected input?! +#ifdef PRALINE // PRALINE FIX: Add timeout to prevent infinite hang uint32_t timeout = 100000; while (LPC_CGU->FREQ_MON.MEAS == 1 && timeout > 0) { timeout--; } +#else + while (LPC_CGU->FREQ_MON.MEAS == 1); #endif } @@ -855,7 +932,8 @@ uint32_t ClockManager::get_frequency_monitor_measurement_in_hertz() { void ClockManager::start_audio_pll() { #ifdef PRALINE - // Comprehensive init matches non-PRALINE for stability and source selection + + // Control Block cgu::pll0audio::ctrl({ .pd = 1, // Start powered down .bypass = 0, // Use the PLL @@ -864,19 +942,66 @@ void ClockManager::start_audio_pll() { .clken = 0, // Disable output initially .frm = 0, // Normal mode .autoblock = 1, // Glitchless switching - .pllfract_req = 0, // Integer mode - .sel_ext = 1, // MUST BE 1 to use clk_sel GP_CLKIN - .mod_pd = 1, // Power down modulator (Reduces noise/hiss) + .pllfract_req = 1, // Integer, Disabled + .sel_ext = 1, // Use GP_CLKIN (CLK2) + .mod_pd = 0, // Modulator OFF .clk_sel = cgu::CLK_SEL::GP_CLKIN, }); - cgu::pll0audio::mdiv({.mdec = 30542UL}); - cgu::pll0audio::np_div({.pdec = 31, .ndec = 45}); - cgu::pll0audio::frac({.pllfract_ctrl = 0}); + /* + * Audio PLL Configuration for 48 kHz audio with 256Fs MCLK + * Target output: Fout = 12.288 MHz + * + * Formulas: + * Fout = Fin × MSEL / (NSEL × PSEL) + * FCO = 2 × Fin × MSEL / NSEL (must be 275-550 MHz) + */ + /* + * ┌─────────────────────────────────────────────────────────────────┐ + * │ 10 MHz INPUT (HackRF r9 compatible but interfere with fm band) │ + * ├─────────────────────────────────────────────────────────────────┤ + * │ MSEL=3072, NSEL=125, PSEL=20 │ + * │ Fout = 10 × 3072 / (125 × 20) = 30720 / 2500 = 12.288 MHz ✓ │ + * │ FCO = 2 × 10 × 3072 / 125 = 61440 / 125 = 491.52 MHz ✓ │ + * │ │ + * │ Encoded values: MDEC=8308, NDEC=45, PDEC=31 │ + * │ CLK2 harmonics: 90, 100, 110 MHz (interfere with FM band!) │ + * └─────────────────────────────────────────────────────────────────┘ + */ + + /* + // Math: (10 MHz * 3072) / (125 * 20) * 2 = 12.288MHz + cgu::pll0audio::mdiv({.mdec = 8308UL}); // MSEL = 3072 + cgu::pll0audio::np_div({ + .pdec = 31, // PSEL = 20 for 10 MHz + .ndec = 45 // NSEL = 125 for 10 MHz + }); + */ + + /* + * ┌─────────────────────────────────────────────────────────────────┐ + * │ 40 MHz INPUT (Recommended - avoids FM band harmonics) │ + * ├─────────────────────────────────────────────────────────────────┤ + * │ MSEL=768, NSEL=125, PSEL=20 │ + * │ Fout = 40 × 768 / (125 × 20) = 30720 / 2500 = 12.288 MHz ✓ │ + * │ FCO = 2 × 40 × 768 / 125 = 61440 / 125 = 491.52 MHz ✓ │ + * │ │ + * │ Encoded values: MDEC=30542, NDEC=45, PDEC=31 │ + * │ CLK2 harmonics: 80, 120, 160 MHz (none in FM 88-108 MHz band) │ + * └─────────────────────────────────────────────────────────────────┘ + */ + + // 40 MHz input → 12.288 MHz output (same as HackRF OG) + // Math: (40MHz * 768) / (125 * 20) * 2 = 12.288MHz + cgu::pll0audio::mdiv({.mdec = 30542UL}); // MSEL = 768 + cgu::pll0audio::np_div({.pdec = 31, + .ndec = 45}); + + cgu::pll0audio::frac({.pllfract_ctrl = 0}); cgu::pll0audio::power_up(); - // Safety timeout prevents boot hang if Si5351 clock is missing + // Lock and Routing (Keep as is) { uint32_t timeout = 100000; while (!cgu::pll0audio::is_locked() && timeout > 0) { @@ -889,6 +1014,7 @@ void ClockManager::start_audio_pll() { LPC_CGU->BASE_AUDIO_CLK.AUTOBLOCK = 1; LPC_CGU->BASE_AUDIO_CLK.CLK_SEL = toUType(cgu::CLK_SEL::IDIVD); + #else cgu::pll0audio::ctrl({ .pd = 1, diff --git a/firmware/application/hw/si5351.hpp b/firmware/application/hw/si5351.hpp index c459ac1ac..416f92852 100644 --- a/firmware/application/hw/si5351.hpp +++ b/firmware/application/hw/si5351.hpp @@ -361,15 +361,18 @@ class Si5351 { } void wait_for_device_ready() { -#ifndef PRALINE - while (device_status() & 0x80); -#else +#ifdef PRALINE // Add timeout to prevent infinite loop if I2C communication fails // (e.g., on PRALINE hardware with different configuration) uint32_t timeout = 100000; while ((device_status() & 0x80) && (timeout > 0)) { timeout--; + if (timeout == 0) { + chDbgAssert(false, "si5351: wait_for_device_ready timeout", nullptr); + } } +#else + while (device_status() & 0x80); #endif } @@ -396,13 +399,14 @@ class Si5351 { } void reset_plls() { -#ifndef PRALINE - // Datasheet recommends value 0xac, though the low nibble bits are not defined in AN619. - write_register(Register::PLLReset, 0xac); -#else +#ifdef PRALINE // Reset both PLLA and PLLB. Use 0xA0 to match HackRF reference firmware. // The low nibble bits are reserved/undefined in AN619. write_register(Register::PLLReset, 0xa0); +#else + // Datasheet recommends value 0xac, though the low nibble bits are not defined in AN619. + write_register(Register::PLLReset, 0xac); + #endif } regvalue_t read_register(const uint8_t reg); diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index a18fcbb64..67f4faf8d 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -392,18 +392,8 @@ static void set_cpu_clock_speed() { #endif cgu::pll1::enable(); -#ifndef PRALINE - while (!cgu::pll1::is_locked()); - set_clock_config(clock_config_pll1_step); - /* Delay >50us at 90-110MHz clock speed */ - volatile uint32_t delay = 1400; - while (delay--); - set_clock_config(clock_config_pll1); - - /* Remove /2P divider from PLL1 output to achieve full speed */ - cgu::pll1::direct(); -#else +#ifdef PRALINE // Wait for PLL1 to lock with timeout { uint32_t timeout = 100000; @@ -423,6 +413,18 @@ static void set_cpu_clock_speed() { /* Remove /2P divider from PLL1 output to achieve full speed */ cgu::pll1::direct(); } +#else + while (!cgu::pll1::is_locked()); + + set_clock_config(clock_config_pll1_step); + /* Delay >50us at 90-110MHz clock speed */ + volatile uint32_t delay = 1400; + while (delay--); + set_clock_config(clock_config_pll1); + + /* Remove /2P divider from PLL1 output to achieve full speed */ + cgu::pll1::direct(); + #endif } @@ -660,6 +662,7 @@ init_status_t init() { draw_splash_screen_icon(2, ui::bitmap_icon_sd); init_status_t return_code = init_status_t::INIT_SUCCESS; + #ifndef PRALINE // HackRF One uses CPLD - load it via JTAG if (!hackrf::cpld::load_sram()) { diff --git a/firmware/application/rf_path.cpp b/firmware/application/rf_path.cpp index 0df0745ab..45e655b09 100644 --- a/firmware/application/rf_path.cpp +++ b/firmware/application/rf_path.cpp @@ -83,9 +83,7 @@ constexpr GPIOs gpios{ gpio_rx_amp, gpio_not_rx_amp_pwr, }; -#endif -#ifndef PRALINE /* HackRF One Config struct - not used on PRALINE */ struct Config { using base_type = uint16_t; @@ -186,9 +184,7 @@ struct Config { } } }; -#endif /* !PRALINE */ -#ifndef PRALINE /* HackRF One config table - not used on PRALINE */ using ConfigAmp = std::array; using ConfigDirection = std::array; @@ -229,7 +225,7 @@ constexpr Config get_config( const bool amplify) { return config_table[toUType(band)][toUType(direction)][amplify ? 1 : 0]; } -#endif /* !PRALINE */ +#endif /* PRALINE */ } /* namespace */ diff --git a/firmware/baseband/proc_am_audio.hpp b/firmware/baseband/proc_am_audio.hpp index 635f96289..296094889 100644 --- a/firmware/baseband/proc_am_audio.hpp +++ b/firmware/baseband/proc_am_audio.hpp @@ -76,13 +76,13 @@ class NarrowbandAMAudio : public BasebandProcessor { SpectrumCollector channel_spectrum{}; /* NB: Threads should be the last members in the class definition. */ -#ifndef PRALINE - BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive}; - RSSIThread rssi_thread{}; -#else +#ifdef PRALINE BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive, /*auto_start*/ false}; // Phase 2: Manual start RSSIThread rssi_thread{/*auto_start*/ false}; // Phase 2: Manual start +#else + BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive}; + RSSIThread rssi_thread{}; #endif void configure(const AMConfigureMessage& message); diff --git a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp index 5a738b482..6ff23fb33 100755 --- a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp +++ b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp @@ -867,33 +867,8 @@ extern "C" void __late_init(void) { * @todo Add your board-specific code, if any. */ extern "C" void boardInit(void) { -#ifndef PRALINE - /* Detect HackRF variant */ - /* 1. Perform Standard Initialization first */ - /* This configures VAA power, LED pins, and detects board revision */ - /* Let detect_hackrf_r9() run - don't force for PRALINE */ - hackrf_r9 = detect_hackrf_r9(); - /* Configure variant-dependent pins. */ - if (hackrf_r9) { - setup_gpios(gpio_setup_r9); - setup_pins(pins_setup_r9); - } else { - setup_gpios(gpio_setup_og); - setup_pins(pins_setup_og); - } - - /* 2. Turn on VAA (Critical for Radio/Transceiver) */ - vaa_power_on(); - - /* 3. Handle VAA Enable Pin Latching */ - if (hackrf_r9) { - LPC_GPIO->W2[9] = 1; - } else { - LPC_GPIO->W3[6] = 1; - } - - /* 4. HackRF Pro Specific: Initialize and Load FPGA */ -#else +#ifdef PRALINE + /* HackRF Pro Specific: Initialize and Load FPGA */ hackrf_r9 = false; /* Enable 3.3V aux power - P6_7 = GPIO5[15], active LOW (clear to enable) */ LPC_SCU->SFSP[6][7] = 0xF4; /* SCU_GPIO_FAST | FUNCTION4 */ @@ -1048,6 +1023,32 @@ extern "C" void boardInit(void) { LPC_GPIO->SET[2] = (1 << 1) | (1 << 2) | (1 << 8); { volatile uint32_t delay = 200000; while(delay--); } +#else + + /* Detect HackRF variant */ + /* 1. Perform Standard Initialization first */ + /* This configures VAA power, LED pins, and detects board revision */ + /* Let detect_hackrf_r9() run - don't force for PRALINE */ + hackrf_r9 = detect_hackrf_r9(); + /* Configure variant-dependent pins. */ + if (hackrf_r9) { + setup_gpios(gpio_setup_r9); + setup_pins(pins_setup_r9); + } else { + setup_gpios(gpio_setup_og); + setup_pins(pins_setup_og); + } + + /* 2. Turn on VAA (Critical for Radio/Transceiver) */ + vaa_power_on(); + + /* 3. Handle VAA Enable Pin Latching */ + if (hackrf_r9) { + LPC_GPIO->W2[9] = 1; + } else { + LPC_GPIO->W3[6] = 1; + } + #endif } diff --git a/firmware/common/baseband_sgpio.cpp b/firmware/common/baseband_sgpio.cpp index 9d6b78131..3b90b918e 100644 --- a/firmware/common/baseband_sgpio.cpp +++ b/firmware/common/baseband_sgpio.cpp @@ -199,22 +199,22 @@ constexpr uint32_t out_mux_cfg(const P_OUT_CFG out, const P_OE_CFG oe) { constexpr uint32_t data_sgpio_mux_cfg( const CONCAT_ENABLE concat_enable, const CONCAT_ORDER concat_order) { -#ifndef PRALINE - return (1U << 0) | (0U << 1) | (0U << 3) | (3U << 5) | (1U << 7) | (0U << 9) | (toUType(concat_enable) << 11) | (toUType(concat_order) << 12); -#else +#ifdef PRALINE return (1U << 0) | (0U << 1) | (3U << 3) | (3U << 5) | (1U << 7) | (0U << 9) | (toUType(concat_enable) << 11) | (toUType(concat_order) << 12); // Bits 3-4: CLK_SOURCE_SLICE_MODE = 3 (slice D as clock source for data slices) +#else + return (1U << 0) | (0U << 1) | (0U << 3) | (3U << 5) | (1U << 7) | (0U << 9) | (toUType(concat_enable) << 11) | (toUType(concat_order) << 12); #endif } constexpr uint32_t data_slice_mux_cfg( const PARALLEL_MODE parallel_mode, const CLK_CAPTURE_MODE clk_capture_mode) { -#ifndef PRALINE - return (0U << 0) | (toUType(clk_capture_mode) << 1) | (1U << 2) | (0U << 3) | (0U << 4) | (toUType(parallel_mode) << 6) | (0U << 8); -#else +#ifdef PRALINE return (0U << 0) | (toUType(clk_capture_mode) << 1) | (1U << 2) | (0U << 3) | (1U << 4) | (toUType(parallel_mode) << 6) | (0U << 8); // Bit 4 CLKGEN_MODE: 0=internal counter, 1=external clock (REQUIRED for PRALINE!) +#else + return (0U << 0) | (toUType(clk_capture_mode) << 1) | (1U << 2) | (0U << 3) | (0U << 4) | (toUType(parallel_mode) << 6) | (0U << 8); #endif } @@ -330,10 +330,6 @@ void SGPIO::configure(const Direction direction) { const auto clk_capture_mode = data_clk_capture_mode(direction); const auto single_slice = !slice_mode_multislice; -#ifndef PRALINE - uint32_t slice_enable_mask = 0; -#endif - #ifdef PRALINE // Configure slice D as clock generator (REQUIRED for PRALINE!) // Reference: HackRF sgpio.c line 193 @@ -347,6 +343,8 @@ void SGPIO::configure(const Direction direction) { LPC_SGPIO->REG_SS[slice_d] = 0x11111111; uint32_t slice_enable_mask = (1U << slice_d); // Start with slice D enabled +#else + uint32_t slice_enable_mask = 0; #endif for (size_t i = 0; i < slice_count; i++) {