diff --git a/firmware/common/fixed_point.h b/firmware/common/fixed_point.h new file mode 100644 index 00000000..9485aaea --- /dev/null +++ b/firmware/common/fixed_point.h @@ -0,0 +1,34 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __FIXED_POINT_H__ +#define __FIXED_POINT_H__ + +/* 40.24 fixed-point */ +typedef uint64_t fp_40_24_t; + +/* one million in 40.24 fixed-point */ +#define FP_ONE_MHZ ((1000ULL * 1000ULL) << 24) + +/* one in 40.24 fixed-point */ +#define FP_ONE_HZ (1 << 24) + +#endif /*__FIXED_POINT_H__*/ diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c index f3bb01c1..1df90daf 100644 --- a/firmware/common/fpga_selftest.c +++ b/firmware/common/fpga_selftest.c @@ -190,7 +190,7 @@ bool fpga_if_xcvr_selftest(void) max2831_set_frequency(&max283x, 2500000000); // Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF - sample_rate_frac_set(4000000, 1); + sample_rate_set(4ULL * FP_ONE_MHZ, true); delay_us_at_mhz(1000, 204); if (rx_samples(num_samples, 2000000) == -1) { timeout = true; @@ -213,7 +213,7 @@ bool fpga_if_xcvr_selftest(void) // Capture 3: 20 Msps, tone at 5 MHz, narrowband filter OFF fpga_set_tx_nco_pstep(&fpga, 255); - sample_rate_frac_set(20000000, 1); + sample_rate_set(20ULL * FP_ONE_MHZ, true); narrowband_filter_set(0); delay_us_at_mhz(1000, 204); if (rx_samples(num_samples, 2000000) == -1) { @@ -236,7 +236,7 @@ bool fpga_if_xcvr_selftest(void) &selftest.xcvr_measurements[3]); // Restore default settings. - sample_rate_set(10000000); + sample_rate_set(10ULL * FP_ONE_MHZ, true); rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_OFF); narrowband_filter_set(0); fpga_init(&fpga); diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 1b60b13f..383e5540 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -351,7 +351,9 @@ fpga_driver_t fpga = { }; #endif -radio_t radio; +radio_t radio = { + .sample_rate_cb = sample_rate_set, +}; rf_path_t rf_path = { .switchctrl = 0, @@ -409,53 +411,26 @@ jtag_t jtag_cpld = { .gpio = &jtag_gpio_cpld, }; -/* GCD algo from wikipedia */ -/* http://en.wikipedia.org/wiki/Greatest_common_divisor */ -static uint32_t gcd(uint32_t u, uint32_t v) +/* + * Configure clock generator to produce sample clock in units of 1/(2**24) Hz. + * Can be called with program=false for a dry run that returns the resultant + * frequency without actually configuring the clock generator. + * + * The clock generator output frequency is: + * + * fs = 128 * vco / (512 + p1 + p2/p3)) + * + * where p1, p2, and p3 are register values. + * + * For more information see: + * https://www.pa3fwm.nl/technotes/tn42a-si5351-programming.html + */ +fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program) { - int s; - - if (!u || !v) { - return u | v; - } - - for (s = 0; !((u | v) & 1); s++) { - u >>= 1; - v >>= 1; - } - - while (!(u & 1)) { - u >>= 1; - } - - do { - while (!(v & 1)) { - v >>= 1; - } - - if (u > v) { - uint32_t t; - t = v; - v = u; - u = t; - } - - v = v - u; - } while (v); - - return u << s; -} - -bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) -{ - const uint64_t VCO_FREQ = 800 * 1000 * 1000; /* 800 MHz */ - uint32_t MSx_P1, MSx_P2, MSx_P3; - uint32_t a, b, c; - uint32_t rem; - - /* Round to the nearest Hz for display. */ - uint32_t rate_hz = (rate_num + (rate_denom >> 1)) / rate_denom; - hackrf_ui()->set_sample_rate(rate_hz); + const fp_40_24_t vco = 800 * FP_ONE_MHZ; + uint64_t p1, p2, p3; + uint64_t n, d, q; + fp_40_24_t remainder, resultant_rate; /* * First double the sample rate so that we can produce a clock at twice @@ -463,36 +438,72 @@ bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) * and it is divided by two in an output divider to produce the actual * AFE clock. */ - rate_num *= 2; + fp_40_24_t rate = sample_rate * 2; - /* Find best config */ - a = (VCO_FREQ * rate_denom) / rate_num; + p1 = ((128 * vco) / rate) - 512; + if (vco % rate) { + /* + * Compute numerator (p2) for denominator (p3) matching + * fixed-point type. + */ + n = (128 * vco) - (rate * (p1 + 512)); + d = rate / FP_ONE_HZ; + n += (d / 2); + p2 = n / d; + p3 = 1 << 24; - rem = (VCO_FREQ * rate_denom) - (a * rate_num); + /* Reduce fraction. p3 is 1<<24, so gcd(p2, p3) is a power of 2 */ + unsigned int shift = p2 ? __builtin_ctz(p2) : 24; + p2 >>= shift; + p3 >>= shift; - if (!rem) { - /* Integer mode */ - b = 0; - c = 1; - } else { - /* Fractional */ - uint32_t g = gcd(rem, rate_num); - rem /= g; - rate_num /= g; - - if (rate_num < (1 << 20)) { - /* Perfect match */ - b = rem; - c = rate_num; - } else { - /* Approximate */ - c = (1 << 20) - 1; - b = ((uint64_t) c * (uint64_t) rem) / rate_num; - - g = gcd(b, c); - b /= g; - c /= g; + /* Convert fraction to valid denominator. */ + const uint64_t p3_max = 0xfffff; + if (p3 > p3_max) { + p2 *= p3_max; + p2 += (p3 / 2); + p2 /= p3; + p3 = p3_max; } + + /* Roll over to next p1 to enable integer mode. */ + if (p2 >= p3) { + p1++; + p2 = 0; + } + } else { + p2 = 0; + } + + /* Maximum: (128 * 2048) - 512 */ + if (p1 > 0x3fe00) { + p1 = 0x3fe00; + p2 = 0; + } + + if (p2 == 0) { + /* Use unity denominator for integer mode. */ + p3 = 1; + n = (vco * 128); + d = (p1 + 512); + n += (d / 2); + resultant_rate = n / d; + } else { + const uint64_t vco_hz = vco / FP_ONE_HZ; + n = p3 * vco_hz * 128; + d = p3 * (p1 + 512) + p2; + const uint64_t rate_hz = n / d; + remainder = (n - (d * rate_hz)) * FP_ONE_HZ; + remainder += (d / 2); + q = remainder / d; + resultant_rate = (rate_hz * FP_ONE_HZ) + q; + } + + /* Return MCU sample rate, not AFE clock rate. */ + resultant_rate = (resultant_rate + 1) / 2; + + if (!program) { + return resultant_rate; } bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); @@ -501,103 +512,13 @@ bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) sgpio_cpld_stream_disable(&sgpio_config); } - /* Can we enable integer mode ? */ - if (a & 0x1 || b) { + /* Integer mode can be enabled if p1 is even and p2 is zero. */ + if (p1 & 0x1 || p2) { si5351c_set_int_mode(&clock_gen, 0, 0); } else { si5351c_set_int_mode(&clock_gen, 0, 1); } - /* Final MS values */ - MSx_P1 = 128 * a + (128 * b / c) - 512; - MSx_P2 = (128 * b) % c; - MSx_P3 = c; - -#ifndef PRALINE - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* - * On HackRF One r9 all sample clocks are externally derived - * from MS1/CLK1 operating at twice the sample rate. - */ - si5351c_configure_multisynth(&clock_gen, 1, MSx_P1, MSx_P2, MSx_P3, 0); - } else { - /* - * On other platforms the clock generator produces three - * different sample clocks, all derived from multisynth 0. - */ - /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1); - - /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter - - /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ - si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter - } -#else - /* MS0/CLK0 is the source for the MAX5864/FPGA (AFE_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1); -#endif - - if (streaming) { - sgpio_cpld_stream_enable(&sgpio_config); - } - - return true; -} - -bool sample_rate_set(const uint32_t sample_rate_hz) -{ - uint32_t p1 = 4608; - uint32_t p2 = 0; - uint32_t p3 = 0; - - switch (sample_rate_hz) { - case 8000000: - p1 = SI_INTDIV(50); // 800MHz / 50 = 16 MHz (SGPIO), 8 MHz (codec) - break; - - case 9216000: - // 43.40277777777778: a = 43; b = 29; c = 72 - p1 = 5043; - p2 = 40; - p3 = 72; - break; - - case 10000000: - p1 = SI_INTDIV(40); // 800MHz / 40 = 20 MHz (SGPIO), 10 MHz (codec) - break; - - case 12288000: - // 32.552083333333336: a = 32; b = 159; c = 288 - p1 = 3654; - p2 = 192; - p3 = 288; - break; - - case 12500000: - p1 = SI_INTDIV(32); // 800MHz / 32 = 25 MHz (SGPIO), 12.5 MHz (codec) - break; - - case 16000000: - p1 = SI_INTDIV(25); // 800MHz / 25 = 32 MHz (SGPIO), 16 MHz (codec) - break; - - case 18432000: - // 21.70138888889: a = 21; b = 101; c = 144 - p1 = 2265; - p2 = 112; - p3 = 144; - break; - - case 20000000: - p1 = SI_INTDIV(20); // 800MHz / 20 = 40 MHz (SGPIO), 20 MHz (codec) - break; - - default: - return false; - } - #ifndef PRALINE if (detected_platform() == BOARD_ID_HACKRF1_R9) { /* @@ -614,32 +535,21 @@ bool sample_rate_set(const uint32_t sample_rate_hz) si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ - si5351c_configure_multisynth( - &clock_gen, - 1, - p1, - 0, - 1, - 0); //p1 doesn't matter + si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ - si5351c_configure_multisynth( - &clock_gen, - 2, - p1, - 0, - 1, - 0); //p1 doesn't matter + si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter } #else /* MS0/CLK0 is the source for the MAX5864/FPGA (AFE_CLK). */ si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); - - /* MS0/CLK1 is the source for SCT_CLK (CODEC_X2_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); #endif - return true; + if (streaming) { + sgpio_cpld_stream_enable(&sgpio_config); + } + + return resultant_rate; } /* @@ -926,7 +836,7 @@ void clock_gen_init(void) /* MS7/CLK7 is unused. */ /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ - sample_rate_set(10000000); + sample_rate_set(10ULL * FP_ONE_MHZ, true); si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); // soft reset diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 6bc8e909..d3a7b0d6 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -45,6 +45,7 @@ extern "C" { #include "cpld_jtag.h" #include "ice40_spi.h" #include "fpga.h" +#include "fixed_point.h" /* * SCU PinMux @@ -435,8 +436,7 @@ void enable_1v8_power(void); void disable_1v8_power(void); #endif -bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom); -bool sample_rate_set(const uint32_t sampling_rate_hz); +fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program); clock_source_t activate_best_clock_source(void); diff --git a/firmware/common/max2831.c b/firmware/common/max2831.c index 6f641f54..efae9eb6 100644 --- a/firmware/common/max2831.c +++ b/firmware/common/max2831.c @@ -150,7 +150,7 @@ void max2831_set_mode(max2831_driver_t* const drv, const max2831_mode_t new_mode } drv->set_mode(drv, new_mode); - max2831_set_lpf_bandwidth(drv, drv->desired_lpf_bw); + max2831_set_lpf_bandwidth(drv, new_mode, drv->desired_lpf_bw); } max2831_mode_t max2831_mode(max2831_driver_t* const drv) @@ -304,13 +304,13 @@ static const max2831_ft_fine_t max2831_tx_ft_fine[] = { //clang-format on -uint32_t max2831_set_lpf_bandwidth(max2831_driver_t* const drv, const uint32_t bandwidth_hz) { +uint32_t max2831_set_lpf_bandwidth(max2831_driver_t* const drv, const max2831_mode_t mode, const uint32_t bandwidth_hz) { const max2831_ft_t* coarse; const max2831_ft_fine_t* fine; drv->desired_lpf_bw = bandwidth_hz; - if (drv->mode == MAX2831_MODE_RX) { + if (mode == MAX2831_MODE_RX) { coarse = max2831_rx_ft; fine = max2831_rx_ft_fine; } else { @@ -343,7 +343,7 @@ uint32_t max2831_set_lpf_bandwidth(max2831_driver_t* const drv, const uint32_t b /* Program found settings. */ set_MAX2831_LPF_COARSE(drv, coarse->ft); - if (drv->mode == MAX2831_MODE_RX) { + if (mode == MAX2831_MODE_RX) { set_MAX2831_RX_LPF_FINE_ADJ(drv, f->ft_fine); } else { set_MAX2831_TX_LPF_FINE_ADJ(drv, f->ft_fine); diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h index 033b590e..c60b6a7e 100644 --- a/firmware/common/max2831.h +++ b/firmware/common/max2831.h @@ -93,6 +93,7 @@ extern void max2831_stop(max2831_driver_t* const drv); extern void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq); uint32_t max2831_set_lpf_bandwidth( max2831_driver_t* const drv, + const max2831_mode_t mode, const uint32_t bandwidth_hz); bool max2831_set_lna_gain(max2831_driver_t* const drv, const uint32_t gain_db); bool max2831_set_vga_gain(max2831_driver_t* const drv, const uint32_t gain_db); diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 482da9fb..bae631f7 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -27,6 +27,8 @@ #include "fpga.h" #include "platform_detect.h" #include "radio.h" +#include "fixed_point.h" +#include "hackrf_ui.h" #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) @@ -118,46 +120,6 @@ static bool radio_update_direction(radio_t* const radio, uint64_t* bank) return true; } -/* - * Convert sample rate in units of 1/(2**24) Hz to fraction. - */ -static inline uint64_t frac_sample_rate(const uint64_t rate) -{ - uint64_t num = rate; - uint64_t denom = 1 << 24; - while ((num & 1) == 0) { - num >>= 1; - denom >>= 1; - if (denom == 1) { - break; - } - } - return (denom << 32) | (num & 0xffffffff); -} - -static inline uint32_t numerator(const uint64_t frac) -{ - return frac & 0xffffffff; -} - -static inline uint32_t denominator(const uint64_t frac) -{ - return frac >> 32; -} - -/* - * Convert fractional sample rate to units of 1/(2**24) Hz. - */ -static inline uint64_t round_sample_rate(const uint64_t frac) -{ - uint64_t num = (uint64_t) numerator(frac) << 24; - uint32_t denom = denominator(frac); - if (denom == 0) { - denom = 1; - } - return (num + (denom >> 1)) / denom; -} - #define MAX_AFE_RATE_HZ (40000000UL) static inline uint8_t compute_resample_log( @@ -182,57 +144,55 @@ static inline uint8_t compute_resample_log( return n; } -#define MIN_MCU_RATE (200000ULL << 24) -#define MAX_MCU_RATE (21800000ULL << 24) -#define DEFAULT_MCU_RATE (10000000ULL << 24) +#define MIN_MCU_RATE (200000ULL * FP_ONE_HZ) +#define MAX_MCU_RATE (21800000ULL * FP_ONE_HZ) +#define DEFAULT_MCU_RATE (10000000ULL * FP_ONE_HZ) -static uint64_t applied_afe_rate = RADIO_UNSET; +static fp_40_24_t applied_afe_rate = RADIO_UNSET; static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) { - uint64_t rate, frac, previous_n; + fp_40_24_t rate, afe_rate; + uint64_t previous_n; uint8_t n = 0; + bool new_afe_rate = false; bool new_rate = false; + bool new_n = false; - const uint64_t requested_frac = bank[RADIO_SAMPLE_RATE_FRAC]; const uint64_t requested_rate = bank[RADIO_SAMPLE_RATE]; const uint64_t requested_n = bank[RADIO_RESAMPLE_RX]; if (requested_rate != RADIO_UNSET) { rate = MIN(requested_rate, MAX_MCU_RATE); rate = MAX(rate, MIN_MCU_RATE); - frac = frac_sample_rate(rate); - } else if (requested_frac != RADIO_UNSET) { - frac = requested_frac; - if (round_sample_rate(frac) > MAX_MCU_RATE) { - frac = frac_sample_rate(MAX_MCU_RATE); - } - if (round_sample_rate(frac) < MIN_MCU_RATE) { - frac = frac_sample_rate(MIN_MCU_RATE); - } - rate = round_sample_rate(frac); } else { rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]; if (rate == RADIO_UNSET) { rate = DEFAULT_MCU_RATE; } - frac = RADIO_UNSET; } + const uint64_t previous_opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; uint64_t opmode = bank[RADIO_OPMODE]; if (opmode == RADIO_UNSET) { - opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; + opmode = previous_opmode; + } + switch (previous_opmode) { + case TRANSCEIVER_MODE_TX: + case TRANSCEIVER_MODE_SS: + previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]; + break; + default: + previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]; } switch (opmode) { case TRANSCEIVER_MODE_TX: case TRANSCEIVER_MODE_SS: - previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]; - if (n != previous_n) { + if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) { #ifdef PRALINE fpga_set_tx_interpolation_ratio(&fpga, n); #endif radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX] = n; - new_rate = true; } break; default: @@ -240,38 +200,44 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) * Resampling is enabled only in RX mode to work around a * spectrum inversion bug with TX interpolation. */ - n = compute_resample_log(rate >> 24, requested_n); - previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]; - if (n != previous_n) { + n = compute_resample_log(rate / FP_ONE_HZ, requested_n); + if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) { #ifdef PRALINE fpga_set_rx_decimation_ratio(&fpga, n); #endif radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX] = n; - new_rate = true; + } + } + new_n = (n != previous_n); + + if (radio->sample_rate_cb) { + afe_rate = rate << n; + afe_rate = radio->sample_rate_cb(afe_rate, false); + new_afe_rate = (afe_rate != applied_afe_rate); + if (new_afe_rate) { + afe_rate = radio->sample_rate_cb(afe_rate, true); + applied_afe_rate = afe_rate; + } + } else { + return false; + } + rate = afe_rate >> n; + new_rate = (rate != radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]); + if (new_rate) { + radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] = rate; + if (rate != RADIO_UNSET) { + /* Round to the nearest Hz for display. */ + const uint32_t rate_hz = (rate + (FP_ONE_HZ >> 1)) / FP_ONE_HZ; + hackrf_ui()->set_sample_rate(rate_hz); } } - uint64_t afe_rate = rate << n; - uint64_t previous_afe_rate = RADIO_UNSET; - if (previous_n != RADIO_UNSET) { - previous_afe_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] - << previous_n; - } - - if (afe_rate != previous_afe_rate) { - sample_rate_frac_set(numerator(frac) << n, denominator(frac)); - applied_afe_rate = afe_rate; - radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE_FRAC] = frac; - radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] = rate; - new_rate = true; - } - - return new_rate; + return (new_afe_rate || new_rate || new_n); } -#define DEFAULT_RF (2450000000ULL << 24) +#define DEFAULT_RF (2450ULL * FP_ONE_MHZ) -static uint64_t applied_offset = RADIO_UNSET; +static fp_40_24_t applied_offset = RADIO_UNSET; #ifdef PRALINE static const tune_config_t* select_tune_config(uint64_t opmode) @@ -312,8 +278,8 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT]); if (new_if || new_lo || new_img_reject) { set_freq_explicit( - requested_if >> 24, - requested_lo >> 24, + requested_if / FP_ONE_HZ, + requested_lo / FP_ONE_HZ, requested_img_reject); radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = requested_if; @@ -348,7 +314,7 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) bool new_rf = (radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] != requested_rf); - uint64_t requested_rf_hz = requested_rf >> 24; + uint64_t requested_rf_hz = requested_rf / FP_ONE_HZ; #ifdef PRALINE if (applied_afe_rate == RADIO_UNSET) { return false; @@ -376,10 +342,10 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = tune_config->shift << 6; } - uint64_t offset = applied_afe_rate / 4; + fp_40_24_t offset = applied_afe_rate / 4; bool new_offset = (applied_offset != offset); if (new_rotation || new_offset || new_config || new_rf) { - tuning_set_frequency(tune_config, requested_rf_hz, offset >> 24); + tuning_set_frequency(tune_config, requested_rf_hz, offset / FP_ONE_HZ); applied_offset = offset; } #else @@ -394,89 +360,68 @@ static bool radio_update_frequency(radio_t* const radio, uint64_t* bank) return true; } -static uint32_t auto_bandwidth(radio_t* const radio, uint64_t opmode) +static uint32_t auto_bandwidth(radio_t* const radio) { uint64_t rotation = radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION]; - uint32_t sample_rate_hz = DEFAULT_MCU_RATE >> 24; + uint32_t sample_rate_hz = DEFAULT_MCU_RATE / FP_ONE_HZ; if (radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] != RADIO_UNSET) { sample_rate_hz = - radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] >> 24; + radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] / FP_ONE_HZ; } uint32_t offset_hz = 0; if ((rotation != 0) && (rotation != RADIO_UNSET) && (applied_offset != RADIO_UNSET)) { - offset_hz = applied_offset >> 24; + offset_hz = applied_offset / FP_ONE_HZ; } const uint32_t bb_bandwidth = (sample_rate_hz * 3) / 4; const uint32_t lpf_bandwidth = bb_bandwidth + offset_hz * 2; - switch (opmode) { - case TRANSCEIVER_MODE_TX: - case TRANSCEIVER_MODE_SS: - radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = bb_bandwidth; - break; - default: - radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] = bb_bandwidth; - } + radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = bb_bandwidth; + radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] = bb_bandwidth; + return lpf_bandwidth; } static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) { bool new_bw = false; - uint64_t opmode = bank[RADIO_OPMODE]; - if (opmode == RADIO_UNSET) { - opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]; - } #ifdef PRALINE /* Praline legacy mode always sets baseband bandwidth automatically. */ (void) bank; - uint32_t lpf_bandwidth = auto_bandwidth(radio, opmode); + uint32_t lpf_bandwidth = auto_bandwidth(radio); - switch (opmode) { - case TRANSCEIVER_MODE_TX: - case TRANSCEIVER_MODE_SS: - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != - lpf_bandwidth) { - max2831_set_lpf_bandwidth(&max283x, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = - lpf_bandwidth; - new_bw = true; - } - break; - default: - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != - lpf_bandwidth) { - max2831_set_lpf_bandwidth(&max283x, lpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = - lpf_bandwidth; - new_bw = true; - } - bool narrow_lpf_enable = false; - bool applied_narrow_lpf_enable = - radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF]; - if (lpf_bandwidth <= 1750000) { - narrow_lpf_enable = true; - } - if (applied_narrow_lpf_enable != narrow_lpf_enable) { - narrowband_filter_set(narrow_lpf_enable); - radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] = - narrow_lpf_enable; - new_bw = true; - } - /* Always set HPF bandwidth to 30 kHz for now. */ - const max2831_rx_hpf_freq_t hpf_bandwidth = MAX2831_RX_HPF_30_KHZ; - if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != - hpf_bandwidth) { - max2831_set_rx_hpf_frequency(&max283x, hpf_bandwidth); - radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = - hpf_bandwidth; - new_bw = true; - } + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { + max2831_set_lpf_bandwidth(&max283x, MAX2831_MODE_TX, lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth; + new_bw = true; + } + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != lpf_bandwidth) { + max2831_set_lpf_bandwidth(&max283x, MAX2831_MODE_RX, lpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth; + new_bw = true; + } + bool narrow_lpf_enable = false; + bool applied_narrow_lpf_enable = + radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF]; + if (lpf_bandwidth <= 1750000) { + narrow_lpf_enable = true; + } + if (applied_narrow_lpf_enable != narrow_lpf_enable) { + narrowband_filter_set(narrow_lpf_enable); + radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] = + narrow_lpf_enable; + new_bw = true; + } + /* Always set HPF bandwidth to 30 kHz for now. */ + const max2831_rx_hpf_freq_t hpf_bandwidth = MAX2831_RX_HPF_30_KHZ; + if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != hpf_bandwidth) { + max2831_set_rx_hpf_frequency(&max283x, hpf_bandwidth); + radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = hpf_bandwidth; + new_bw = true; } #else uint64_t lpf_bandwidth; @@ -494,7 +439,7 @@ static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank) lpf_bandwidth = radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF]; } if (lpf_bandwidth == RADIO_UNSET) { - lpf_bandwidth = auto_bandwidth(radio, opmode); + lpf_bandwidth = auto_bandwidth(radio); } if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) { @@ -710,8 +655,8 @@ bool radio_update(radio_t* const radio) bool dc = false; if ((dirty & - ((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_SAMPLE_RATE_FRAC) | - (1 << RADIO_RESAMPLE_TX) | (1 << RADIO_RESAMPLE_RX))) || + ((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | + (1 << RADIO_RESAMPLE_RX))) || ((detected_platform() == BOARD_ID_PRALINE) && (dirty & (1 << RADIO_OPMODE)))) { rate = radio_update_sample_rate(radio, &tmp_bank[0]); diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 5922977f..13cd2626 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -27,6 +27,8 @@ #include #include +#include "fixed_point.h" + typedef enum { RADIO_OK = 1, RADIO_ERR_INVALID_PARAM = -2, @@ -106,88 +108,82 @@ typedef enum { * Sample rate (as seen by MCU/host) in 1/(2**24) Hz. */ RADIO_SAMPLE_RATE = 6, - /** - * Sample rate (as seen by MCU/host) in fractional format. The - * numerator is stored in the low 32 bits. The denominator is stored in - * the high 32 bits. - */ - RADIO_SAMPLE_RATE_FRAC = 7, /** * Base two logarithm of TX decimation ratio (0 means a ratio of 1). */ - RADIO_RESAMPLE_TX = 8, + RADIO_RESAMPLE_TX = 7, /** * Base two logarithm of RX decimation ratio (0 means a ratio of 1). */ - RADIO_RESAMPLE_RX = 9, + RADIO_RESAMPLE_RX = 8, /** * TX RF amplifier enable of type bool. */ - RADIO_GAIN_TX_RF = 10, + RADIO_GAIN_TX_RF = 9, /** * TX IF amplifier gain in dB. */ - RADIO_GAIN_TX_IF = 11, + RADIO_GAIN_TX_IF = 10, /** * RX RF amplifier enable of type bool. */ - RADIO_GAIN_RX_RF = 12, + RADIO_GAIN_RX_RF = 11, /** * RX IF amplifier gain in dB. */ - RADIO_GAIN_RX_IF = 13, + RADIO_GAIN_RX_IF = 12, /** * RX baseband amplifier gain in dB. */ - RADIO_GAIN_RX_BB = 14, + RADIO_GAIN_RX_BB = 13, /** * TX baseband bandwidth in Hz. This controls analog baseband filter * settings but is specified as the desired bandwidth centered in * digital baseband as seen by the MCU/host. */ - RADIO_BB_BANDWIDTH_TX = 15, + RADIO_BB_BANDWIDTH_TX = 14, /** * RX baseband bandwidth in Hz. This controls analog baseband filter * settings but is specified as the desired bandwidth centered in * digital baseband as seen by the MCU/host. */ - RADIO_BB_BANDWIDTH_RX = 16, + RADIO_BB_BANDWIDTH_RX = 15, /** * Quadrature transceiver TX baseband LPF bandwidth in Hz. If no * rotation is performed, this is set to match RADIO_BB_BANDWIDTH. * Currently unused. */ - RADIO_XCVR_TX_LPF = 17, + RADIO_XCVR_TX_LPF = 16, /** * Quadrature transceiver RX baseband LPF bandwidth in Hz. If no * rotation is performed, this is set to match RADIO_BB_BANDWIDTH. * Currently unused. */ - RADIO_XCVR_RX_LPF = 18, + RADIO_XCVR_RX_LPF = 17, /** * Quadrature transceiver RX baseband HPF bandwidth in Hz. Currently * unused. */ - RADIO_XCVR_RX_HPF = 19, + RADIO_XCVR_RX_HPF = 18, /** * Narrowband RX analog baseband LPF enable of type bool. Currently unused. */ - RADIO_RX_NARROW_LPF = 20, + RADIO_RX_NARROW_LPF = 19, /** * RF port bias tee enable of type bool. */ - RADIO_BIAS_TEE = 21, + RADIO_BIAS_TEE = 20, /** * Trigger input enable of type bool. */ - RADIO_TRIGGER = 22, + RADIO_TRIGGER = 21, /** * DC block enable of type bool. */ - RADIO_DC_BLOCK = 23, + RADIO_DC_BLOCK = 22, } radio_register_t; -#define RADIO_NUM_REGS (24) +#define RADIO_NUM_REGS (23) #define RADIO_UNSET (0xffffffffffffffff) /** @@ -211,10 +207,20 @@ typedef enum { #define RADIO_NUM_BANKS (5) +/** + * A callback function must be provided that configures clock generation to + * produce the requested sample clock frequency. The function must return the + * configured sample rate. A boolean program argument may be set to false to + * execute a dry run, returning the sample rate without configuring clock + * generation. + */ +typedef fp_40_24_t (*sample_rate_fn)(const fp_40_24_t sample_rate, const bool program); + typedef struct radio_t { radio_config_mode_t config_mode; uint64_t config[RADIO_NUM_BANKS][RADIO_NUM_REGS]; volatile uint32_t regs_dirty; + sample_rate_fn sample_rate_cb; } radio_t; void radio_init(radio_t* const radio); diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 84a813a1..a8e7934c 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -30,6 +30,7 @@ #include "tuning.h" #include "usb_endpoint.h" #include "streaming.h" +#include "fixed_point.h" #include @@ -98,7 +99,7 @@ usb_request_status_t usb_vendor_request_init_sweep( &radio, RADIO_BANK_ACTIVE, RADIO_FREQUENCY_RF, - (sweep_freq + offset) << 24); + (sweep_freq + offset) * FP_ONE_HZ); usb_transfer_schedule_ack(endpoint->in); nvic_enable_irq(NVIC_USB0_IRQ); radio_update(&radio); @@ -228,7 +229,7 @@ void sweep_mode(uint32_t seq) &radio, RADIO_BANK_ACTIVE, RADIO_FREQUENCY_RF, - (sweep_freq + offset) << 24); + (sweep_freq + offset) * FP_ONE_HZ); nvic_enable_irq(NVIC_USB0_IRQ); blocks_queued = 0; } diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 6a72a1f4..964ff22c 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -40,6 +40,7 @@ #include "usb.h" #include "usb_queue.h" #include "platform_detect.h" +#include "fixed_point.h" #include #include @@ -108,7 +109,11 @@ usb_request_status_t usb_vendor_request_set_freq( } else if (stage == USB_TRANSFER_STAGE_DATA) { const uint64_t freq = set_freq_params.freq_mhz * 1000000ULL + set_freq_params.freq_hz; - radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_FREQUENCY_RF, freq << 24); + radio_reg_write( + &radio, + RADIO_BANK_ACTIVE, + RADIO_FREQUENCY_RF, + freq * FP_ONE_HZ); radio_reg_write( &radio, RADIO_BANK_ACTIVE, @@ -145,12 +150,12 @@ usb_request_status_t usb_vendor_request_set_freq_explicit( &radio, RADIO_BANK_ACTIVE, RADIO_FREQUENCY_IF, - explicit_params.if_freq_hz << 24); + explicit_params.if_freq_hz * FP_ONE_HZ); radio_reg_write( &radio, RADIO_BANK_ACTIVE, RADIO_FREQUENCY_LO, - explicit_params.lo_freq_hz << 24); + explicit_params.lo_freq_hz * FP_ONE_HZ); radio_reg_write( &radio, RADIO_BANK_ACTIVE, @@ -161,6 +166,18 @@ usb_request_status_t usb_vendor_request_set_freq_explicit( return USB_REQUEST_STATUS_OK; } +/* + * Convert fractional sample rate to units of 1/(2**24) Hz. + */ +static inline fp_40_24_t round_sample_rate(uint64_t num, uint32_t denom) +{ + num *= FP_ONE_HZ; + if (denom == 0) { + denom = 1; + } + return (num + (denom >> 1)) / denom; +} + usb_request_status_t usb_vendor_request_set_sample_rate_frac( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) @@ -174,9 +191,9 @@ usb_request_status_t usb_vendor_request_set_sample_rate_frac( NULL); } else if (stage == USB_TRANSFER_STAGE_DATA) { uint32_t numerator = set_sample_r_params.freq_hz; - uint64_t denominator = set_sample_r_params.divider; - uint64_t value = (denominator << 32) | numerator; - radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_SAMPLE_RATE_FRAC, value); + uint32_t denominator = set_sample_r_params.divider; + uint64_t value = round_sample_rate(numerator, denominator); + radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_SAMPLE_RATE, value); usb_transfer_schedule_ack(endpoint->in); } return USB_REQUEST_STATUS_OK; diff --git a/host/hackrf-tools/src/hackrf_debug.c b/host/hackrf-tools/src/hackrf_debug.c index 5f8785b5..52ede9cb 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -501,7 +501,7 @@ int radio_read_register( return result; } -#define RADIO_NUM_REGS (24) +#define RADIO_NUM_REGS (23) int radio_read_registers(hackrf_device* device, const uint8_t bank) {