diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index f279c402c..a9877e90e 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -144,6 +144,10 @@ static rf::Direction cached_direction = rf::Direction::Receive; static bool cached_rf_amp = false; static int_fast8_t cached_lna_gain = 0; static int_fast8_t cached_vga_gain = 0; +/* FPGA quarter-rate shift mode currently programmed, in gateware encoding + * (0b00 none / 0b11 up / 0b01 down). The baseband filter width depends on it, + * so ReceiverModel reads it back through get_quarter_shift(). */ +static uint8_t cached_quarter_shift = 0; #endif void init() { @@ -182,18 +186,15 @@ void init() { fpga_set_mode(FPGA_MODE_RX); - // These FPGA registers control DC_BLOCK, Q-Inv, QUARTER SHIFT, and Decimation. - fpga_debug_register_write(FPGA_REG_CTRL, FPGA_CTRL_DC_BLOCK_EN); // DC_BLOCK=1, QUARTER_SHIFT=0, Q_INVERT=0 - fpga_debug_register_write(FPGA_REG_DECIM, 0x00); // RX_DECIM=No Decim - - // RX Mode: Register 3 is RX Digital Gain. Start with 0dB (no shift). - fpga_debug_register_write(FPGA_REG_RX_DIGITAL_GAIN, FPGA_RX_DEFAULT_DIGITAL_GAIN); - - /* RX Mode: Initialize DC Block parameters to standard Praline values. - * 0x04 Width and 0x08 Adapt Rate are typical for 40MHz stability. - */ - fpga_debug_register_write(FPGA_REG_RX_DC_BLOCK_WIDTH, FPGA_RX_DEFAULT_DC_WIDTH); - fpga_debug_register_write(FPGA_REG_RX_DC_ADAPT_RATE, FPGA_RX_DEFAULT_ADAPT_RATE); + /* Boot register state, matching fpga_init() in hackrf/firmware/common/fpga.c: + * DC block on, no PRBS, no external trigger, no quarter shift, TX NCO off. + * The decimation ratio and the quarter shift are programmed later by + * ClockManager::set_sampling_frequency() and set_tuning_frequency(). */ + fpga_debug_register_write(FPGA_REG_CTRL, FPGA_CTRL_DC_BLOCK_EN); + fpga_debug_register_write(FPGA_REG_DECIM, 0x00); // RX_DECIM = no decimation + fpga_debug_register_write(FPGA_REG_RX_PSTEP, 0x00); // quarter shift off + fpga_debug_register_write(FPGA_REG_TX_CONTROL, 0x00); + cached_quarter_shift = 0; ssp1_arbiter.invalidate(); chThdSleepMilliseconds(10); // Let FPGA registers settle @@ -224,13 +225,13 @@ void set_direction(const rf::Direction new_direction) { fpga_debug_register_write(FPGA_REG_TX_PHASE_STEP, 0x00); } else { fpga_set_mode(FPGA_MODE_RX); - // RX Mode: Ensure NCO is disabled and reset digital gain - fpga_debug_register_write(FPGA_REG_RX_DIGITAL_GAIN, FPGA_RX_DEFAULT_DIGITAL_GAIN); - /* RX Mode: Initialize DC Block parameters to standard Praline values. - * 0x04 Width and 0x08 Adapt Rate are typical for 40MHz stability. - */ - fpga_debug_register_write(FPGA_REG_RX_DC_BLOCK_WIDTH, FPGA_RX_DEFAULT_DC_WIDTH); - fpga_debug_register_write(FPGA_REG_RX_DC_ADAPT_RATE, FPGA_RX_DEFAULT_ADAPT_RATE); + /* RX Mode: DC block on, TX NCO off. The quarter shift is re-applied by + * set_tuning_frequency(); clear it here so a stale TX/RX transition + * cannot leave a rotation programmed with no matching LO offset. */ + fpga_debug_register_write(FPGA_REG_CTRL, FPGA_CTRL_DC_BLOCK_EN); + fpga_debug_register_write(FPGA_REG_TX_CONTROL, 0x00); + fpga_debug_register_write(FPGA_REG_RX_PSTEP, 0x00); + cached_quarter_shift = 0; } #endif @@ -309,7 +310,19 @@ bool set_tuning_frequency(const rf::Frequency frequency) { final_frequency = final_frequency + portapack::persistent_memory::config_freq_rx_correction(); } +#ifdef PRALINE + /* The PRALINE tuning tables offset the analogue passband by a quarter of + * the ADC sample rate and have the FPGA rotate it back to DC, so the + * planner needs to know the AFE rate. See tuning.cpp. */ + const uint32_t afe_rate = portapack::clock_manager.get_sampling_frequency() + << portapack::clock_manager.get_resampling_n(); + const auto tuning_config = tuning::config::create( + final_frequency, + afe_rate, + direction == rf::Direction::Transmit); +#else const auto tuning_config = tuning::config::create(final_frequency); +#endif if (tuning_config.is_valid()) { first_if.disable(); @@ -339,6 +352,22 @@ bool set_tuning_frequency(const rf::Frequency frequency) { LPC_GPIO->CLR[0] = (1 << 13); // SGPIO12 = 0 (Q normal) } + /* Program the FPGA's quarter-rate shift to match the offset the tuning + * table just applied to the analogue centre frequency. The gateware + * (hackrf/firmware/fpga/top/standard.py) takes both bits from the top + * of register 0x03 (rx_pstep): + * rx_pstep[6] -> quarter_shift.enable + * rx_pstep[7] -> quarter_shift.up + * which is exactly fpga_set_rx_quarter_shift_mode() in + * hackrf/firmware/common/fpga.c: write (mode & 0b11) << 6. + * + * These two settings MUST be programmed together. Tuning off-centre + * without the rotation puts the signal outside the decimation filter's + * passband and it disappears entirely; rotating without the offset + * moves the wanted signal off DC by the same amount. */ + cached_quarter_shift = tuning_config.quarter_shift; + fpga_debug_register_write(FPGA_REG_RX_PSTEP, (cached_quarter_shift & 0b11) << 6); + ssp1_arbiter.invalidate(); #else baseband_cpld.set_invert(mixer_invert ^ baseband_invert); @@ -469,6 +498,10 @@ int_fast8_t get_cached_lna_gain() { int_fast8_t get_cached_vga_gain() { return cached_vga_gain; } + +uint8_t get_cached_quarter_shift() { + return cached_quarter_shift; +} #endif namespace first_if { @@ -570,18 +603,11 @@ void register_write(const size_t register_number, uint32_t value) { void init() { fpga_set_mode(FPGA_MODE_RX); - // These FPGA registers control DC_BLOCK, Q-Inv, QUARTER SHIFT, and Decimation. - fpga_debug_register_write(FPGA_REG_CTRL, FPGA_CTRL_DC_BLOCK_EN); // DC_BLOCK=1, QUARTER_SHIFT=0, Q_INVERT=0 - fpga_debug_register_write(FPGA_REG_DECIM, 0x00); // RX_DECIM=No Decim - - // RX Mode: Register 3 is RX Digital Gain. Start with 0dB (no shift). - fpga_debug_register_write(FPGA_REG_RX_DIGITAL_GAIN, FPGA_RX_DEFAULT_DIGITAL_GAIN); - - /* RX Mode: Initialize DC Block parameters to standard Praline values. - * 0x04 Width and 0x08 Adapt Rate are typical for 40MHz stability. - */ - fpga_debug_register_write(FPGA_REG_RX_DC_BLOCK_WIDTH, FPGA_RX_DEFAULT_DC_WIDTH); - fpga_debug_register_write(FPGA_REG_RX_DC_ADAPT_RATE, FPGA_RX_DEFAULT_ADAPT_RATE); + /* Same boot state as fpga_init() in hackrf/firmware/common/fpga.c. */ + fpga_debug_register_write(FPGA_REG_CTRL, FPGA_CTRL_DC_BLOCK_EN); + fpga_debug_register_write(FPGA_REG_DECIM, 0x00); // RX_DECIM = no decimation + fpga_debug_register_write(FPGA_REG_RX_PSTEP, 0x00); // quarter shift off + fpga_debug_register_write(FPGA_REG_TX_CONTROL, 0x00); ssp1_arbiter.invalidate(); // Force arbiter to reconfigure on next transfer }