diff --git a/FPGA/VNA/SPIConfig.vhd b/FPGA/VNA/SPIConfig.vhd old mode 100644 new mode 100755 index a2887d1..4cf523f --- a/FPGA/VNA/SPIConfig.vhd +++ b/FPGA/VNA/SPIConfig.vhd @@ -45,7 +45,7 @@ entity SPICommands is MAX2871_DEF_3 : out STD_LOGIC_VECTOR (31 downto 0); MAX2871_DEF_1 : out STD_LOGIC_VECTOR (31 downto 0); MAX2871_DEF_0 : out STD_LOGIC_VECTOR (31 downto 0); - SWEEP_DATA : out STD_LOGIC_VECTOR (111 downto 0); + SWEEP_DATA : out STD_LOGIC_VECTOR (95 downto 0); SWEEP_ADDRESS : out STD_LOGIC_VECTOR (12 downto 0); SWEEP_WRITE : out STD_LOGIC_VECTOR (0 downto 0); SWEEP_POINTS : out STD_LOGIC_VECTOR (12 downto 0); @@ -54,6 +54,7 @@ entity SPICommands is SETTLING_TIME : out STD_LOGIC_VECTOR (19 downto 0); SYNC_ENABLED : out STD_LOGIC; SYNC_MASTER : out STD_LOGIC; + CDS_ENABLED : out STD_LOGIC; PORT1_STAGE : out STD_LOGIC_VECTOR (2 downto 0); PORT2_STAGE : out STD_LOGIC_VECTOR (2 downto 0); PORT1_EN : out STD_LOGIC; @@ -288,6 +289,7 @@ begin when 5 => ADC_PHASEINC <= spi_buf_out(11 downto 0); when 6 => STAGES <= spi_buf_out(15 downto 13); SYNC_ENABLED <= spi_buf_out(12); + CDS_ENABLED <= spi_buf_out(11); PORT1_STAGE <= spi_buf_out(5 downto 3); PORT2_STAGE <= spi_buf_out(2 downto 0); when 7 => SPI_OVERWRITE_ENABLED <= spi_buf_out(15); @@ -308,9 +310,9 @@ begin end case; selected_register <= selected_register + 1; when WriteSweepConfig => - if word_cnt = 7 then - -- Sweep config data is complete pass on (112 bits = 96 + 16) - SWEEP_DATA <= sweepconfig_buffer & spi_buf_out; + if word_cnt = 5 then + -- Sweep config data is complete (96 bits = 6 x 16-bit words) + SWEEP_DATA <= sweepconfig_buffer(79 downto 0) & spi_buf_out; sweep_config_write <= '1'; else -- shift next word into buffer diff --git a/FPGA/VNA/Sweep.vhd b/FPGA/VNA/Sweep.vhd old mode 100644 new mode 100755 index 7dc3201..3ed7d24 --- a/FPGA/VNA/Sweep.vhd +++ b/FPGA/VNA/Sweep.vhd @@ -34,7 +34,7 @@ entity Sweep is RESET : in STD_LOGIC; NPOINTS : in STD_LOGIC_VECTOR (12 downto 0); CONFIG_ADDRESS : out STD_LOGIC_VECTOR (12 downto 0); - CONFIG_DATA : in STD_LOGIC_VECTOR (111 downto 0); + CONFIG_DATA : in STD_LOGIC_VECTOR (95 downto 0); USER_NSAMPLES : in STD_LOGIC_VECTOR (12 downto 0); NSAMPLES : out STD_LOGIC_VECTOR (12 downto 0); SETTLING_TIME : in STD_LOGIC_VECTOR (19 downto 0); @@ -86,6 +86,9 @@ entity Sweep is SOURCE_CE : out STD_LOGIC; + -- Correlated Double Sampling + CDS_ENABLED : in STD_LOGIC; + -- Debug signals DEBUG_STATUS : out STD_LOGIC_VECTOR (10 downto 0); RESULT_INDEX : out STD_LOGIC_VECTOR (15 downto 0) @@ -98,19 +101,31 @@ architecture Behavioral of Sweep is signal state : Point_states; signal settling_cnt : unsigned(19 downto 0); signal stage_cnt : unsigned (2 downto 0); - signal config_reg : std_logic_vector(111 downto 0); + signal config_reg : std_logic_vector(95 downto 0); signal source_active : std_logic; - -- Source phase value extracted from config (bits 111:100) + -- CDS phase tracking: 0 = first measurement (phase 0), 1 = second measurement (phase 180) + signal cds_phase : std_logic; + + -- Source phase value: computed from CDS state + -- When CDS disabled or cds_phase=0: phase = 0 + -- When CDS enabled and cds_phase=1: phase = M/2 (180 degrees) signal source_phase : std_logic_vector(11 downto 0); + signal source_M : std_logic_vector(11 downto 0); begin CONFIG_ADDRESS <= std_logic_vector(point_cnt); - -- Extract source phase from config (new bits 111:100) - source_phase <= config_reg(111 downto 100); + -- Extract Source M from config (bits 38:27) + source_M <= config_reg(38 downto 27); - -- Phase adjustment is enabled when source_phase is non-zero + -- Compute source phase for CDS + -- Phase 0: source_phase = 0 + -- Phase 180: source_phase = M/2 (right shift by 1) + source_phase <= (others => '0') when (CDS_ENABLED = '0' or cds_phase = '0') + else '0' & source_M(11 downto 1); -- M/2 for 180 degree shift + + -- Phase adjustment is enabled when source_phase is non-zero (i.e., CDS phase 1) SOURCE_PHASE_ADJUST <= '0' when source_phase = x"000" else '1'; -- assemble registers @@ -164,7 +179,7 @@ begin DEBUG_STATUS(2) <= source_active; DEBUG_STATUS(1 downto 0) <= (others => '1'); - config_reg <= CONFIG_DATA; + config_reg <= CONFIG_DATA(95 downto 0); process(CLK, RESET) begin @@ -172,6 +187,7 @@ begin if RESET = '1' then point_cnt <= (others => '0'); stage_cnt <= (others => '0'); + cds_phase <= '0'; state <= WaitInitialLow; START_SAMPLING <= '0'; RELOAD_PLL_REGS <= '0'; @@ -243,7 +259,9 @@ begin -- wait for sampling to finish START_SAMPLING <= '0'; if SAMPLING_BUSY = '0' then - RESULT_INDEX <= std_logic_vector(stage_cnt) & std_logic_vector(point_cnt); + -- RESULT_INDEX format: stage(2:0) & point(11:0) & cds_phase when CDS enabled + -- When CDS disabled, cds_phase is always 0 + RESULT_INDEX <= std_logic_vector(stage_cnt) & std_logic_vector(point_cnt(11 downto 0)) & cds_phase; state <= WaitTriggerLow; end if; when WaitTriggerLow => @@ -259,19 +277,32 @@ begin NEW_DATA <= '1'; if stage_cnt < unsigned(STAGES) then stage_cnt <= stage_cnt + 1; - -- can go directly to preperation for next stage + -- can go directly to preparation for next stage state <= Settling; else - state <= NextPoint; + -- All stages done for this measurement + -- Check if we need another CDS phase + if CDS_ENABLED = '1' and cds_phase = '0' then + -- First CDS phase done, do second phase at 180 degrees + cds_phase <= '1'; + stage_cnt <= (others => '0'); + -- Go back to reload PLL with new phase + state <= TriggerSetup; + else + -- CDS complete or disabled, move to next point + state <= NextPoint; + end if; end if; settling_cnt <= unsigned(SETTLING_TIME); when NextPoint => NEW_DATA <= '0'; + -- Reset CDS phase for next point + cds_phase <= '0'; if point_cnt < unsigned(NPOINTS) then point_cnt <= point_cnt + 1; stage_cnt <= (others => '0'); state <= TriggerSetup; - else + else point_cnt <= (others => '0'); state <= Done; TRIGGER_OUT <= '0'; diff --git a/FPGA/VNA/ipcore_dir/SweepConfigMem.xco b/FPGA/VNA/ipcore_dir/SweepConfigMem.xco old mode 100644 new mode 100755 index f272b47..e597e48 --- a/FPGA/VNA/ipcore_dir/SweepConfigMem.xco +++ b/FPGA/VNA/ipcore_dir/SweepConfigMem.xco @@ -1,108 +1,108 @@ -############################################################## -# -# Xilinx Core Generator version 14.6 -# Date: Mon Sep 14 08:50:12 2020 -# -############################################################## -# -# This file contains the customisation parameters for a -# Xilinx CORE Generator IP GUI. It is strongly recommended -# that you do not manually alter this file as it may cause -# unexpected and unsupported behavior. -# -############################################################## -# -# Generated from component: xilinx.com:ip:blk_mem_gen:7.3 -# -############################################################## -# -# BEGIN Project Options -SET addpads = false -SET asysymbol = true -SET busformat = BusFormatAngleBracketNotRipped -SET createndf = false -SET designentry = VHDL -SET device = xc6slx9 -SET devicefamily = spartan6 -SET flowvendor = Other -SET formalverification = false -SET foundationsym = false -SET implementationfiletype = Ngc -SET package = tqg144 -SET removerpms = false -SET simulationfiles = Behavioral -SET speedgrade = -2 -SET verilogsim = false -SET vhdlsim = true -# END Project Options -# BEGIN Select -SELECT Block_Memory_Generator xilinx.com:ip:blk_mem_gen:7.3 -# END Select -# BEGIN Parameters -CSET additional_inputs_for_power_estimation=false -CSET algorithm=Minimum_Area -CSET assume_synchronous_clk=false -CSET axi_id_width=4 -CSET axi_slave_type=Memory_Slave -CSET axi_type=AXI4_Full -CSET byte_size=9 -CSET coe_file=no_coe_file_loaded -CSET collision_warnings=ALL -CSET component_name=SweepConfigMem -CSET disable_collision_warnings=false -CSET disable_out_of_range_warnings=false -CSET ecc=false -CSET ecctype=No_ECC -CSET enable_32bit_address=false -CSET enable_a=Use_ENA_Pin -CSET enable_b=Always_Enabled -CSET error_injection_type=Single_Bit_Error_Injection -CSET fill_remaining_memory_locations=false -CSET interface_type=Native -CSET load_init_file=false -CSET mem_file=no_Mem_file_loaded -CSET memory_type=Simple_Dual_Port_RAM -CSET operating_mode_a=WRITE_FIRST -CSET operating_mode_b=WRITE_FIRST -CSET output_reset_value_a=0 -CSET output_reset_value_b=0 -CSET pipeline_stages=0 -CSET port_a_clock=100 -CSET port_a_enable_rate=100 -CSET port_a_write_rate=50 -CSET port_b_clock=100 -CSET port_b_enable_rate=100 -CSET port_b_write_rate=0 -CSET primitive=8kx2 -CSET read_width_a=112 -CSET read_width_b=112 -CSET register_porta_input_of_softecc=false -CSET register_porta_output_of_memory_core=false -CSET register_porta_output_of_memory_primitives=false -CSET register_portb_output_of_memory_core=false -CSET register_portb_output_of_memory_primitives=false -CSET register_portb_output_of_softecc=false -CSET remaining_memory_locations=0 -CSET reset_memory_latch_a=false -CSET reset_memory_latch_b=false -CSET reset_priority_a=CE -CSET reset_priority_b=CE -CSET reset_type=SYNC -CSET softecc=false -CSET use_axi_id=false -CSET use_bram_block=Stand_Alone -CSET use_byte_write_enable=false -CSET use_error_injection_pins=false -CSET use_regcea_pin=false -CSET use_regceb_pin=false -CSET use_rsta_pin=false -CSET use_rstb_pin=false -CSET write_depth_a=4501 -CSET write_width_a=112 -CSET write_width_b=112 -# END Parameters -# BEGIN Extra information -MISC pkg_timestamp=2012-11-19T16:22:25Z -# END Extra information -GENERATE -# CRC: e7b4a756 +############################################################## +# +# Xilinx Core Generator version 14.7 +# Date: Sun Feb 01 07:48:09 2026 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# Generated from component: xilinx.com:ip:blk_mem_gen:7.3 +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = VHDL +SET device = xc6slx9 +SET devicefamily = spartan6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = tqg144 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = false +SET vhdlsim = true +# END Project Options +# BEGIN Select +SELECT Block_Memory_Generator xilinx.com:ip:blk_mem_gen:7.3 +# END Select +# BEGIN Parameters +CSET additional_inputs_for_power_estimation=false +CSET algorithm=Minimum_Area +CSET assume_synchronous_clk=false +CSET axi_id_width=4 +CSET axi_slave_type=Memory_Slave +CSET axi_type=AXI4_Full +CSET byte_size=9 +CSET coe_file=no_coe_file_loaded +CSET collision_warnings=ALL +CSET component_name=SweepConfigMem +CSET disable_collision_warnings=false +CSET disable_out_of_range_warnings=false +CSET ecc=false +CSET ecctype=No_ECC +CSET enable_32bit_address=false +CSET enable_a=Use_ENA_Pin +CSET enable_b=Always_Enabled +CSET error_injection_type=Single_Bit_Error_Injection +CSET fill_remaining_memory_locations=false +CSET interface_type=Native +CSET load_init_file=false +CSET mem_file=no_Mem_file_loaded +CSET memory_type=Simple_Dual_Port_RAM +CSET operating_mode_a=WRITE_FIRST +CSET operating_mode_b=WRITE_FIRST +CSET output_reset_value_a=0 +CSET output_reset_value_b=0 +CSET pipeline_stages=0 +CSET port_a_clock=100 +CSET port_a_enable_rate=100 +CSET port_a_write_rate=50 +CSET port_b_clock=100 +CSET port_b_enable_rate=100 +CSET port_b_write_rate=0 +CSET primitive=8kx2 +CSET read_width_a=96 +CSET read_width_b=96 +CSET register_porta_input_of_softecc=false +CSET register_porta_output_of_memory_core=false +CSET register_porta_output_of_memory_primitives=false +CSET register_portb_output_of_memory_core=false +CSET register_portb_output_of_memory_primitives=false +CSET register_portb_output_of_softecc=false +CSET remaining_memory_locations=0 +CSET reset_memory_latch_a=false +CSET reset_memory_latch_b=false +CSET reset_priority_a=CE +CSET reset_priority_b=CE +CSET reset_type=SYNC +CSET softecc=false +CSET use_axi_id=false +CSET use_bram_block=Stand_Alone +CSET use_byte_write_enable=false +CSET use_error_injection_pins=false +CSET use_regcea_pin=false +CSET use_regceb_pin=false +CSET use_rsta_pin=false +CSET use_rstb_pin=false +CSET write_depth_a=4501 +CSET write_width_a=96 +CSET write_width_b=96 +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-11-19T16:22:25Z +# END Extra information +GENERATE +# CRC: e7b4a756 diff --git a/FPGA/VNA/top.bin b/FPGA/VNA/top.bin old mode 100644 new mode 100755 index 38af89c..3b7dca5 Binary files a/FPGA/VNA/top.bin and b/FPGA/VNA/top.bin differ diff --git a/FPGA/VNA/top.vhd b/FPGA/VNA/top.vhd old mode 100644 new mode 100755 index a39d81c..dd07a9c --- a/FPGA/VNA/top.vhd +++ b/FPGA/VNA/top.vhd @@ -113,7 +113,7 @@ architecture Behavioral of top is CLK : IN std_logic; RESET : IN std_logic; NPOINTS : IN std_logic_vector(12 downto 0); - CONFIG_DATA : IN std_logic_vector(111 downto 0); + CONFIG_DATA : IN std_logic_vector(95 downto 0); USER_NSAMPLES : in STD_LOGIC_VECTOR (12 downto 0); NSAMPLES : out STD_LOGIC_VECTOR (12 downto 0); SETTLING_TIME : in STD_LOGIC_VECTOR (19 downto 0); @@ -154,11 +154,12 @@ architecture Behavioral of top is PORT1_ACTIVE : out STD_LOGIC; PORT2_ACTIVE : out STD_LOGIC; SOURCE_CE : out STD_LOGIC; + CDS_ENABLED : in STD_LOGIC; RESULT_INDEX : out STD_LOGIC_VECTOR (15 downto 0); DEBUG_STATUS : out STD_LOGIC_VECTOR (10 downto 0) ); END COMPONENT; - + COMPONENT Windowing PORT( CLK : IN std_logic; @@ -252,7 +253,7 @@ architecture Behavioral of top is MAX2871_DEF_3 : OUT std_logic_vector(31 downto 0); MAX2871_DEF_1 : OUT std_logic_vector(31 downto 0); MAX2871_DEF_0 : OUT std_logic_vector(31 downto 0); - SWEEP_DATA : OUT std_logic_vector(111 downto 0); + SWEEP_DATA : OUT std_logic_vector(95 downto 0); SWEEP_ADDRESS : OUT std_logic_vector(12 downto 0); SWEEP_WRITE : OUT std_logic_vector(0 to 0); SWEEP_POINTS : OUT std_logic_vector(12 downto 0); @@ -261,6 +262,7 @@ architecture Behavioral of top is SETTLING_TIME : out STD_LOGIC_VECTOR (19 downto 0); SYNC_ENABLED : out STD_LOGIC; SYNC_MASTER : out STD_LOGIC; + CDS_ENABLED : out STD_LOGIC; PORT1_STAGE : out STD_LOGIC_VECTOR (2 downto 0); PORT2_STAGE : out STD_LOGIC_VECTOR (2 downto 0); PORT1_EN : out STD_LOGIC; @@ -315,10 +317,10 @@ architecture Behavioral of top is ena : IN STD_LOGIC; wea : IN STD_LOGIC_VECTOR(0 DOWNTO 0); addra : IN STD_LOGIC_VECTOR(12 DOWNTO 0); - dina : IN STD_LOGIC_VECTOR(111 DOWNTO 0); + dina : IN STD_LOGIC_VECTOR(95 DOWNTO 0); clkb : IN STD_LOGIC; addrb : IN STD_LOGIC_VECTOR(12 DOWNTO 0); - doutb : OUT STD_LOGIC_VECTOR(111 DOWNTO 0) + doutb : OUT STD_LOGIC_VECTOR(95 DOWNTO 0) ); END COMPONENT; @@ -387,14 +389,15 @@ architecture Behavioral of top is signal sweep_sync_master : STD_LOGIC; signal sweep_port1_stage : STD_LOGIC_VECTOR (2 downto 0); signal sweep_port2_stage : STD_LOGIC_VECTOR (2 downto 0); - signal sweep_config_data : std_logic_vector(111 downto 0); + signal sweep_config_data : std_logic_vector(95 downto 0); + signal cds_enabled : std_logic; signal sweep_config_address : std_logic_vector(12 downto 0); signal sweep_source_filter : std_logic_vector(1 downto 0); signal sweep_band : std_logic; signal sweep_attenuator : std_logic_vector(6 downto 0); signal sweep_config_write_address : std_logic_vector(12 downto 0); - signal sweep_config_write_data : std_logic_vector(111 downto 0); + signal sweep_config_write_data : std_logic_vector(95 downto 0); signal sweep_config_write : std_logic_vector(0 downto 0); -- Phase adjustment signal from Sweep to Source PLL @@ -724,6 +727,7 @@ begin SWEEP_HALTED => sweep_halted, SWEEP_RESUME => sweep_resume, SOURCE_PHASE_ADJUST => source_phase_adjust, + CDS_ENABLED => cds_enabled, SYNC_ENABLED => sweep_sync_enabled, SYNC_MASTER => sweep_sync_master, TRIGGER_IN => sweep_trigger_in, @@ -815,6 +819,7 @@ begin SETTLING_TIME => settling_time, SYNC_ENABLED => sweep_sync_enabled, SYNC_MASTER => sweep_sync_master, + CDS_ENABLED => cds_enabled, PORT1_STAGE => sweep_port1_stage, PORT2_STAGE => sweep_port2_stage, SPI_OVERWRITE_ENABLED => HW_overwrite_enabled, diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp index bce2a94..d4a8eb3 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp @@ -13,6 +13,7 @@ static FPGA::HaltedCallback halted_cb; static uint16_t SysCtrlReg = 0x0000; static uint16_t ISRMaskReg = 0x0000; +static uint16_t SweepSetupReg = 0x0000; static uint32_t ADC_samplerate; static volatile bool busy_reading = false; @@ -144,17 +145,27 @@ void FPGA::SetSettlingTime(uint16_t us) { } void FPGA::SetupSweep(uint8_t stages, uint8_t port1_stage, uint8_t port2_stage, bool synchronize, bool syncMaster) { - uint16_t value = 0x0000; - value |= (uint16_t) (stages & 0x07) << 13; + // Preserve CDS bit when updating sweep setup + SweepSetupReg = SweepSetupReg & 0x0800; // Keep bit 11 (CDS_ENABLED) + SweepSetupReg |= (uint16_t) (stages & 0x07) << 13; if(synchronize) { - value |= 0x1000; + SweepSetupReg |= 0x1000; } - value |= (port1_stage & 0x07) << 3; - value |= (port2_stage & 0x07) << 0; - WriteRegister(Reg::SweepSetup, value); + SweepSetupReg |= (port1_stage & 0x07) << 3; + SweepSetupReg |= (port2_stage & 0x07) << 0; + WriteRegister(Reg::SweepSetup, SweepSetupReg); Enable(Periphery::SyncMaster, syncMaster); } +void FPGA::SetCDSEnabled(bool enabled) { + if(enabled) { + SweepSetupReg |= 0x0800; // Set bit 11 + } else { + SweepSetupReg &= ~0x0800; // Clear bit 11 + } + WriteRegister(Reg::SweepSetup, SweepSetupReg); +} + void FPGA::Enable(Periphery p, bool enable) { if (enable) { SysCtrlReg |= (uint16_t) p; @@ -210,9 +221,8 @@ void FPGA::WriteMAX2871Default(uint32_t *DefaultRegs) { } void FPGA::WriteSweepConfig(uint16_t pointnum, bool lowband, uint32_t *SourceRegs, uint32_t *LORegs, - uint8_t attenuation, uint64_t frequency, Samples samples, bool halt, LowpassFilter filter, - uint16_t sourcePhase) { - uint16_t send[8]; + uint8_t attenuation, uint64_t frequency, Samples samples, bool halt, LowpassFilter filter) { + uint16_t send[7]; // select which point this sweep config is for send[0] = pointnum & 0x1FFF; // assemble sweep config from required fields of PLL registers @@ -230,48 +240,48 @@ void FPGA::WriteSweepConfig(uint16_t pointnum, bool lowband, uint32_t *SourceReg uint16_t Source_Power = (SourceRegs[4] & 0x00000018) >> 3; - // Config memory layout (112 bits): - // bits 111:100 - Source Phase P[11:0] - // bits 99:96 - Reserved - // bits 95:0 - Original 96-bit config + // Config memory layout (96 bits): + // bits 95:80 - LO_M[11:4], halt, LO_N[6], Source_N[6], samples[2:0], filter[1:0] + // bits 79:64 - LO_M[3:0], LO_FRAC[11:0] + // bits 63:48 - LO_DIV_A[2:0], LO_VCO[5:0], LO_N[5:0], lowband + // bits 47:32 - Source_Power[1:0], attenuation[6:0], Source_M[11:5] + // bits 31:16 - Source_M[4:0], Source_FRAC[11:1] + // bits 15:0 - Source_FRAC[0], Source_DIV_A[2:0], Source_VCO[5:0], Source_N[5:0] - // send[1]: bits 111:96 - Source Phase (12 bits) + Reserved (4 bits) - send[1] = (sourcePhase & 0x0FFF) << 4; // Phase in bits 15:4, reserved bits 3:0 - - // send[2]: bits 95:80 - LO_M[11:4], halt, LO_N[6], Source_N[6], samples[2:0], filter[1:0] - send[2] = LO_M >> 4; + // send[1]: bits 95:80 + send[1] = LO_M >> 4; if (halt) { - send[2] |= 0x8000; + send[1] |= 0x8000; } if(LO_N & 0x40) { - send[2] |= 0x4000; + send[1] |= 0x4000; } if(Source_N & 0x40) { - send[2] |= 0x2000; + send[1] |= 0x2000; } - send[2] |= (int) samples << 10; + send[1] |= (int) samples << 10; if(filter == LowpassFilter::Auto) { // Select source LP filter if (frequency >= 3500000000) { - send[2] |= (int) LowpassFilter::None << 8; + send[1] |= (int) LowpassFilter::None << 8; } else if (frequency >= 1800000000) { - send[2] |= (int) LowpassFilter::M3500 << 8; + send[1] |= (int) LowpassFilter::M3500 << 8; } else if (frequency >= 900000000) { - send[2] |= (int) LowpassFilter::M1880 << 8; + send[1] |= (int) LowpassFilter::M1880 << 8; } else { - send[2] |= (int) LowpassFilter::M947 << 8; + send[1] |= (int) LowpassFilter::M947 << 8; } } else { - send[2] |= (int) filter << 8; + send[1] |= (int) filter << 8; } - send[3] = (LO_M & 0x000F) << 12 | LO_FRAC; - send[4] = LO_DIV_A << 13 | LO_VCO << 7 | (LO_N & 0x3F) << 1; + send[2] = (LO_M & 0x000F) << 12 | LO_FRAC; + send[3] = LO_DIV_A << 13 | LO_VCO << 7 | (LO_N & 0x3F) << 1; if (lowband) { - send[4] |= 0x0001; + send[3] |= 0x0001; } - send[5] = Source_Power << 14 | (uint16_t) attenuation << 7 | Source_M >> 5; - send[6] = (Source_M & 0x001F) << 11 | Source_FRAC >> 1; - send[7] = (Source_FRAC & 0x0001) << 15 | Source_DIV_A << 12 | Source_VCO << 6 | (Source_N & 0x3F); + send[4] = Source_Power << 14 | (uint16_t) attenuation << 7 | Source_M >> 5; + send[5] = (Source_M & 0x001F) << 11 | Source_FRAC >> 1; + send[6] = (Source_FRAC & 0x0001) << 15 | Source_DIV_A << 12 | Source_VCO << 6 | (Source_N & 0x3F); SwitchBytes(send[0]); SwitchBytes(send[1]); SwitchBytes(send[2]); @@ -279,9 +289,8 @@ void FPGA::WriteSweepConfig(uint16_t pointnum, bool lowband, uint32_t *SourceReg SwitchBytes(send[4]); SwitchBytes(send[5]); SwitchBytes(send[6]); - SwitchBytes(send[7]); Low(CS); - HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) send, 16, 100); + HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) send, 14, 100); High(CS); } @@ -333,8 +342,10 @@ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { result.P2Q = assembleSampleResultValue(&raw[14]); result.RefI = assembleSampleResultValue(&raw[8]); result.RefQ = assembleSampleResultValue(&raw[2]); - result.pointNum = (uint16_t)(raw[38]&0x1F) << 8 | raw[39]; + // RESULT_INDEX format: stage[2:0] & point[11:0] & cds_phase result.stageNum = (raw[38] & 0xE0) >> 5; + result.pointNum = ((uint16_t)(raw[38] & 0x1F) << 7) | (raw[39] >> 1); + result.cdsPhase = raw[39] & 0x01; High(CS); busy_reading = false; if ((status & 0x0004) && callback) { diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp index b060190..b3f002c 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp @@ -37,8 +37,9 @@ using SamplingResult = struct _samplingresult { int64_t P1I, P1Q; int64_t P2I, P2Q; int64_t RefI, RefQ; - uint16_t pointNum :13; + uint16_t pointNum :12; uint16_t stageNum :3; + uint16_t cdsPhase :1; // 0 = first measurement (phase 0), 1 = second measurement (phase 180) }; using DFTResult = struct _dftresult { @@ -120,8 +121,8 @@ void DisableInterrupt(Interrupt i); void DisableAllInterrupts(); void WriteMAX2871Default(uint32_t *DefaultRegs); void WriteSweepConfig(uint16_t pointnum, bool lowband, uint32_t *SourceRegs, uint32_t *LORegs, - uint8_t attenuation, uint64_t frequency, Samples samples, bool halt = false, LowpassFilter filter = LowpassFilter::Auto, - uint16_t sourcePhase = 0); + uint8_t attenuation, uint64_t frequency, Samples samples, bool halt = false, LowpassFilter filter = LowpassFilter::Auto); +void SetCDSEnabled(bool enabled); using ReadCallback = void(*)(const SamplingResult &result); bool InitiateSampleRead(ReadCallback cb); void SetupDFT(uint32_t f_firstBin, uint32_t f_binSpacing); diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp index 81f784d..00f07c3 100644 --- a/Software/VNA_embedded/Application/VNA.cpp +++ b/Software/VNA_embedded/Application/VNA.cpp @@ -55,14 +55,13 @@ static constexpr uint8_t PLLRefFreqsNum = sizeof(PLLRefFreqs)/sizeof(PLLRefFreqs static uint8_t sourceRefIndex, LO1RefIndex; // Correlated Double Sampling (CDS) state -static uint8_t cdsPhaseCount; // Number of phase samples (0 or 2-7) +// CDS is now handled by FPGA which takes 2 measurements at 0° and 180° phase +static bool cdsEnabled; // CDS accumulators for weighted samples, per stage (max 8 stages) // I and Q for each receiver: Port1, Port2, Reference static double cdsAccumP1I[8], cdsAccumP1Q[8]; static double cdsAccumP2I[8], cdsAccumP2Q[8]; static double cdsAccumRefI[8], cdsAccumRefQ[8]; -// Precomputed cosine weights for CDS -static float cdsWeights[7]; // Max 7 phases using namespace HWHAL; @@ -177,34 +176,20 @@ bool VNA::Setup(Protocol::SweepSettings s) { logMultiplier = pow((double) settings.f_stop / settings.f_start, 1.0 / (settings.points-1)); // Initialize Correlated Double Sampling (CDS) - cdsPhaseCount = settings.cdsPhases >= 2 ? settings.cdsPhases : 0; + // CDS is now handled by FPGA with 180° phase shift (2 measurements per point) + cdsEnabled = settings.cdsPhases >= 2; // Clear per-stage accumulators for(uint8_t stg = 0; stg < 8; stg++) { cdsAccumP1I[stg] = cdsAccumP1Q[stg] = 0; cdsAccumP2I[stg] = cdsAccumP2Q[stg] = 0; cdsAccumRefI[stg] = cdsAccumRefQ[stg] = 0; } - // Precompute cosine weights: cos(2*pi*k/N) - if(cdsPhaseCount >= 2) { - for(uint8_t k = 0; k < cdsPhaseCount; k++) { - cdsWeights[k] = cosf(2.0f * M_PI * k / cdsPhaseCount); - } - } - // Calculate internal point count (multiply by CDS phases if enabled) - uint16_t internalPoints = settings.points; - if(cdsPhaseCount >= 2) { - internalPoints = settings.points * cdsPhaseCount; - if(internalPoints > FPGA::MaxPoints) { - // Reduce user points to fit - settings.points = FPGA::MaxPoints / cdsPhaseCount; - internalPoints = settings.points * cdsPhaseCount; - LOG_WARN("CDS: reduced points to %u to fit FPGA limit", settings.points); - } - } + // Configure CDS in FPGA (FPGA handles the phase switching internally) + FPGA::SetCDSEnabled(cdsEnabled); // Configure sweep - FPGA::SetNumberOfPoints(internalPoints); + FPGA::SetNumberOfPoints(settings.points); uint32_t samplesPerPoint = (HW::getADCRate() / s.if_bandwidth); // round up to next multiple of 16 (16 samples are spread across 5 IF2 periods) if(samplesPerPoint%16) { @@ -324,33 +309,11 @@ bool VNA::Setup(Protocol::SweepSettings s) { needs_halt = true; } - // Write sweep config(s) for this user point - if(cdsPhaseCount >= 2) { - // CDS enabled: write N configs with different phases - // Extract Source M from PLL registers for phase calculation - uint32_t* sourceRegs = Source.GetRegisters(); - uint16_t Source_M = (sourceRegs[1] & 0x00007FF8) >> 3; - - for(uint8_t k = 0; k < cdsPhaseCount; k++) { - uint16_t internalPointNum = i * cdsPhaseCount + k; - // Calculate phase: sourcePhase = M * k / N - // This gives phase = k * 360 / N degrees - uint16_t sourcePhase = (uint16_t)((uint32_t)Source_M * k / cdsPhaseCount); - - // Only halt on first CDS phase of each point (if needed) - bool pointHalt = (k == 0) ? needs_halt : false; - - FPGA::WriteSweepConfig(internalPointNum, lowband, sourceRegs, - LO1.GetRegisters(), attenuator, freq, - FPGA::Samples::SPPRegister, pointHalt, FPGA::LowpassFilter::Auto, - sourcePhase); - } - } else { - // No CDS: single config per point - FPGA::WriteSweepConfig(i, lowband, Source.GetRegisters(), - LO1.GetRegisters(), attenuator, freq, - FPGA::Samples::SPPRegister, needs_halt); - } + // Write sweep config for this point + // CDS is handled by FPGA internally - it takes 2 measurements per point when enabled + FPGA::WriteSweepConfig(i, lowband, Source.GetRegisters(), + LO1.GetRegisters(), attenuator, freq, + FPGA::Samples::SPPRegister, needs_halt); last_lowband = lowband; } // reset a possibly changed PLL reference index @@ -428,19 +391,18 @@ bool VNA::MeasurementDone(const FPGA::SamplingResult &result) { if(!active) { return false; } - if(result.pointNum != pointCnt || result.stageNum != stageCnt) { - LOG_WARN("Indicated point does not match (%u != %u, %d != %d)", result.pointNum, pointCnt, result.stageNum, stageCnt); - FPGA::AbortSweep(); - return false; - } - if(cdsPhaseCount >= 2) { - // CDS mode: accumulate weighted samples per stage - // Internal point mapping: user_point = pointCnt / cdsPhaseCount - // cds_phase = pointCnt % cdsPhaseCount - uint16_t userPoint = pointCnt / cdsPhaseCount; - uint8_t cdsPhase = pointCnt % cdsPhaseCount; - float weight = cdsWeights[cdsPhase]; + if(cdsEnabled) { + // CDS mode: FPGA returns 2 measurements per point (cdsPhase 0 and 1) + // Verify point and stage numbers match expected values + if(result.pointNum != pointCnt || result.stageNum != stageCnt) { + LOG_WARN("Indicated point does not match (%u != %u, %d != %d)", result.pointNum, pointCnt, result.stageNum, stageCnt); + FPGA::AbortSweep(); + return false; + } + + // Weight: +1.0 for phase 0 (0°), -1.0 for phase 1 (180°) + float weight = result.cdsPhase == 0 ? 1.0f : -1.0f; // Accumulate weighted values into per-stage accumulators cdsAccumP1I[stageCnt] += result.P1I * weight; @@ -450,24 +412,24 @@ bool VNA::MeasurementDone(const FPGA::SamplingResult &result) { cdsAccumRefI[stageCnt] += result.RefI * weight; cdsAccumRefQ[stageCnt] += result.RefQ * weight; - // Check if all stages for this internal point are complete + // Check if all stages for this CDS phase are complete stageCnt++; if(stageCnt > settings.stages) { stageCnt = 0; - // Check if this was the last CDS phase for this user point - if(cdsPhase == cdsPhaseCount - 1) { - // All CDS phases complete - add combined values to data + // Check if this was the second CDS phase (phase 1 = 180°) + if(result.cdsPhase == 1) { + // Both CDS phases complete - add combined values to data for(uint8_t stg = 0; stg <= settings.stages; stg++) { data.addValue((int64_t)cdsAccumP1I[stg], (int64_t)cdsAccumP1Q[stg], stg, (int) Protocol::Source::Port1); data.addValue((int64_t)cdsAccumP2I[stg], (int64_t)cdsAccumP2Q[stg], stg, (int) Protocol::Source::Port2); data.addValue((int64_t)cdsAccumRefI[stg], (int64_t)cdsAccumRefQ[stg], stg, (int) Protocol::Source::Port1 | (int) Protocol::Source::Port2 | (int) Protocol::Source::Reference); - // Reset accumulators for next user point + // Reset accumulators for next point cdsAccumP1I[stg] = cdsAccumP1Q[stg] = 0; cdsAccumP2I[stg] = cdsAccumP2Q[stg] = 0; cdsAccumRefI[stg] = cdsAccumRefQ[stg] = 0; } - data.pointNum = userPoint; + data.pointNum = pointCnt; if(zerospan) { uint64_t timestamp = HW::getLastISRTimestamp(); @@ -479,24 +441,31 @@ bool VNA::MeasurementDone(const FPGA::SamplingResult &result) { data.us = timestamp - firstPointTime; } } else { - data.frequency = getPointFrequency(userPoint); - data.cdBm = settings.cdbm_excitation_start + (settings.cdbm_excitation_stop - settings.cdbm_excitation_start) * userPoint / (settings.points - 1); + data.frequency = getPointFrequency(pointCnt); + data.cdBm = settings.cdbm_excitation_start + (settings.cdbm_excitation_stop - settings.cdbm_excitation_start) * pointCnt / (settings.points - 1); } - // Send data for this user point + // Send data for this point STM::DispatchToInterrupt(PassOnData); // Check if sweep is complete - if(userPoint >= settings.points - 1) { + if(pointCnt >= settings.points - 1) { pointCnt = 0; return true; // End of sweep } + pointCnt++; } - pointCnt++; } return false; } + // Non-CDS mode: verify point matches + if(result.pointNum != pointCnt || result.stageNum != stageCnt) { + LOG_WARN("Indicated point does not match (%u != %u, %d != %d)", result.pointNum, pointCnt, result.stageNum, stageCnt); + FPGA::AbortSweep(); + return false; + } + // Non-CDS mode: original behavior data.addValue(result.P1I, result.P1Q, stageCnt, (int) Protocol::Source::Port1); data.addValue(result.P2I, result.P2Q, stageCnt, (int) Protocol::Source::Port2);