diff --git a/Documentation/DeveloperInfo/FPGA_protocol.pdf b/Documentation/DeveloperInfo/FPGA_protocol.pdf index c30225d..e95fab7 100644 Binary files a/Documentation/DeveloperInfo/FPGA_protocol.pdf and b/Documentation/DeveloperInfo/FPGA_protocol.pdf differ diff --git a/Documentation/DeveloperInfo/FPGA_protocol.tex b/Documentation/DeveloperInfo/FPGA_protocol.tex index 459343e..76e55b2 100644 --- a/Documentation/DeveloperInfo/FPGA_protocol.tex +++ b/Documentation/DeveloperInfo/FPGA_protocol.tex @@ -343,10 +343,10 @@ The register contains the number of points per sweep negative one, e.g. set to 1 \rwbits{9}{2}{Window[1:0]} \rwbits{11}{1}{SCEN} \rwbits{12}{1}{LCEN} -\robits{13}{3}{reserved} +\robits{13}{2}{reserved} %\rwbits{13}{1}{EXP2} %\rwbits{14}{1}{EXP1} -%\rwbits{15}{1}{PSEN} +\rwbits{15}{1}{PSEN} \end{tikzpicture} \end{center} \begin{itemize} @@ -426,7 +426,40 @@ Each point in the sweep is done in stages. Each stage consists of (optionally) r \item \textbf{IH:} Individual halt: Sets the behavior of the "halt sweep" bit (see section~\ref{sweepconfig}). If 1, the sampling is halted before each stage. If 0, the sampling is only halted before the point and all stages are executed without additional halts inbetween. \item \textbf{Port 1 stage} Number of stage during which the source signal is routed to port 1. Must not have the same value as Port 2 stage. \item \textbf{Port 2 stage} Number of stage during which the source signal is routed to port 2. Must not have the same value as Port 1 stage. +\end{itemize} +\subsection{Hardware override register: 0x07} +Allows overwriting hardware settings, regardless of whether a sweep is active or not. +\label{reg:hw_override} +\begin{center} +\begin{tikzpicture} +\bitrect{16}{16-\bit} +\rwbits{0}{1}{OWE} +\rwbits{1}{7}{Attenuator[6:0]} +\rwbits{8}{2}{SourceFilter} +\rwbits{10}{1}{BS} +\rwbits{11}{1}{P1EN} +\rwbits{12}{1}{P2EN} +\robits{13}{3}{reserved} +\end{tikzpicture} +\end{center} +\begin{itemize} +\item \textbf{OWE:} Overwrite enable. If 1, this register is used to configure the hardware. If 0, all other bits in the register are ignored. Must be set to 0 for valid sweep operation. +\item \textbf{Attenuator[6:0]:} Attenuator setting +\item \textbf{SourceFilter:} Low pass filter selection for source signal +\begin{center} +\begin{tabular}{ c|c } +Setting & Selected Band\\ + \hline +00 & \SIrange{0}{900}{\mega\hertz}\\ +01 & \SIrange{900}{1800}{\mega\hertz}\\ +10 & \SIrange{1800}{3500}{\mega\hertz}\\ +11 & \SIrange{3500}{6000}{\mega\hertz}\\ +\end{tabular} +\end{center} +\item \textbf{BS: Band select.} Set to 0 for highband, set to 1 for lowband. +\item \textbf{P1EN:} Route signal to port 1. Must not be enabled at the same time as P2EN. +\item \textbf{P2EN:} Route signal to port 2. Must not be enabled at the same time as P1EN. \end{itemize} \subsection{MAX2871 Default Values Registers: 0x08-0x0F} diff --git a/FPGA/VNA/SPIConfig.vhd b/FPGA/VNA/SPIConfig.vhd index 8503c4b..5d7aaa9 100644 --- a/FPGA/VNA/SPIConfig.vhd +++ b/FPGA/VNA/SPIConfig.vhd @@ -72,6 +72,10 @@ entity SPICommands is SWEEP_HALTED : in STD_LOGIC; SWEEP_RESUME : out STD_LOGIC; + -- hardware overwrite signals + SPI_OVERWRITE_ENABLED : out STD_LOGIC; + SPI_OVERWRITE_DATA : out STD_LOGIC_VECTOR(14 downto 0); + -- DFT signals DFT_BIN1_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); DFT_DIFFBIN_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); @@ -166,6 +170,9 @@ begin INTERRUPT_ASSERTED <= '0'; latched_result <= (others => '0'); + SPI_OVERWRITE_ENABLED <= '0'; + SPI_OVERWRITE_DATA <= (others => '0'); + DFT_BIN1_PHASEINC <= (others => '0'); DFT_DIFFBIN_PHASEINC <= (others => '0'); dft_next <= '0'; @@ -249,6 +256,8 @@ begin INDIVIDUAL_HALT <= spi_buf_out(12); 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); + SPI_OVERWRITE_DATA <= spi_buf_out(14 downto 0); when 8 => MAX2871_DEF_0(15 downto 0) <= spi_buf_out; when 9 => MAX2871_DEF_0(31 downto 16) <= spi_buf_out; when 10 => MAX2871_DEF_1(15 downto 0) <= spi_buf_out; diff --git a/FPGA/VNA/VNA.gise b/FPGA/VNA/VNA.gise index 67a2b99..2f95209 100644 --- a/FPGA/VNA/VNA.gise +++ b/FPGA/VNA/VNA.gise @@ -224,7 +224,7 @@ - + @@ -253,7 +253,7 @@ - + @@ -275,7 +275,7 @@ - + @@ -284,7 +284,7 @@ - + @@ -298,7 +298,7 @@ - + @@ -312,7 +312,7 @@ - + @@ -365,7 +365,7 @@ - + diff --git a/FPGA/VNA/top.bin b/FPGA/VNA/top.bin index 1ed3339..061f534 100644 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 index 3879038..a3b61ac 100644 --- a/FPGA/VNA/top.vhd +++ b/FPGA/VNA/top.vhd @@ -268,6 +268,8 @@ architecture Behavioral of top is RESET_MINMAX : out STD_LOGIC; SWEEP_HALTED : in STD_LOGIC; SWEEP_RESUME : out STD_LOGIC; + SPI_OVERWRITE_ENABLED : out STD_LOGIC; + SPI_OVERWRITE_DATA : out STD_LOGIC_VECTOR(14 downto 0); DFT_BIN1_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); DFT_DIFFBIN_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); DFT_RESULT_READY : in STD_LOGIC; @@ -339,6 +341,8 @@ architecture Behavioral of top is signal source_unlocked : std_logic; signal lo_unlocked : std_logic; + signal source_filter : std_logic_vector(1 downto 0); + -- ADC signals signal adc_trigger_sample : std_logic; signal adc_port1_ready : std_logic; @@ -372,8 +376,9 @@ architecture Behavioral of top is signal sweep_port2_stage : STD_LOGIC_VECTOR (2 downto 0); signal sweep_config_data : std_logic_vector(95 downto 0); signal sweep_config_address : std_logic_vector(12 downto 0); - signal source_filter : std_logic_vector(1 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(95 downto 0); @@ -419,6 +424,10 @@ architecture Behavioral of top is signal debug : std_logic_vector(10 downto 0); signal intr : std_logic; + -- HW overwrite signals + signal HW_overwrite_enabled : std_logic; + signal HW_overwrite_data : std_logic_vector(14 downto 0); + -- DFT signals signal dft_bin1_phaseinc : std_logic_vector (15 downto 0); signal dft_diffbin_phaseinc : std_logic_vector (15 downto 0); @@ -436,20 +445,20 @@ begin LEDS(2) <= SOURCE_LD; LEDS(3) <= LO1_LD; -- Sweep and active port - PORT_SELECT2 <= sweep_excite_port2 and portswitch_en; - PORT2_SELECT <= sweep_excite_port2 and portswitch_en; - PORT_SELECT1 <= sweep_excite_port1 and portswitch_en; - PORT1_SELECT <= sweep_excite_port1 and portswitch_en; - BAND_SELECT_HIGH <= not sweep_band; - BAND_SELECT_LOW <= sweep_band; + PORT_SELECT2 <= (sweep_excite_port2 and portswitch_en) when HW_overwrite_enabled = '0' else HW_overwrite_data(3); + PORT2_SELECT <= (sweep_excite_port2 and portswitch_en) when HW_overwrite_enabled = '0' else HW_overwrite_data(3); + PORT_SELECT1 <= (sweep_excite_port1 and portswitch_en) when HW_overwrite_enabled = '0' else HW_overwrite_data(4); + PORT1_SELECT <= (sweep_excite_port1 and portswitch_en) when HW_overwrite_enabled = '0' else HW_overwrite_data(4); + BAND_SELECT_HIGH <= not sweep_band when HW_overwrite_enabled = '0' else not HW_overwrite_data(5); + BAND_SELECT_LOW <= sweep_band when HW_overwrite_enabled = '0' else HW_overwrite_data(5); PORT1_MIX2_EN <= port1mix_en; PORT1_MIX1_EN <= not port1mix_en; PORT2_MIX2_EN <= port2mix_en; PORT2_MIX1_EN <= not port2mix_en; REF_MIX2_EN <= refmix_en; REF_MIX1_EN <= not refmix_en; - LEDS(4) <= not (not sweep_reset and sweep_excite_port2 and portswitch_en); - LEDS(5) <= not (not sweep_reset and sweep_excite_port1 and portswitch_en); + LEDS(4) <= not (not sweep_reset and sweep_excite_port2 and portswitch_en) when HW_overwrite_enabled = '0' else not HW_overwrite_data(3); + LEDS(5) <= not (not sweep_reset and sweep_excite_port1 and portswitch_en) when HW_overwrite_enabled = '0' else not HW_overwrite_data(4); -- Uncommitted LEDs LEDS(7 downto 6) <= user_leds(1 downto 0); --LEDS(7) <= '0'; @@ -675,8 +684,8 @@ begin PLL_LOCKED => plls_locked, SWEEP_HALTED => sweep_halted, SWEEP_RESUME => sweep_resume, - ATTENUATOR => ATTENUATION, - SOURCE_FILTER => source_filter, + ATTENUATOR => sweep_attenuator, + SOURCE_FILTER => sweep_source_filter, STAGES => sweep_stages, INDIVIDUAL_HALT => sweep_individual_halt, PORT1_STAGE => sweep_port1_stage, @@ -689,11 +698,14 @@ begin ); -- Source filter mapping + source_filter <= sweep_source_filter when HW_overwrite_enabled = '0' else HW_overwrite_data(7 downto 6); FILT_IN_C1 <= '0' when source_filter = "00" or source_filter = "10" else '1'; FILT_IN_C2 <= '0' when source_filter = "11" or source_filter = "10" else '1'; FILT_OUT_C1 <= '0' when source_filter = "00" or source_filter = "10" else '1'; FILT_OUT_C2 <= '0' when source_filter = "00" or source_filter = "01" else '1'; + ATTENUATION <= sweep_attenuator when HW_overwrite_enabled = '0' else HW_overwrite_data(14 downto 8); + -- PLL/SPI mux -- only select FPGA SPI slave when both AUX1 and AUX2 are low fpga_select <= nss_sync when aux1_sync = '0' and aux2_sync = '0' else '1'; @@ -756,6 +768,8 @@ begin INDIVIDUAL_HALT => sweep_individual_halt, PORT1_STAGE => sweep_port1_stage, PORT2_STAGE => sweep_port2_stage, + SPI_OVERWRITE_ENABLED => HW_overwrite_enabled, + SPI_OVERWRITE_DATA => HW_overwrite_data, DFT_BIN1_PHASEINC => dft_bin1_phaseinc, DFT_DIFFBIN_PHASEINC => dft_diffbin_phaseinc, DFT_RESULT_READY => dft_ready, diff --git a/Software/VNA_embedded/Application/App.cpp b/Software/VNA_embedded/Application/App.cpp index 25e4322..048d4dd 100644 --- a/Software/VNA_embedded/Application/App.cpp +++ b/Software/VNA_embedded/Application/App.cpp @@ -259,6 +259,7 @@ inline void App_Process() { // insert the last received packet (restarts the timed out operation) USBPacketReceived(last_measure_packet); } + HW::updateDeviceStatus(); } } diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp index 7f47c4e..2181eaa 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp @@ -356,6 +356,27 @@ uint16_t FPGA::GetStatus() { return (uint16_t) status[0] << 8 | status[1]; } +void FPGA::OverwriteHardware(uint8_t attenuation, LowpassFilter filter, bool lowband, bool port1_enabled, bool port2_enabled) { + uint16_t val = 0; + val |= 0x8000; // enable overwrite + val |= (attenuation & 0x7F) << 8; + val |= (int) filter << 6; + if (lowband) { + val |= 0x0020; + } + if (port1_enabled) { + val |= 0x0010; + } + if (port2_enabled) { + val |= 0x0008; + } + WriteRegister(Reg::HardwareOverwrite, val); +} + +void FPGA::DisableHardwareOverwrite() { + WriteRegister(Reg::HardwareOverwrite, 0x0000); +} + FPGA::ADCLimits FPGA::GetADCLimits() { uint16_t cmd = 0xE000; SwitchBytes(cmd); diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp index 98cd586..149870b 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp @@ -18,6 +18,7 @@ enum class Reg { ADCPrescaler = 0x04, PhaseIncrement = 0x05, SweepSetup = 0x06, + HardwareOverwrite = 0x07, MAX2871Def0LSB = 0x08, MAX2871Def0MSB = 0x09, MAX2871Def1LSB = 0x0A, @@ -134,6 +135,9 @@ void ResetADCLimits(); void ResumeHaltedSweep(); uint16_t GetStatus(); +void OverwriteHardware(uint8_t attenuation, LowpassFilter filter, bool lowband, bool port1_enabled, bool port2_enabled); +void DisableHardwareOverwrite(); + void StartSweep(); void AbortSweep(); diff --git a/Software/VNA_embedded/Application/Generator.cpp b/Software/VNA_embedded/Application/Generator.cpp index acbb85c..4bdc682 100644 --- a/Software/VNA_embedded/Application/Generator.cpp +++ b/Software/VNA_embedded/Application/Generator.cpp @@ -2,73 +2,59 @@ #include "Generator.hpp" #include "Manual.hpp" #include "Hardware.hpp" +#include "HW_HAL.hpp" #include "max2871.hpp" #include "Si5351C.hpp" -void Generator::Setup(Protocol::GeneratorSettings g) { - if(g.activePort == 0) { - // both ports disabled, no need to configure PLLs - HW::SetIdle(); - return; - } - Protocol::ManualControlV1 m; - // LOs not required - m.LO1CE = 0; - m.LO1Frequency = 1000000000; - m.LO1RFEN = 0; - m.LO1RFEN = 0; - m.LO2EN = 0; - m.LO2Frequency = 60000000; - m.Port1EN = 0; - m.Port2EN = 0; - m.RefEN = 0; - m.Samples = 131072; - m.WindowType = (int) FPGA::Window::None; +using namespace HWHAL; - switch(g.activePort) { - case 1: - m.AmplifierEN = 1; - m.PortSwitch = 0; - break; - case 2: - m.AmplifierEN = 1; - m.PortSwitch = 1; - break; +void Generator::Setup(Protocol::GeneratorSettings g) { + HW::SetMode(HW::Mode::Generator); + if(g.activePort == 0) { + // both ports disabled, no need to configure PLLs + Si5351.Disable(SiChannel::LowbandSource); + FPGA::Disable(FPGA::Periphery::SourceChip); + FPGA::Disable(FPGA::Periphery::Amplifier); + FPGA::Disable(FPGA::Periphery::SourceRF); + FPGA::Disable(FPGA::Periphery::PortSwitch); + FPGA::DisableHardwareOverwrite(); + return; } + g.frequency = Cal::FrequencyCorrectionToDevice(g.frequency); auto amplitude = HW::GetAmplitudeSettings(g.cdbm_level, g.frequency, g.applyAmplitudeCorrection, g.activePort == 2); // Select correct source + bool bandSelect; + FPGA::LowpassFilter lp = FPGA::LowpassFilter::M947; if(g.frequency < HW::BandSwitchFrequency) { - m.SourceLowEN = 1; - m.SourceLowFrequency = g.frequency; - m.SourceHighCE = 0; - m.SourceHighRFEN = 0; - m.SourceHighFrequency = HW::BandSwitchFrequency; - m.SourceHighLowpass = (int) FPGA::LowpassFilter::M947; - m.SourceHighPower = (int) MAX2871::Power::n4dbm; - m.SourceHighband = false; - m.SourceLowPower = (int) amplitude.lowBandPower; + bandSelect = true; + FPGA::Disable(FPGA::Periphery::SourceChip); + Si5351.SetCLK(SiChannel::LowbandSource, g.frequency, Si5351C::PLL::B, + amplitude.lowBandPower); + Si5351.Enable(SiChannel::LowbandSource); } else { - m.SourceLowEN = 0; - m.SourceLowFrequency = HW::BandSwitchFrequency; - m.SourceHighCE = 1; - m.SourceHighRFEN = 1; - m.SourceHighFrequency = g.frequency; + bandSelect = false; + Si5351.Disable(SiChannel::LowbandSource); + FPGA::Enable(FPGA::Periphery::SourceChip); + FPGA::SetMode(FPGA::Mode::SourcePLL); + Source.SetPowerOutA(amplitude.highBandPower); + Source.SetFrequency(g.frequency); + Source.Update(); + FPGA::SetMode(FPGA::Mode::FPGA); if(g.frequency < 900000000UL) { - m.SourceHighLowpass = (int) FPGA::LowpassFilter::M947; + lp = FPGA::LowpassFilter::M947; } else if(g.frequency < 1800000000UL) { - m.SourceHighLowpass = (int) FPGA::LowpassFilter::M1880; + lp = FPGA::LowpassFilter::M1880; } else if(g.frequency < 3500000000UL) { - m.SourceHighLowpass = (int) FPGA::LowpassFilter::M3500; + lp = FPGA::LowpassFilter::M3500; } else { - m.SourceHighLowpass = (int) FPGA::LowpassFilter::None; + lp = FPGA::LowpassFilter::None; } - m.SourceHighband = true; - m.SourceHighPower = (int) amplitude.highBandPower; - m.SourceLowPower = (int) Si5351C::DriveStrength::mA2; } - m.attenuator = amplitude.attenuator; - Manual::Setup(m); + FPGA::OverwriteHardware(amplitude.attenuator, lp, bandSelect, g.activePort == 1, g.activePort == 2); HW::SetOutputUnlevel(amplitude.unlevel); + FPGA::Enable(FPGA::Periphery::Amplifier, true); + FPGA::Enable(FPGA::Periphery::SourceRF, true); + FPGA::Enable(FPGA::Periphery::PortSwitch, true); } diff --git a/Software/VNA_embedded/Application/Hardware.cpp b/Software/VNA_embedded/Application/Hardware.cpp index f4a3d0a..200d5ef 100644 --- a/Software/VNA_embedded/Application/Hardware.cpp +++ b/Software/VNA_embedded/Application/Hardware.cpp @@ -8,6 +8,7 @@ #include "Manual.hpp" #include "delay.hpp" #include "SpectrumAnalyzer.hpp" +#include "Communication.h" #include #define LOG_LEVEL LOG_LEVEL_INFO @@ -139,6 +140,8 @@ bool HW::Init() { return false; } + FPGA::DisableHardwareOverwrite(); + // Set default ADC samplerate FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, ADCprescaler); // Set phase increment according to @@ -229,6 +232,7 @@ void HW::SetIdle() { unlevel = false; FPGA::AbortSweep(); FPGA::SetMode(FPGA::Mode::FPGA); + FPGA::DisableHardwareOverwrite(); FPGA::Enable(FPGA::Periphery::SourceChip, false); FPGA::Enable(FPGA::Periphery::SourceRF, false); FPGA::Enable(FPGA::Periphery::LO1Chip, false); @@ -289,7 +293,7 @@ HW::AmplitudeSettings HW::GetAmplitudeSettings(int16_t cdbm, uint64_t freq, bool bool HW::TimedOut() { constexpr uint64_t timeout = 1000000; - if(activeMode != Mode::Idle && Delay::get_us() - lastISR > timeout) { + if(activeMode != Mode::Idle && activeMode != Mode::Generator && Delay::get_us() - lastISR > timeout) { return true; } else { return false; @@ -420,6 +424,28 @@ uint64_t HW::getLastISRTimestamp() { return lastISR; } +void HW::updateDeviceStatus() { + if(activeMode == Mode::Idle || activeMode == Mode::Generator) { + static uint32_t last_update = 0; + if(HAL_GetTick() - last_update >= 1000) { + last_update = HAL_GetTick(); + HW::Ref::update(); + Protocol::PacketInfo packet; + packet.type = Protocol::PacketType::DeviceStatusV1; + // Enable PLL chips for temperature reading + bool srcEn = FPGA::IsEnabled(FPGA::Periphery::SourceChip); + bool LOEn = FPGA::IsEnabled(FPGA::Periphery::LO1Chip); + FPGA::Enable(FPGA::Periphery::SourceChip); + FPGA::Enable(FPGA::Periphery::LO1Chip); + HW::getDeviceStatus(&packet.statusV1, true); + // restore PLL state + FPGA::Enable(FPGA::Periphery::SourceChip, srcEn); + FPGA::Enable(FPGA::Periphery::LO1Chip, LOEn); + Communication::Send(packet); + } + } +} + uint16_t HW::getDFTPhaseInc() { return DFTphaseInc; } diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp index e81a26f..a6d029d 100644 --- a/Software/VNA_embedded/Application/Hardware.hpp +++ b/Software/VNA_embedded/Application/Hardware.hpp @@ -88,6 +88,7 @@ static constexpr Protocol::DeviceInfo Info = { enum class Mode { Idle, Manual, + Generator, VNA, SA, }; @@ -101,6 +102,8 @@ uint64_t getLastISRTimestamp(); void SetOutputUnlevel(bool unlev); +void updateDeviceStatus(); + using AmplitudeSettings = struct _amplitudeSettings { uint8_t attenuator; union {