PRALINE Tx and clock fix (Pr 3192 takeover (#3198) )

* fix clock init and add mayhem tx capacity
* aligned external clock and RX config
* fix(clock): cap TX interpolation to x16 to prevent doubled waveform speed

  The TX gateware only implements interpolation factors x1..x16 (tx_intrp
  0..4), but the rate search could select n==5 (x32) for very low TX
  sample rates. That left the AFE/CLK0/CLK1 running at the x32 rate while
  the FPGA only interpolated by x16, clocking the TX datapath at twice the
  gateware output rate. Cap afe_rate and _resampling_n to the x16 limit in
  TX so the clocks stay aligned with the interpolation setting.

* fix(praline): source PLL1 from 40MHz GP_CLKIN instead of 12MHz XTAL

  For the intermediate 90-110MHz PLL1 stepping stage, source PLL1 from
  GP_CLKIN (Si5351 CLK2/MCU_CLK at 40MHz) giving N=2, M=10, Fclk=100MHz,
  rather than the on-chip 12MHz XTAL bootstrap path. This matches the
  non-PRALINE path. Remove the dead commented-out XTAL enable/PLL code.

* docs(fpga_bridge): correct register 0x03 description for PRALINE gateware

  Register 0x03 is RX_DIGITAL_GAIN in RX and TX_NCO_CTRL in TX, not
  RX_PSTEP.

* Fix fpga_tx_set_nco_enable to use FPGA_REG3_TX_NCO_CTRL (0x03)
This commit is contained in:
gullradriel
2026-05-31 12:44:40 +02:00
committed by GitHub
parent 4e56d0f420
commit 8f5b25b10d
11 changed files with 241 additions and 201 deletions
@@ -140,7 +140,7 @@
// ============================================================================
static fpga_mode_t current_mode = FPGA_MODE_OFF;
// Cached register values for debug reads (since reads may require mode switch)
static uint8_t fpga_reg_cache[6] = {0, 0x01, 0x00, 0x00, 0x00, 0x00};
static uint8_t fpga_reg_cache[7] = {0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
// Context structure for SPIFI-based reading
struct spifi_fpga_read_ctx {
@@ -315,7 +315,7 @@ static void fpga_spi_write(uint8_t reg, uint8_t value) {
// Public function to read FPGA register (callable from C++ application code)
// Switches SPI mode, reads register, switches back
uint8_t fpga_debug_register_read(uint8_t reg) {
if (reg == 0 || reg > 5) return 0xFF;
if (reg == 0 || reg > 6) return 0xFF;
uint8_t value;
ssp1_set_mode_ice40();
@@ -328,7 +328,7 @@ uint8_t fpga_debug_register_read(uint8_t reg) {
// Public function to write FPGA register (callable from C++ application code)
void fpga_debug_register_write(uint8_t reg, uint8_t value) {
if (reg == 0 || reg > 5) return;
if (reg == 0 || reg > 6) return;
ssp1_set_mode_ice40();
fpga_spi_write(reg, value);
@@ -342,7 +342,7 @@ void fpga_debug_register_write(uint8_t reg, uint8_t value) {
// ============================================================================
uint8_t fpga_register_read(uint8_t reg) {
if (reg == 0 || reg > 5) return 0xFF;
if (reg == 0 || reg > 6) return 0xFF;
ssp1_set_mode_ice40();
uint8_t val = fpga_spi_read(reg);
@@ -353,7 +353,7 @@ uint8_t fpga_register_read(uint8_t reg) {
}
void fpga_register_write(uint8_t reg, uint8_t value) {
if (reg == 0 || reg > 5) return;
if (reg == 0 || reg > 6) return;
ssp1_set_mode_ice40();
fpga_spi_write(reg, value);
@@ -422,22 +422,22 @@ void fpga_rx_set_dc_adapt_rate(uint8_t rate) {
/* TX Functions with mode assertion */
void fpga_tx_set_nco_enable(bool enable) {
if (current_mode != FPGA_MODE_TX) return;
uint8_t val = fpga_register_read(FPGA_REG_SHARED_3);
uint8_t val = fpga_register_read(FPGA_REG3_TX_NCO_CTRL);
if (enable)
val |= FPGA_TX_NCO_EN;
else
val &= ~FPGA_TX_NCO_EN;
fpga_register_write(FPGA_REG_SHARED_3, val);
fpga_register_write(FPGA_REG3_TX_NCO_CTRL, val);
}
void fpga_tx_set_interpolation(uint8_t ratio) {
if (current_mode != FPGA_MODE_TX) return;
fpga_register_write(FPGA_REG_SHARED_4, ratio & FPGA_TX_INTERP_MASK);
fpga_register_write(FPGA_REG_TX_INTERP, ratio & FPGA_TX_INTERP_MASK);
}
void fpga_tx_set_phase_step(uint8_t step) {
if (current_mode != FPGA_MODE_TX) return;
fpga_register_write(FPGA_REG_SHARED_5, step);
fpga_register_write(FPGA_REG_TX_PHASE_STEP, step);
}
// ============================================================================
@@ -16,17 +16,21 @@ extern "C" {
#ifdef PRALINE
/* FPGA Register Map Address 0x03 (Dual Purpose) */
/* RX path (legacy PRALINE software map kept for compatibility) */
#define FPGA_REG_RX_DIGITAL_GAIN 0x03 /* Digital Shift / scaling (RX Mode) */
#define FPGA_REG_TX_CONTROL 0x03 /* NCO_EN and TX flags (TX Mode) */
/* FPGA Register Map Address 0x04 (Shared) */
#define FPGA_REG_RX_DC_BLOCK_WIDTH 0x04 /* Notch filter cutoff (RX Mode) */
#define FPGA_REG_TX_INTERP 0x04 /* Interpolation ratio (TX Mode) */
/* FPGA Register Map Address 0x05 (Shared) */
#define FPGA_REG_RX_DC_ADAPT_RATE 0x05 /* Settle time/Integration (RX Mode) */
#define FPGA_REG_TX_PHASE_STEP 0x05 /* NCO frequency step (TX Mode) */
/*
* TX path must match the currently built PRALINE standard gateware in
* hackrf/firmware/fpga/top/standard.py:
* 0x04 tx_ctrl
* 0x05 tx_intrp
* 0x06 tx_pstep
*/
#define FPGA_REG_TX_CONTROL 0x04
#define FPGA_REG_TX_INTERP 0x05
#define FPGA_REG_TX_PHASE_STEP 0x06
/*
* FPGA Operating Mode
@@ -39,13 +43,21 @@ typedef enum {
/*
* FPGA Register Addresses
* Note: Registers 3-5 are dual-purpose (meaning depends on RX/TX mode)
* NOTE:
* The currently loaded PRALINE standard gateware uses:
* 0x01 CTRL
* 0x02 RX_DECIM
* 0x03 RX_DIGITAL_GAIN (RX) / TX_NCO_CTRL (TX)
* 0x04 TX_CTRL
* 0x05 TX_INTRP
* 0x06 TX_PSTEP
*/
#define FPGA_REG_CTRL 0x01 /* Control register */
#define FPGA_REG_DECIM 0x02 /* Decimation (RX) / unused (TX) */
#define FPGA_REG_SHARED_3 0x03 /* Dual-purpose register */
#define FPGA_REG_SHARED_4 0x04 /* Dual-purpose register */
#define FPGA_REG_SHARED_5 0x05 /* Dual-purpose register */
#define FPGA_REG_DECIM 0x02 /* RX decimation */
#define FPGA_REG_SHARED_3 0x03 /* Legacy shared register */
#define FPGA_REG_SHARED_4 0x04 /* Legacy shared register */
#define FPGA_REG_SHARED_5 0x05 /* Legacy shared register */
#define FPGA_REG_SHARED_6 0x06 /* TX phase step */
/*
* Register 1 (CTRL) Bit Definitions
@@ -77,7 +89,7 @@ typedef enum {
#define FPGA_RX_DC_WIDTH_MASK 0x07 /* Bits [2:0] */
/* TX Mode: Interpolation ratio */
#define FPGA_REG4_TX_INTERP 0x04
#define FPGA_REG4_TX_INTERP 0x05
#define FPGA_TX_INTERP_MASK 0x07 /* Bits [2:0] */
/*
@@ -88,7 +100,7 @@ typedef enum {
#define FPGA_RX_DC_RATE_MASK 0xFF /* Bits [7:0] */
/* TX Mode: NCO phase step (frequency) */
#define FPGA_REG5_TX_PHASE_STEP 0x05
#define FPGA_REG5_TX_PHASE_STEP 0x06
#define FPGA_TX_PHASE_STEP_MASK 0xFF /* Bits [7:0] */
/* Export default values so other methods can use them */