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 5509b9b1..220961ff 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -446,16 +446,26 @@ static uint32_t gcd(uint32_t u, uint32_t v) return u << s; } -bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) +/* + * 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) { - 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 +473,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. */ + uint32_t g = gcd(p2, p3); + p2 /= g; + p3 /= g; - 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 +547,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 +570,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 +871,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/radio.c b/firmware/common/radio.c index 3b823872..ef16102f 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -28,6 +28,7 @@ #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)) @@ -119,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 fp_40_24_t rate) -{ - uint64_t num = rate; - uint64_t denom = FP_ONE_HZ; - 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 fp_40_24_t round_sample_rate(const uint64_t frac) -{ - uint64_t num = (uint64_t) numerator(frac) * FP_ONE_HZ; - 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( @@ -191,50 +152,47 @@ static fp_40_24_t applied_afe_rate = RADIO_UNSET; static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) { - fp_40_24_t rate; - uint64_t 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: @@ -243,32 +201,33 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank) * spectrum inversion bug with TX interpolation. */ n = compute_resample_log(rate / FP_ONE_HZ, requested_n); - previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]; - if (n != previous_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); - fp_40_24_t afe_rate = rate << n; - fp_40_24_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)); + afe_rate = rate << n; + afe_rate = sample_rate_set(afe_rate, false); + new_afe_rate = (afe_rate != applied_afe_rate); + if (new_afe_rate) { + afe_rate = sample_rate_set(afe_rate, true); applied_afe_rate = afe_rate; - radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE_FRAC] = frac; + } + 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; - new_rate = true; + + /* 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); } - return new_rate; + return (new_afe_rate || new_rate || new_n); } #define DEFAULT_RF (2450ULL * FP_ONE_MHZ) @@ -691,8 +650,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/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index 88e61329..964ff22c 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -166,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) @@ -179,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;