diff --git a/Documentation/DeveloperInfo/FPGA_protocol.pdf b/Documentation/DeveloperInfo/FPGA_protocol.pdf index e93a2bc..0e4cacd 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 10b061a..9660519 100644 --- a/Documentation/DeveloperInfo/FPGA_protocol.tex +++ b/Documentation/DeveloperInfo/FPGA_protocol.tex @@ -448,30 +448,6 @@ In addition to the single bin DFT configured through the ADC prescaler and phase The DFT has a fixed number of bins (64), but the frequencies these bins correspond to can be changed. -\subsubsection{Number of samples: 0x10} -\label{reg:dft-nsamples} -\begin{center} -\begin{tikzpicture} -\bitrect{16}{16-\bit} -\rwbits{0}{16}{DFT\_SAMPLES[15:0]} -\end{tikzpicture} -\end{center} -\begin{itemize} -\item \textbf{DFT\_SAMPLES[15:0]:} Amount of samples the DFT should take. This should always match the number of samples configured with the Samples Per Point register (see~\ref{reg:spp}). -\end{itemize} - -\subsubsection{Window increment: 0x11} -\begin{center} -\begin{tikzpicture} -\bitrect{16}{16-\bit} -\rwbits{0}{16}{DFT\_WINDOW\_INC[15:0]} -\end{tikzpicture} -\end{center} -\begin{itemize} -\item \textbf{DFT\_WINDOW\_INC[15:0]:} Window increment value after each sample. This should always be set in such a way, that the window is iterated over exactly once after all samples configured with DFT\_SAMPLES (see \ref{reg:dft-nsamples}) have been taken. -$$ TODO: formula $$ -\end{itemize} - \subsubsection{DFT\_FIRST\_BIN: 0x12} \begin{center} \begin{tikzpicture} diff --git a/FPGA/VNA/DFT.vhd b/FPGA/VNA/DFT.vhd index 46a5666..9a90d40 100644 --- a/FPGA/VNA/DFT.vhd +++ b/FPGA/VNA/DFT.vhd @@ -36,11 +36,9 @@ entity DFT is PORT1 : in STD_LOGIC_VECTOR (15 downto 0); PORT2 : in STD_LOGIC_VECTOR (15 downto 0); NEW_SAMPLE : in STD_LOGIC; - NSAMPLES : in STD_LOGIC_VECTOR (15 downto 0); + NSAMPLES : in STD_LOGIC_VECTOR (12 downto 0); BIN1_PHASEINC : in STD_LOGIC_VECTOR (15 downto 0); DIFFBIN_PHASEINC : in STD_LOGIC_VECTOR (15 downto 0); - WINDOW_INC : in STD_LOGIC_VECTOR (15 downto 0); - WINDOW_TYPE : in STD_LOGIC_VECTOR (1 downto 0); RESULT_READY : out STD_LOGIC; OUTPUT : out STD_LOGIC_VECTOR (191 downto 0); NEXT_OUTPUT : in STD_LOGIC); @@ -85,6 +83,17 @@ COMPONENT SinCosMult p : OUT STD_LOGIC_VECTOR(31 DOWNTO 0) ); END COMPONENT; +COMPONENT DSP_SLICE + PORT ( + clk : IN STD_LOGIC; + ce : IN STD_LOGIC; + sel : IN STD_LOGIC_VECTOR(0 DOWNTO 0); + a : IN STD_LOGIC_VECTOR(17 DOWNTO 0); + b : IN STD_LOGIC_VECTOR(17 DOWNTO 0); + c : IN STD_LOGIC_VECTOR(47 DOWNTO 0); + p : OUT STD_LOGIC_VECTOR(47 DOWNTO 0) + ); +END COMPONENT; COMPONENT window PORT( CLK : IN std_logic; @@ -105,7 +114,8 @@ END COMPONENT; --signal port2_real_read : std_logic_vector(47 downto 0); --signal port2_imag_read : std_logic_vector(47 downto 0); - signal sample_cnt : integer range 0 to 65535; + signal sample_cnt : integer range 0 to 131072; + signal samples_to_take : integer range 0 to 131072; signal bin_cnt : integer range 0 to BINS+2; signal read_address : integer range 0 to BINS-1; @@ -116,35 +126,38 @@ END COMPONENT; signal ram_in : std_logic_vector(191 downto 0); signal ram_out : std_logic_vector(191 downto 0); - type States is (WaitingForSample, WaitMult, WaitMult2, PhaseReady, WindowingReady, WaitSinCos, Busy, Ready); + type States is (WaitingForSample, WaitMult, WaitSinCos, Busy, Ready); signal state : States; signal port1_latch : std_logic_vector(15 downto 0); signal port2_latch : std_logic_vector(15 downto 0); - signal window_index : std_logic_vector(15 downto 0); - - signal window_value : std_logic_vector(15 downto 0); - signal phase : std_logic_vector(31 downto 0); signal phase_inc : std_logic_vector(31 downto 0); signal sine : std_logic_vector(15 downto 0); signal cosine : std_logic_vector(15 downto 0); - signal mult1_a : std_logic_vector(15 downto 0); - signal mult1_b : std_logic_vector(15 downto 0); - signal mult1_p : std_logic_vector(31 downto 0); + signal mult1_a : std_logic_vector(17 downto 0); + signal mult1_b : std_logic_vector(17 downto 0); + signal mult1_c : std_logic_vector(47 downto 0); + signal mult1_p : std_logic_vector(47 downto 0); - signal mult2_a : std_logic_vector(15 downto 0); - signal mult2_b : std_logic_vector(15 downto 0); - signal mult2_p : std_logic_vector(31 downto 0); + signal mult2_a : std_logic_vector(17 downto 0); + signal mult2_b : std_logic_vector(17 downto 0); + signal mult2_c : std_logic_vector(47 downto 0); + signal mult2_p : std_logic_vector(47 downto 0); - signal mult3_a : std_logic_vector(15 downto 0); - signal mult3_b : std_logic_vector(15 downto 0); - signal mult3_p : std_logic_vector(31 downto 0); + signal mult3_a : std_logic_vector(17 downto 0); + signal mult3_b : std_logic_vector(17 downto 0); + signal mult3_c : std_logic_vector(47 downto 0); + signal mult3_p : std_logic_vector(47 downto 0); - signal mult4_a : std_logic_vector(15 downto 0); - signal mult4_b : std_logic_vector(15 downto 0); - signal mult4_p : std_logic_vector(31 downto 0); + signal mult4_a : std_logic_vector(17 downto 0); + signal mult4_b : std_logic_vector(17 downto 0); + signal mult4_c : std_logic_vector(47 downto 0); + signal mult4_p : std_logic_vector(47 downto 0); + + signal mult_enable : std_logic; + signal mult_accumulate : std_logic_vector(0 downto 0); begin LookupTable : SinCos @@ -154,72 +167,75 @@ begin cosine => cosine, sine => sine ); - Mult1 : SinCosMult + Mult1 : DSP_SLICE PORT MAP ( clk => CLK, + ce => mult_enable, + sel => mult_accumulate, a => mult1_a, b => mult1_b, + c => mult1_c, p => mult1_p ); - Mult2 : SinCosMult + Mult2 : DSP_SLICE PORT MAP ( clk => CLK, + ce => mult_enable, + sel => mult_accumulate, a => mult2_a, b => mult2_b, + c => mult2_c, p => mult2_p ); - Mult3 : SinCosMult + Mult3 : DSP_SLICE PORT MAP ( clk => CLK, + ce => mult_enable, + sel => mult_accumulate, a => mult3_a, b => mult3_b, + c => mult3_c, p => mult3_p ); - Mult4 : SinCosMult + Mult4 : DSP_SLICE PORT MAP ( clk => CLK, + ce => mult_enable, + sel => mult_accumulate, a => mult4_a, b => mult4_b, + c => mult4_c, p => mult4_p ); - WindowROM: window PORT MAP( - CLK => CLK, - INDEX => window_index(15 downto 9), - WINDOW_TYPE => WINDOW_TYPE, - VALUE => window_value - ); --- result_ram: dft_result --- GENERIC MAP(depth => BINS) --- PORT MAP( --- CLK => CLK, --- READ_ADDRESS => read_address, --- WRITE_ADDRESS => write_address, --- DATA_IN => ram_in, --- DATA_OUT => ram_out, --- WE => we --- ); - -your_instance_name : result_bram - PORT MAP ( - clka => CLK, - wea => we, - addra => write_address_vector, - dina => ram_in, - clkb => CLK, - addrb => read_address_vector, - doutb => ram_out - ); + result_ram: result_bram + PORT MAP ( + clka => CLK, + wea => we, + addra => write_address_vector, + dina => ram_in, + clkb => CLK, + addrb => read_address_vector, + doutb => ram_out + ); read_address_vector <= std_logic_vector(to_unsigned(read_address, 6)); write_address_vector <= std_logic_vector(to_unsigned(write_address, 6)); OUTPUT <= ram_out; + mult1_c <= ram_out(191 downto 144); + mult2_c <= ram_out(143 downto 96); + mult3_c <= ram_out(95 downto 48); + mult4_c <= ram_out(47 downto 0); + + ram_in <= mult1_p & mult2_p & mult3_p & mult4_p; + process(CLK, RESET) begin if rising_edge(CLK) then if RESET = '1' then - window_index <= (others => '0'); + mult_enable <= '0'; + mult_accumulate <= "0"; sample_cnt <= 0; RESULT_READY <= '0'; read_address <= 0; @@ -227,108 +243,124 @@ your_instance_name : result_bram we <= "0"; state <= WaitingForSample; else + samples_to_take <= to_integer(unsigned(NSAMPLES & "0000")) - 1; case state is when WaitingForSample => we <= "0"; + mult_enable <= '0'; + mult_accumulate <= "0"; + read_address <= 0; + write_address <= 0; if NEW_SAMPLE = '1' then -- calculate phase for initial bin - -- TODO this should use unsigned multiplication - mult1_a <= std_logic_vector(to_unsigned(sample_cnt, 16)); - mult1_b <= BIN1_PHASEINC; - mult2_a <= std_logic_vector(to_unsigned(sample_cnt, 16)); - mult2_b <= DIFFBIN_PHASEINC; - -- window ADC data - mult3_a <= PORT1; - mult3_b <= window_value; - mult4_a <= PORT2; - mult4_b <= window_value; + mult1_a <= std_logic_vector(to_unsigned(sample_cnt, 18)); + mult1_b <= "00" & BIN1_PHASEINC; + mult2_a <= std_logic_vector(to_unsigned(sample_cnt, 18)); + mult2_b <= "00" & DIFFBIN_PHASEINC; state <= WaitMult; - read_address <= 0; + bin_cnt <= 0; + mult_enable <= '1'; end if; when WaitMult => RESULT_READY <= '0'; we <= "0"; - state <= WaitMult2; - when WaitMult2 => - RESULT_READY <= '0'; - we <= "0"; - state <= PhaseReady; - when PhaseReady => - RESULT_READY <= '0'; - we <= "0"; - -- initial phase is ready - phase <= mult1_p(15 downto 0) & "0000000000000000"; - phase_inc <= mult2_p(23 downto 0) & "00000000"; - state <= WindowingReady; - when WindowingReady => - RESULT_READY <= '0'; - we <= "0"; - phase <= std_logic_vector(unsigned(phase)+unsigned(phase_inc)); - port1_latch <= mult3_p(31 downto 16); - port2_latch <= mult4_p(31 downto 16); - bin_cnt <= 0; - state <= WaitSinCos; - when WaitSinCos => - phase <= std_logic_vector(unsigned(phase)+unsigned(phase_inc)); - RESULT_READY <= '0'; - we <= "0"; + mult_enable <= '1'; + mult_accumulate <= "0"; + read_address <= 0; + write_address <= 0; if bin_cnt < 4 then bin_cnt <= bin_cnt + 1; else bin_cnt <= 0; + mult_enable <= '0'; + state <= WaitSinCos; + phase <= mult1_p(15 downto 0) & "0000000000000000"; + phase_inc <= mult2_p(23 downto 0) & "00000000"; + port1_latch <= PORT1; + port2_latch <= PORT2; + end if; + when WaitSinCos => + phase <= std_logic_vector(unsigned(phase)+unsigned(phase_inc)); + RESULT_READY <= '0'; + we <= "0"; + mult_enable <= '0'; + mult_accumulate <= "0"; + read_address <= 0; + write_address <= 0; + if bin_cnt < 6 then + bin_cnt <= bin_cnt + 1; + else + bin_cnt <= 0; + mult_enable <= '1'; + read_address <= 1; + -- sign extended multiplication + mult1_a <= port1_latch(15) & port1_latch(15) & port1_latch; + mult1_b <= sine(15) & sine(15) & sine; + mult2_a <= port1_latch(15) & port1_latch(15) & port1_latch; + mult2_b <= cosine(15) & cosine(15) & cosine; + mult3_a <= port2_latch(15) & port2_latch(15) & port2_latch; + mult3_b <= sine(15) & sine(15) & sine; + mult4_a <= port2_latch(15) & port2_latch(15) & port2_latch; + mult4_b <= cosine(15) & cosine(15) & cosine; state <= BUSY; + if sample_cnt = 0 then + mult_accumulate <= "0"; + else + mult_accumulate <= "1"; + end if; end if; when BUSY => + mult_enable <= '1'; + if sample_cnt = 0 then + mult_accumulate <= "0"; + else + mult_accumulate <= "1"; + end if; RESULT_READY <= '0'; phase <= std_logic_vector(unsigned(phase)+unsigned(phase_inc)); - mult1_a <= port1_latch; - mult1_b <= sine; - mult2_a <= port1_latch; - mult2_b <= cosine; - mult3_a <= port2_latch; - mult3_b <= sine; - mult4_a <= port2_latch; - mult4_b <= cosine; + -- sign extended multiplication + mult1_a <= port1_latch(15) & port1_latch(15) & port1_latch; + mult1_b <= sine(15) & sine(15) & sine; + mult2_a <= port1_latch(15) & port1_latch(15) & port1_latch; + mult2_b <= cosine(15) & cosine(15) & cosine; + mult3_a <= port2_latch(15) & port2_latch(15) & port2_latch; + mult3_b <= sine(15) & sine(15) & sine; + mult4_a <= port2_latch(15) & port2_latch(15) & port2_latch; + mult4_b <= cosine(15) & cosine(15) & cosine; if bin_cnt >= 3 then - -- multiplier result is available, accumulate + -- multiplier result is available, advance write address we <= "1"; write_address <= bin_cnt - 3; - ram_in <= std_logic_vector(signed(ram_out(191 downto 144))+signed(mult1_p)) - & std_logic_vector(signed(ram_out(143 downto 96))+signed(mult2_p)) - & std_logic_vector(signed(ram_out(95 downto 48))+signed(mult3_p)) - & std_logic_vector(signed(ram_out(47 downto 0))+signed(mult4_p)); else we <= "0"; + write_address <= 0; end if; if bin_cnt >= BINS+2 then read_address <= 0; - if sample_cnt < unsigned(NSAMPLES) then + if sample_cnt < samples_to_take then sample_cnt <= sample_cnt + 1; state <= WaitingForSample; - window_index <= std_logic_vector(unsigned(window_index)+unsigned(WINDOW_INC)); else state <= Ready; end if; else bin_cnt <= bin_cnt + 1; - if bin_cnt >= 1 then - read_address <= bin_cnt - 1; + if bin_cnt < BINS - 2 then + read_address <= bin_cnt + 2; end if; end if; when Ready => we <= "0"; RESULT_READY <= '1'; + write_address <= 0; if NEXT_OUTPUT = '1' then - -- reset last entry to prepare for next DFT - write_address <= read_address; - ram_in <= (others => '0'); - we <= "1"; -- fetch next entry from RAM if read_address < BINS - 1 then read_address <= read_address + 1; else RESULT_READY <= '0'; sample_cnt <= 0; + mult_enable <= '0'; state <= WaitingForSample; read_address <= 0; end if; diff --git a/FPGA/VNA/ResetDelay.vhd b/FPGA/VNA/ResetDelay.vhd index 6ef1028..bb8c1d0 100644 --- a/FPGA/VNA/ResetDelay.vhd +++ b/FPGA/VNA/ResetDelay.vhd @@ -40,16 +40,17 @@ architecture Behavioral of ResetDelay is signal clk_cnt : integer range 0 to CLK_DELAY-1; begin - OUT_RESET <= '1' when IN_RESET = '1' or clk_cnt < CLK_DELAY-1 else '0'; - process(CLK, IN_RESET) begin if rising_edge(CLK) then if IN_RESET = '1' then clk_cnt <= 0; + OUT_RESET <= '1'; else if clk_cnt < CLK_DELAY-1 then clk_cnt <= clk_cnt + 1; + else + OUT_RESET <= '0'; end if; end if; end if; diff --git a/FPGA/VNA/SPIConfig.vhd b/FPGA/VNA/SPIConfig.vhd index 183eeff..62307c6 100644 --- a/FPGA/VNA/SPIConfig.vhd +++ b/FPGA/VNA/SPIConfig.vhd @@ -71,10 +71,8 @@ entity SPICommands is SWEEP_RESUME : out STD_LOGIC; -- DFT signals - DFT_NSAMPLES : out STD_LOGIC_VECTOR (15 downto 0); DFT_BIN1_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); DFT_DIFFBIN_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); - DFT_WINDOW_INC : out STD_LOGIC_VECTOR (15 downto 0); DFT_RESULT_READY : in STD_LOGIC; DFT_OUTPUT : in STD_LOGIC_VECTOR (191 downto 0); DFT_NEXT_OUTPUT : out STD_LOGIC; @@ -103,7 +101,7 @@ architecture Behavioral of SPICommands is signal spi_buf_in : std_logic_vector(15 downto 0); signal spi_complete : std_logic; signal word_cnt : integer range 0 to 19; - type SPI_states is (Invalid, WriteSweepConfig, ReadResult, WriteRegister, ReadTest); + type SPI_states is (FirstWord, WriteSweepConfig, ReadResult, WriteRegister); signal state : SPI_states; signal selected_register : integer range 0 to 31; @@ -166,11 +164,10 @@ begin ADC_PHASEINC <= std_logic_vector(to_unsigned(1120, 12)); RESET_MINMAX <= '0'; INTERRUPT_ASSERTED <= '0'; + latched_result <= (others => '0'); - DFT_NSAMPLES <= (others => '0'); DFT_BIN1_PHASEINC <= (others => '0'); DFT_DIFFBIN_PHASEINC <= (others => '0'); - DFT_WINDOW_INC <= (others => '0'); dft_next <= '0'; last_NSS <= '1'; else @@ -198,19 +195,21 @@ begin if NSS = '0' and last_NSS = '1' then word_cnt <= 0; spi_buf_in <= interrupt_status; + state <= FirstWord; elsif spi_complete = '1' then word_cnt <= word_cnt + 1; - if word_cnt = 0 then + case state is + when FirstWord => -- initial word determines action case spi_buf_out(15 downto 13) is when "000" => state <= WriteSweepConfig; -- also extract the point number SWEEP_ADDRESS <= spi_buf_out(12 downto 0); - when "001" => state <= Invalid; + when "001" => state <= FirstWord; SWEEP_RESUME <= '1'; - when "010" => state <= ReadTest; + when "010" => state <= FirstWord; spi_buf_in <= "1111000010100101"; - when "011" => state <= Invalid; + when "011" => state <= FirstWord; RESET_MINMAX <= '1'; when "100" => state <= WriteRegister; selected_register <= to_integer(unsigned(spi_buf_out(4 downto 0))); @@ -225,60 +224,56 @@ begin when "111" => state <= ReadResult; -- can use same state as read result, but the latched data will contain the min/max ADC values latched_result(79 downto 0) <= ADC_MINMAX(95 downto 16); spi_buf_in <= ADC_MINMAX(15 downto 0); - when others => state <= Invalid; + when others => state <= FirstWord; end case; - else - if state = WriteRegister then - -- write received data into previously selected register - case selected_register is - when 0 => interrupt_mask <= spi_buf_out; - when 1 => SWEEP_POINTS <= spi_buf_out(12 downto 0); - when 2 => NSAMPLES <= spi_buf_out(12 downto 0); - when 3 => PORTSWITCH_EN <= spi_buf_out(0); - PORT1_EN <= spi_buf_out(15); - PORT2_EN <= spi_buf_out(14); - REF_EN <= spi_buf_out(13); - AMP_SHDN <= not spi_buf_out(12); - SOURCE_RF_EN <= spi_buf_out(11); - LO_RF_EN <= spi_buf_out(10); - LEDS <= not spi_buf_out(9 downto 7); - WINDOW_SETTING <= spi_buf_out(6 downto 5); - SOURCE_CE_EN <= spi_buf_out(4); - LO_CE_EN <= spi_buf_out(3); - EXCITE_PORT1 <= spi_buf_out(1); - EXCITE_PORT2 <= spi_buf_out(2); - when 4 => ADC_PRESCALER <= spi_buf_out(7 downto 0); - when 5 => ADC_PHASEINC <= spi_buf_out(11 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; - when 11 => MAX2871_DEF_1(31 downto 16) <= spi_buf_out; - when 12 => MAX2871_DEF_3(15 downto 0) <= spi_buf_out; - when 13 => MAX2871_DEF_3(31 downto 16) <= spi_buf_out; - when 14 => MAX2871_DEF_4(15 downto 0) <= spi_buf_out; - when 15 => MAX2871_DEF_4(31 downto 16) <= spi_buf_out; - when 16 => DFT_NSAMPLES <= spi_buf_out; - when 17 => DFT_WINDOW_INC <= spi_buf_out; - when 18 => DFT_BIN1_PHASEINC <= spi_buf_out; - when 19 => DFT_DIFFBIN_PHASEINC <= spi_buf_out; - when others => - end case; - selected_register <= selected_register + 1; - elsif state = WriteSweepConfig then - if word_cnt = 6 then - -- Sweep config data is complete pass on - SWEEP_DATA <= sweepconfig_buffer & spi_buf_out; - sweep_config_write <= '1'; - else - -- shift next word into buffer - sweepconfig_buffer <= sweepconfig_buffer(63 downto 0) & spi_buf_out; - end if; - elsif state = ReadResult then - -- pass on next word of latched result - spi_buf_in <= latched_result(15 downto 0); - latched_result <= "0000000000000000" & latched_result(287 downto 16); + when WriteRegister => + -- write received data into previously selected register + case selected_register is + when 0 => interrupt_mask <= spi_buf_out; + when 1 => SWEEP_POINTS <= spi_buf_out(12 downto 0); + when 2 => NSAMPLES <= spi_buf_out(12 downto 0); + when 3 => PORTSWITCH_EN <= spi_buf_out(0); + PORT1_EN <= spi_buf_out(15); + PORT2_EN <= spi_buf_out(14); + REF_EN <= spi_buf_out(13); + AMP_SHDN <= not spi_buf_out(12); + SOURCE_RF_EN <= spi_buf_out(11); + LO_RF_EN <= spi_buf_out(10); + LEDS <= not spi_buf_out(9 downto 7); + WINDOW_SETTING <= spi_buf_out(6 downto 5); + SOURCE_CE_EN <= spi_buf_out(4); + LO_CE_EN <= spi_buf_out(3); + EXCITE_PORT1 <= spi_buf_out(1); + EXCITE_PORT2 <= spi_buf_out(2); + when 4 => ADC_PRESCALER <= spi_buf_out(7 downto 0); + when 5 => ADC_PHASEINC <= spi_buf_out(11 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; + when 11 => MAX2871_DEF_1(31 downto 16) <= spi_buf_out; + when 12 => MAX2871_DEF_3(15 downto 0) <= spi_buf_out; + when 13 => MAX2871_DEF_3(31 downto 16) <= spi_buf_out; + when 14 => MAX2871_DEF_4(15 downto 0) <= spi_buf_out; + when 15 => MAX2871_DEF_4(31 downto 16) <= spi_buf_out; + when 18 => DFT_BIN1_PHASEINC <= spi_buf_out; + when 19 => DFT_DIFFBIN_PHASEINC <= spi_buf_out; + when others => + end case; + selected_register <= selected_register + 1; + when WriteSweepConfig => + if word_cnt = 6 then + -- Sweep config data is complete pass on + SWEEP_DATA <= sweepconfig_buffer & spi_buf_out; + sweep_config_write <= '1'; + else + -- shift next word into buffer + sweepconfig_buffer <= sweepconfig_buffer(63 downto 0) & spi_buf_out; end if; - end if; + when ReadResult => + -- pass on next word of latched result + spi_buf_in <= latched_result(15 downto 0); + latched_result <= "0000000000000000" & latched_result(287 downto 16); + end case; end if; end if; end if; diff --git a/FPGA/VNA/Sampling.vhd b/FPGA/VNA/Sampling.vhd index 7cc30ab..f1b2287 100644 --- a/FPGA/VNA/Sampling.vhd +++ b/FPGA/VNA/Sampling.vhd @@ -44,7 +44,6 @@ entity Sampling is PRE_DONE : out STD_LOGIC; START : in STD_LOGIC; SAMPLES : in STD_LOGIC_VECTOR (12 downto 0); - WINDOW_TYPE : in STD_LOGIC_VECTOR (1 downto 0); PORT1_I : out STD_LOGIC_VECTOR (47 downto 0); PORT1_Q : out STD_LOGIC_VECTOR (47 downto 0); PORT2_I : out STD_LOGIC_VECTOR (47 downto 0); @@ -94,16 +93,6 @@ END COMPONENT; signal sine : std_logic_vector(15 downto 0); signal cosine : std_logic_vector(15 downto 0); - signal windowed_sine : std_logic_vector(31 downto 0); - signal windowed_cosine : std_logic_vector(31 downto 0); - - signal window_index : std_logic_vector(6 downto 0); - signal window_value : std_logic_vector(15 downto 0); - signal window_sample_cnt : integer range 0 to 8191; - signal window_index_inc : integer range 0 to 8; - signal window_sample_compare : integer range 0 to 8191; - signal window_sample_cnt_inc : integer range 0 to 8; - signal mult1_I : std_logic_vector(31 downto 0); signal mult1_Q : std_logic_vector(31 downto 0); signal mult2_I : std_logic_vector(31 downto 0); @@ -132,66 +121,45 @@ begin PORT MAP ( clk => CLK, a => PORT1, - b => windowed_cosine(31 downto 16), + b => cosine, p => mult1_I ); Port1_Q_Mult : SinCosMult PORT MAP ( clk => CLK, a => PORT1, - b => windowed_sine(31 downto 16), + b => sine, p => mult1_Q ); Port2_I_Mult : SinCosMult PORT MAP ( clk => CLK, a => PORT2, - b => windowed_cosine(31 downto 16), + b => cosine, p => mult2_I ); Port2_Q_Mult : SinCosMult PORT MAP ( clk => CLK, a => PORT2, - b => windowed_sine(31 downto 16), + b => sine, p => mult2_Q ); Ref_I_Mult : SinCosMult PORT MAP ( clk => CLK, a => REF, - b => windowed_cosine(31 downto 16), + b => cosine, p => multR_I ); Ref_Q_Mult : SinCosMult PORT MAP ( clk => CLK, a => REF, - b => windowed_sine(31 downto 16), + b => sine, p => multR_Q ); - Sine_Mult : SinCosMult - PORT MAP ( - clk => CLK, - a => window_value, - b => sine, - p => windowed_sine - ); - Cosine_Mult : SinCosMult - PORT MAP ( - clk => CLK, - a => window_value, - b => cosine, - p => windowed_cosine - ); - WindowROM: window PORT MAP( - CLK => CLK, - INDEX => window_index, - WINDOW_TYPE => WINDOW_TYPE, - VALUE => window_value - ); - process(CLK, RESET) begin if rising_edge(CLK) then @@ -203,8 +171,6 @@ begin ACTIVE <= '0'; clk_cnt <= 0; sample_cnt <= 0; - window_sample_cnt <= 0; - window_index <= (others => '0'); phase <= (others => '0'); else -- when not idle, generate pulses for ADCs @@ -244,25 +210,6 @@ begin if START = '1' then state <= Sampling; samples_to_take <= to_integer(unsigned(SAMPLES & "0000") - 1); - window_sample_compare <= to_integer(unsigned(SAMPLES) - 1); - case SAMPLES is - when "0000000000001" => - -- 16 samples, increment on every sample by 8 - window_sample_cnt_inc <= 1; - window_index_inc <= 8; - when "0000000000010" | "0000000000011" => - -- 32-48 samples, increment by 4 - window_sample_cnt_inc <= 2; - window_index_inc <= 4; - when "0000000000100" | "0000000000101" | "0000000000110" | "0000000000111"=> - -- 64-112 samples, increment by 2 - window_sample_cnt_inc <= 4; - window_index_inc <= 2; - when others => - -- 128 or more samples, increment by 1 - window_sample_cnt_inc <= 8; - window_index_inc <= 1; - end case; end if; when Sampling => DONE <= '0'; @@ -294,13 +241,6 @@ begin else state <= Ready; end if; - -- keep track of window index - if window_sample_cnt < window_sample_compare then - window_sample_cnt <= window_sample_cnt + window_sample_cnt_inc; - else - window_sample_cnt <= window_sample_cnt - window_sample_compare; - window_index <= std_logic_vector( unsigned(window_index) + window_index_inc ); - end if; when Ready => ACTIVE <= '1'; DONE <= '1'; diff --git a/FPGA/VNA/Sweep.vhd b/FPGA/VNA/Sweep.vhd index 3e2fc3e..3c81251 100644 --- a/FPGA/VNA/Sweep.vhd +++ b/FPGA/VNA/Sweep.vhd @@ -82,45 +82,41 @@ architecture Behavioral of Sweep is signal state : Point_states; signal settling_cnt : unsigned(15 downto 0); signal settling_time : unsigned(15 downto 0); + signal config_reg : std_logic_vector(95 downto 0); begin CONFIG_ADDRESS <= std_logic_vector(point_cnt); -- assemble registers -- source register 0: N divider and fractional division value - SOURCE_REG_0 <= MAX2871_DEF_0(31) & "000000000" & CONFIG_DATA(6 downto 0) & CONFIG_DATA(27 downto 16) & "000"; + SOURCE_REG_0 <= MAX2871_DEF_0(31) & "000000000" & config_reg(6 downto 0) & config_reg(27 downto 16) & "000"; -- source register 1: Modulus value - SOURCE_REG_1 <= MAX2871_DEF_1(31 downto 15) & CONFIG_DATA(39 downto 28) & "001"; + SOURCE_REG_1 <= MAX2871_DEF_1(31 downto 15) & config_reg(39 downto 28) & "001"; -- source register 3: VCO selection - SOURCE_REG_3 <= CONFIG_DATA(12 downto 7) & MAX2871_DEF_3(25 downto 3) & "011"; + SOURCE_REG_3 <= config_reg(12 downto 7) & MAX2871_DEF_3(25 downto 3) & "011"; -- output power A passed on from default registers, output B disabled - SOURCE_REG_4 <= MAX2871_DEF_4(31 downto 23) & CONFIG_DATA(15 downto 13) & MAX2871_DEF_4(19 downto 9) & "000" & MAX2871_DEF_4(5 downto 3) & "100"; + SOURCE_REG_4 <= MAX2871_DEF_4(31 downto 23) & config_reg(15 downto 13) & MAX2871_DEF_4(19 downto 9) & "000" & MAX2871_DEF_4(5 downto 3) & "100"; -- LO register 0: N divider and fractional division value - LO_REG_0 <= MAX2871_DEF_0(31) & "000000000" & CONFIG_DATA(54 downto 48) & CONFIG_DATA(75 downto 64) & "000"; + LO_REG_0 <= MAX2871_DEF_0(31) & "000000000" & config_reg(54 downto 48) & config_reg(75 downto 64) & "000"; -- LO register 1: Modulus value - LO_REG_1 <= MAX2871_DEF_1(31 downto 15) & CONFIG_DATA(87 downto 76) & "001"; + LO_REG_1 <= MAX2871_DEF_1(31 downto 15) & config_reg(87 downto 76) & "001"; -- LO register 3: VCO selection - LO_REG_3 <= CONFIG_DATA(60 downto 55) & MAX2871_DEF_3(25 downto 3) & "011"; + LO_REG_3 <= config_reg(60 downto 55) & MAX2871_DEF_3(25 downto 3) & "011"; -- both outputs enabled at +5dbm - LO_REG_4 <= MAX2871_DEF_4(31 downto 23) & CONFIG_DATA(63 downto 61) & MAX2871_DEF_4(19 downto 9) & "111111100"; + LO_REG_4 <= MAX2871_DEF_4(31 downto 23) & config_reg(63 downto 61) & MAX2871_DEF_4(19 downto 9) & "111111100"; - ATTENUATOR <= CONFIG_DATA(46 downto 40); - SOURCE_FILTER <= CONFIG_DATA(89 downto 88); - BAND_SELECT <= CONFIG_DATA(47); - - settling_time <= to_unsigned(2048, 16) when CONFIG_DATA(94 downto 93) = "00" else -- 20us - to_unsigned(6144, 16) when CONFIG_DATA(94 downto 93) = "01" else -- 60us - to_unsigned(18432, 16) when CONFIG_DATA(94 downto 93) = "10" else -- 180us - to_unsigned(55296, 16); -- 540us + ATTENUATOR <= config_reg(46 downto 40); + SOURCE_FILTER <= config_reg(89 downto 88); + BAND_SELECT <= config_reg(47); - NSAMPLES <= USER_NSAMPLES when CONFIG_DATA(92 downto 90) = "000" else - std_logic_vector(to_unsigned(6, 13)) when CONFIG_DATA(92 downto 90) = "001" else - std_logic_vector(to_unsigned(19, 13)) when CONFIG_DATA(92 downto 90) = "010" else - std_logic_vector(to_unsigned(57, 13)) when CONFIG_DATA(92 downto 90) = "011" else - std_logic_vector(to_unsigned(190, 13)) when CONFIG_DATA(92 downto 90) = "100" else - std_logic_vector(to_unsigned(571, 13)) when CONFIG_DATA(92 downto 90) = "101" else - std_logic_vector(to_unsigned(1904, 13)) when CONFIG_DATA(92 downto 90) = "110" else + NSAMPLES <= USER_NSAMPLES when config_reg(92 downto 90) = "000" else + std_logic_vector(to_unsigned(6, 13)) when config_reg(92 downto 90) = "001" else + std_logic_vector(to_unsigned(19, 13)) when config_reg(92 downto 90) = "010" else + std_logic_vector(to_unsigned(57, 13)) when config_reg(92 downto 90) = "011" else + std_logic_vector(to_unsigned(190, 13)) when config_reg(92 downto 90) = "100" else + std_logic_vector(to_unsigned(571, 13)) when config_reg(92 downto 90) = "101" else + std_logic_vector(to_unsigned(1904, 13)) when config_reg(92 downto 90) = "110" else std_logic_vector(to_unsigned(5712, 13)); DEBUG_STATUS(10 downto 8) <= "000" when state = TriggerSetup else @@ -135,7 +131,9 @@ begin DEBUG_STATUS(6) <= PLL_RELOAD_DONE and PLL_LOCKED; DEBUG_STATUS(5) <= SAMPLING_BUSY; DEBUG_STATUS(4 downto 0) <= (others => '1'); - + + config_reg <= CONFIG_DATA; + process(CLK, RESET) begin if rising_edge(CLK) then @@ -154,21 +152,25 @@ begin state <= SettingUp; end if; when SettingUp => - -- highest bit in CONFIG_DATA determines whether the sweep should be halted prior to sampling - SWEEP_HALTED <= CONFIG_DATA(95); + -- highest bit in config_reg determines whether the sweep should be halted prior to sampling + SWEEP_HALTED <= config_reg(95); RELOAD_PLL_REGS <= '0'; + case config_reg(94 downto 93) is + when "00" => settling_time <= to_unsigned(2048, 16); -- 20us + when "01" => settling_time <= to_unsigned(6144, 16); -- 60us + when "10" => settling_time <= to_unsigned(18432, 16); -- 180us + when others => settling_time <= to_unsigned(55296, 16); -- 540us + end case; + settling_cnt <= settling_time; if PLL_RELOAD_DONE = '1' and PLL_LOCKED = '1' then -- check if halted sweep is resumed - if CONFIG_DATA(95) = '0' or SWEEP_RESUME = '1' then + if config_reg(95) = '0' or SWEEP_RESUME = '1' then SWEEP_HALTED <= '0'; if EXCITE_PORT1 = '1' then state <= SettlingPort1; - elsif EXCITE_PORT2 = '1' then - state <= SettlingPort2; else - state <= Done; + state <= SettlingPort2; end if; - settling_cnt <= settling_time; end if; end if; when SettlingPort1 => @@ -192,7 +194,7 @@ begin else state <= NextPoint; end if; - settling_cnt <= unsigned(SETTLING_TIME); + settling_cnt <= settling_time; end if; when SettlingPort2 => PORT_SELECT <= '0'; diff --git a/FPGA/VNA/Test_DFT.vhd b/FPGA/VNA/Test_DFT.vhd index e4d7f8f..2bd28ad 100644 --- a/FPGA/VNA/Test_DFT.vhd +++ b/FPGA/VNA/Test_DFT.vhd @@ -47,11 +47,9 @@ ARCHITECTURE behavior OF Test_DFT IS PORT1 : IN std_logic_vector(15 downto 0); PORT2 : IN std_logic_vector(15 downto 0); NEW_SAMPLE : IN std_logic; - NSAMPLES : in STD_LOGIC_VECTOR (15 downto 0); + NSAMPLES : in STD_LOGIC_VECTOR (12 downto 0); BIN1_PHASEINC : IN std_logic_vector(15 downto 0); DIFFBIN_PHASEINC : IN std_logic_vector(15 downto 0); - WINDOW_INC : IN std_logic_vector(15 downto 0); - WINDOW_TYPE : IN std_logic_vector(1 downto 0); RESULT_READY : OUT std_logic; OUTPUT : out STD_LOGIC_VECTOR (191 downto 0); NEXT_OUTPUT : IN std_logic @@ -67,10 +65,8 @@ ARCHITECTURE behavior OF Test_DFT IS signal NEW_SAMPLE : std_logic := '0'; signal BIN1_PHASEINC : std_logic_vector(15 downto 0) := (others => '0'); signal DIFFBIN_PHASEINC : std_logic_vector(15 downto 0) := (others => '0'); - signal WINDOW_INC : std_logic_vector(15 downto 0) := (others => '0'); - signal WINDOW_TYPE : std_logic_vector(1 downto 0) := (others => '0'); signal NEXT_OUTPUT : std_logic := '0'; - signal NSAMPLES : STD_LOGIC_VECTOR (15 downto 0); + signal NSAMPLES : STD_LOGIC_VECTOR (12 downto 0); --Outputs signal RESULT_READY : std_logic; @@ -93,8 +89,6 @@ BEGIN NSAMPLES => NSAMPLES, BIN1_PHASEINC => BIN1_PHASEINC, DIFFBIN_PHASEINC => DIFFBIN_PHASEINC, - WINDOW_INC => WINDOW_INC, - WINDOW_TYPE => WINDOW_TYPE, RESULT_READY => RESULT_READY, OUTPUT => OUTPUT, NEXT_OUTPUT => NEXT_OUTPUT @@ -119,7 +113,6 @@ BEGIN PORT2 <= "0100000000000000"; BIN1_PHASEINC <= "0100000000000000"; DIFFBIN_PHASEINC <= "0010000000000000"; - WINDOW_INC <= "0000100000000000"; NSAMPLES <= "0000000000000011"; wait for 100 ns; RESET <= '0'; @@ -128,7 +121,7 @@ BEGIN wait for CLK_period; NEW_SAMPLE <= '0'; while True loop - wait for CLK_period * 111; + wait for CLK_period * 79; NEW_SAMPLE <= '1'; wait for CLK_period; NEW_SAMPLE <= '0'; diff --git a/FPGA/VNA/Test_Windowing.vhd b/FPGA/VNA/Test_Windowing.vhd new file mode 100644 index 0000000..58517e4 --- /dev/null +++ b/FPGA/VNA/Test_Windowing.vhd @@ -0,0 +1,135 @@ +-------------------------------------------------------------------------------- +-- Company: +-- Engineer: +-- +-- Create Date: 18:37:51 11/06/2020 +-- Design Name: +-- Module Name: /home/jan/Projekte/VNA2/FPGA/VNA/Test_Windowing.vhd +-- Project Name: VNA +-- Target Device: +-- Tool versions: +-- Description: +-- +-- VHDL Test Bench Created by ISE for module: Windowing +-- +-- Dependencies: +-- +-- Revision: +-- Revision 0.01 - File Created +-- Additional Comments: +-- +-- Notes: +-- This testbench has been automatically generated using types std_logic and +-- std_logic_vector for the ports of the unit under test. Xilinx recommends +-- that these types always be used for the top-level I/O of a design in order +-- to guarantee that the testbench will bind correctly to the post-implementation +-- simulation model. +-------------------------------------------------------------------------------- +LIBRARY ieee; +USE ieee.std_logic_1164.ALL; + +-- Uncomment the following library declaration if using +-- arithmetic functions with Signed or Unsigned values +--USE ieee.numeric_std.ALL; + +ENTITY Test_Windowing IS +END Test_Windowing; + +ARCHITECTURE behavior OF Test_Windowing IS + + -- Component Declaration for the Unit Under Test (UUT) + + COMPONENT Windowing + PORT( + CLK : IN std_logic; + RESET : IN std_logic; + WINDOW_TYPE : IN std_logic_vector(1 downto 0); + PORT1_RAW : IN std_logic_vector(15 downto 0); + PORT2_RAW : IN std_logic_vector(15 downto 0); + REF_RAW : IN std_logic_vector(15 downto 0); + ADC_READY : IN std_logic; + PORT1_WINDOWED : OUT std_logic_vector(15 downto 0); + PORT2_WINDOWED : OUT std_logic_vector(15 downto 0); + REF_WINDOWED : OUT std_logic_vector(15 downto 0); + WINDOWING_DONE : OUT std_logic; + NSAMPLES : IN std_logic_vector(12 downto 0) + ); + END COMPONENT; + + + --Inputs + signal CLK : std_logic := '0'; + signal RESET : std_logic := '0'; + signal WINDOW_TYPE : std_logic_vector(1 downto 0) := (others => '0'); + signal PORT1_RAW : std_logic_vector(15 downto 0) := (others => '0'); + signal PORT2_RAW : std_logic_vector(15 downto 0) := (others => '0'); + signal REF_RAW : std_logic_vector(15 downto 0) := (others => '0'); + signal ADC_READY : std_logic := '0'; + signal NSAMPLES : std_logic_vector(12 downto 0) := (others => '0'); + + --Outputs + signal PORT1_WINDOWED : std_logic_vector(15 downto 0); + signal PORT2_WINDOWED : std_logic_vector(15 downto 0); + signal REF_WINDOWED : std_logic_vector(15 downto 0); + signal WINDOWING_DONE : std_logic; + + -- Clock period definitions + constant CLK_period : time := 10 ns; + +BEGIN + + -- Instantiate the Unit Under Test (UUT) + uut: Windowing PORT MAP ( + CLK => CLK, + RESET => RESET, + WINDOW_TYPE => WINDOW_TYPE, + PORT1_RAW => PORT1_RAW, + PORT2_RAW => PORT2_RAW, + REF_RAW => REF_RAW, + ADC_READY => ADC_READY, + PORT1_WINDOWED => PORT1_WINDOWED, + PORT2_WINDOWED => PORT2_WINDOWED, + REF_WINDOWED => REF_WINDOWED, + WINDOWING_DONE => WINDOWING_DONE, + NSAMPLES => NSAMPLES + ); + + -- Clock process definitions + CLK_process :process + begin + CLK <= '0'; + wait for CLK_period/2; + CLK <= '1'; + wait for CLK_period/2; + end process; + + + -- Stimulus process + stim_proc: process + begin + -- hold reset state for 100 ns. + wait for 100 ns; + + wait for CLK_period*10; + + -- insert stimulus here + WINDOW_TYPE <= "10"; + NSAMPLES <= "0000000010001"; + PORT1_RAW <= "0000000010000000"; + PORT2_RAW <= "0000000100000000"; + REF_RAW <= "0000001000000000"; + ADC_READY <= '0'; + RESET <= '1'; + wait for CLK_period; + RESET <= '0'; + for i in 0 to 271 loop + wait for CLK_period*111; + ADC_READY <= '1'; + wait for CLK_period; + ADC_READY <= '0'; + end loop; + + wait; + end process; + +END; diff --git a/FPGA/VNA/VNA.gise b/FPGA/VNA/VNA.gise index 2642c8c..955d340 100644 --- a/FPGA/VNA/VNA.gise +++ b/FPGA/VNA/VNA.gise @@ -39,9 +39,7 @@ - - @@ -51,7 +49,12 @@ + + + + + @@ -68,7 +71,6 @@ - @@ -129,7 +131,7 @@ - + @@ -154,22 +156,25 @@ - + + - + - + - + + + @@ -180,7 +185,7 @@ - + @@ -206,7 +211,9 @@ - + + + @@ -216,7 +223,7 @@ - + @@ -224,22 +231,20 @@ - - + + - - + - + - @@ -249,9 +254,11 @@ - + + + @@ -278,8 +285,9 @@ - + + @@ -295,11 +303,11 @@ - + - + @@ -308,7 +316,7 @@ - + @@ -322,7 +330,7 @@ - + @@ -336,7 +344,7 @@ - + @@ -390,7 +398,7 @@ - + diff --git a/FPGA/VNA/VNA.xise b/FPGA/VNA/VNA.xise index ec70abb..1327387 100644 --- a/FPGA/VNA/VNA.xise +++ b/FPGA/VNA/VNA.xise @@ -17,14 +17,14 @@ - + - + @@ -34,22 +34,22 @@ - + - + - + - + - + @@ -64,15 +64,15 @@ - + - + - + @@ -100,7 +100,7 @@ - + @@ -114,7 +114,7 @@ - + @@ -127,23 +127,33 @@ - - + + - + - - - - - + + + + + + + + + + + + + + + @@ -159,6 +169,9 @@ + + + @@ -226,8 +239,8 @@ - - + + @@ -345,7 +358,7 @@ - + @@ -403,8 +416,8 @@ - - + + @@ -422,7 +435,7 @@ - + @@ -454,7 +467,7 @@ - + @@ -474,7 +487,7 @@ - + diff --git a/FPGA/VNA/Windowing.vhd b/FPGA/VNA/Windowing.vhd new file mode 100644 index 0000000..8271d3f --- /dev/null +++ b/FPGA/VNA/Windowing.vhd @@ -0,0 +1,201 @@ +---------------------------------------------------------------------------------- +-- Company: +-- Engineer: +-- +-- Create Date: 18:18:17 11/06/2020 +-- Design Name: +-- Module Name: Windowing - Behavioral +-- Project Name: +-- Target Devices: +-- Tool versions: +-- Description: +-- +-- Dependencies: +-- +-- Revision: +-- Revision 0.01 - File Created +-- Additional Comments: +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +-- Uncomment the following library declaration if using +-- arithmetic functions with Signed or Unsigned values +use IEEE.NUMERIC_STD.ALL; + +-- Uncomment the following library declaration if instantiating +-- any Xilinx primitives in this code. +--library UNISIM; +--use UNISIM.VComponents.all; + +entity Windowing is + Port ( CLK : in STD_LOGIC; + RESET : in STD_LOGIC; + WINDOW_TYPE : in STD_LOGIC_VECTOR (1 downto 0); + PORT1_RAW : in STD_LOGIC_VECTOR (15 downto 0); + PORT2_RAW : in STD_LOGIC_VECTOR (15 downto 0); + REF_RAW : in STD_LOGIC_VECTOR (15 downto 0); + ADC_READY : in STD_LOGIC; + PORT1_WINDOWED : out STD_LOGIC_VECTOR (15 downto 0); + PORT2_WINDOWED : out STD_LOGIC_VECTOR (15 downto 0); + REF_WINDOWED : out STD_LOGIC_VECTOR (15 downto 0); + WINDOWING_DONE : out STD_LOGIC; + NSAMPLES : in STD_LOGIC_VECTOR (12 downto 0)); +end Windowing; + +architecture Behavioral of Windowing is +COMPONENT window +PORT( + CLK : IN std_logic; + INDEX : IN std_logic_vector(6 downto 0); + WINDOW_TYPE : IN std_logic_vector(1 downto 0); + VALUE : OUT std_logic_vector(15 downto 0) + ); +END COMPONENT; +COMPONENT DSP_SLICE + PORT ( + clk : IN STD_LOGIC; + ce : IN STD_LOGIC; + sel : IN STD_LOGIC_VECTOR(0 DOWNTO 0); + a : IN STD_LOGIC_VECTOR(17 DOWNTO 0); + b : IN STD_LOGIC_VECTOR(17 DOWNTO 0); + c : IN STD_LOGIC_VECTOR(47 DOWNTO 0); + p : OUT STD_LOGIC_VECTOR(47 DOWNTO 0) + ); +END COMPONENT; + signal window_index : std_logic_vector(6 downto 0); + signal window_value : std_logic_vector(15 downto 0); + signal window_sample_cnt : integer range -8 to 8191; + signal window_index_inc : integer range 0 to 8; + signal window_sample_compare : integer range 0 to 8191; + signal window_sample_cnt_inc : integer range 0 to 8; + + signal mult_a : std_logic_vector(17 downto 0); + signal mult_b : std_logic_vector(17 downto 0); + signal mult_p : std_logic_vector(47 downto 0); + signal mult_enable : std_logic; + + type states is (CalcWindowInc, WaitingForADC, CalcPort1, CalcPort2, CalcRef, MultDelay1, MultDelay2, StorePort1, StorePort2, StoreRef); + signal state : states; +begin + + Mult : DSP_SLICE + PORT MAP ( + clk => CLK, + ce => mult_enable, + sel => "0", + a => mult_a, + b => mult_b, + c => (others => '0'), + p => mult_p + ); + WindowROM: window PORT MAP( + CLK => CLK, + INDEX => window_index, + WINDOW_TYPE => WINDOW_TYPE, + VALUE => window_value + ); + -- sign extend multiplier inputs + mult_a(17 downto 16) <= mult_a(15) & mult_a(15); + mult_b(17 downto 16) <= mult_b(15) & mult_b(15); + + mult_a(15 downto 0) <= window_value; + + process(CLK) + begin + if rising_edge(CLK) then + if RESET = '1' then + WINDOWING_DONE <= '0'; + state <= CalcWindowInc; + mult_enable <= '0'; + mult_b(15 downto 0) <= (others => '0'); + window_index <= (others => '0'); + window_sample_cnt <= 0; + window_sample_compare <= to_integer(unsigned(NSAMPLES)); + else + case state is + when CalcWindowInc => + case window_sample_compare is + when 1 => + -- 16 samples, increment on every sample by 8 + window_sample_cnt_inc <= 1; + window_index_inc <= 8; + when 2 to 3 => + -- 32-48 samples, increment by 4 + window_sample_cnt_inc <= 2; + window_index_inc <= 4; + when 4 to 7 => + -- 64-112 samples, increment by 2 + window_sample_cnt_inc <= 4; + window_index_inc <= 2; + when others => + -- 128 or more samples, increment by 1 + window_sample_cnt_inc <= 8; + window_index_inc <= 1; + end case; + state <= WaitingForADC; + when WaitingForADC => + WINDOWING_DONE <= '0'; + mult_enable <= '0'; + mult_b(15 downto 0) <= (others => '0'); + if ADC_READY = '1' then + state <= CalcPort1; + end if; + when CalcPort1 => + WINDOWING_DONE <= '0'; + mult_enable <= '1'; + mult_b(15 downto 0) <= PORT1_RAW; + state <= CalcPort2; + when CalcPort2 => + WINDOWING_DONE <= '0'; + mult_enable <= '1'; + mult_b(15 downto 0) <= PORT2_RAW; + state <= CalcRef; + when CalcRef => + WINDOWING_DONE <= '0'; + mult_enable <= '1'; + mult_b(15 downto 0) <= REF_RAW; + state <= MultDelay1; + when MultDelay1 => + WINDOWING_DONE <= '0'; + mult_enable <= '1'; + mult_b(15 downto 0) <= (others => '0'); + state <= MultDelay2; + when MultDelay2 => + WINDOWING_DONE <= '0'; + mult_enable <= '1'; + mult_b(15 downto 0) <= (others => '0'); + state <= StorePort1; + when StorePort1 => + WINDOWING_DONE <= '0'; + mult_enable <= '1'; + mult_b(15 downto 0) <= (others => '0'); + PORT1_WINDOWED <= mult_p(30 downto 15); + state <= StorePort2; + when StorePort2 => + WINDOWING_DONE <= '0'; + mult_enable <= '1'; + mult_b(15 downto 0) <= (others => '0'); + PORT2_WINDOWED <= mult_p(30 downto 15); + state <= StoreRef; + when StoreRef => + WINDOWING_DONE <= '1'; + mult_enable <= '0'; + mult_b(15 downto 0) <= (others => '0'); + REF_WINDOWED <= mult_p(30 downto 15); + -- update window increment + if window_sample_cnt + window_sample_cnt_inc < window_sample_compare then + window_sample_cnt <= window_sample_cnt + window_sample_cnt_inc; + else + window_sample_cnt <= window_sample_cnt + window_sample_cnt_inc - window_sample_compare; + window_index <= std_logic_vector( unsigned(window_index) + window_index_inc ); + end if; + state <= WaitingForADC; + end case; + end if; + end if; + end process; + +end Behavioral; + diff --git a/FPGA/VNA/dft_result.vhd b/FPGA/VNA/dft_result.vhd deleted file mode 100644 index e4388f7..0000000 --- a/FPGA/VNA/dft_result.vhd +++ /dev/null @@ -1,56 +0,0 @@ ----------------------------------------------------------------------------------- --- Company: --- Engineer: --- --- Create Date: 22:01:17 11/03/2020 --- Design Name: --- Module Name: dft_result - Behavioral --- Project Name: --- Target Devices: --- Tool versions: --- Description: --- --- Dependencies: --- --- Revision: --- Revision 0.01 - File Created --- Additional Comments: --- ----------------------------------------------------------------------------------- -library IEEE; -use IEEE.STD_LOGIC_1164.ALL; - --- Uncomment the following library declaration if using --- arithmetic functions with Signed or Unsigned values -use IEEE.NUMERIC_STD.ALL; - --- Uncomment the following library declaration if instantiating --- any Xilinx primitives in this code. ---library UNISIM; ---use UNISIM.VComponents.all; - -entity dft_result is - Generic (depth : integer); - Port ( CLK : in STD_LOGIC; - READ_ADDRESS : in integer range 0 to depth-1; - WRITE_ADDRESS : in integer range 0 to depth-1; - DATA_IN : in STD_LOGIC_VECTOR (191 downto 0); - DATA_OUT : out STD_LOGIC_VECTOR (191 downto 0); - WE : in STD_LOGIC); -end dft_result; - -architecture rtl of dft_result is - type memory is array(depth-1 downto 0) of std_logic_vector(191 downto 0); - signal ram : memory; -begin - process(CLK) - begin - if rising_edge(CLK) then - DATA_OUT <= ram(READ_ADDRESS); - if(WE = '1') then - ram(WRITE_ADDRESS) <= DATA_IN; - end if; - end if; - end process; -end rtl; - diff --git a/FPGA/VNA/ipcore_dir/DSP48.xco b/FPGA/VNA/ipcore_dir/DSP48.xco new file mode 100644 index 0000000..face7fc --- /dev/null +++ b/FPGA/VNA/ipcore_dir/DSP48.xco @@ -0,0 +1,136 @@ +############################################################## +# +# Xilinx Core Generator version 14.6 +# Date: Fri Nov 6 13:56:17 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:xbip_dsp48_macro:2.1 +# +############################################################## +# +# 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 DSP48_Macro xilinx.com:ip:xbip_dsp48_macro:2.1 +# END Select +# BEGIN Parameters +CSET a_binarywidth=0 +CSET a_width=18 +CSET areg_1=false +CSET areg_2=false +CSET areg_3=true +CSET areg_4=true +CSET b_binarywidth=0 +CSET b_width=18 +CSET breg_1=false +CSET breg_2=false +CSET breg_3=true +CSET breg_4=true +CSET c_binarywidth=0 +CSET c_width=48 +CSET cinreg_1=false +CSET cinreg_2=false +CSET cinreg_3=false +CSET cinreg_4=false +CSET cinreg_5=false +CSET component_name=DSP48 +CSET concat_binarywidth=0 +CSET concat_width=48 +CSET concatreg_3=false +CSET concatreg_4=false +CSET concatreg_5=false +CSET creg_1=false +CSET creg_2=false +CSET creg_3=true +CSET creg_4=true +CSET creg_5=true +CSET d_binarywidth=0 +CSET d_width=18 +CSET dreg_1=false +CSET dreg_2=false +CSET dreg_3=false +CSET gui_behaviour=Coregen +CSET has_a_ce=false +CSET has_a_sclr=false +CSET has_acout=false +CSET has_b_ce=false +CSET has_b_sclr=false +CSET has_bcout=false +CSET has_c_ce=false +CSET has_c_sclr=false +CSET has_carrycascout=false +CSET has_carryout=false +CSET has_ce=true +CSET has_concat_ce=false +CSET has_concat_sclr=false +CSET has_d_ce=false +CSET has_d_sclr=false +CSET has_m_ce=false +CSET has_m_sclr=false +CSET has_p_ce=false +CSET has_p_sclr=false +CSET has_pcout=false +CSET has_sclr=false +CSET has_sel_ce=false +CSET has_sel_sclr=false +CSET instruction1=A*B +CSET instruction2=A*B+C +CSET instruction3=# +CSET instruction4=# +CSET instruction5=# +CSET instruction6=# +CSET instruction7=# +CSET instruction8=# +CSET instruction_list=# +CSET mreg_5=true +CSET opreg_1=false +CSET opreg_2=false +CSET opreg_3=true +CSET opreg_4=true +CSET opreg_5=true +CSET output_properties=Full_Precision +CSET p_binarywidth=0 +CSET p_full_width=48 +CSET p_width=48 +CSET pcin_binarywidth=0 +CSET pipeline_options=Automatic +CSET preg_6=true +CSET show_filtered=false +CSET tier_1=false +CSET tier_2=false +CSET tier_3=false +CSET tier_4=false +CSET tier_5=false +CSET tier_6=false +CSET use_dsp48=true +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-11-05T14:23:53Z +# END Extra information +GENERATE +# CRC: 2ca4f824 diff --git a/FPGA/VNA/ipcore_dir/DSP48.xise b/FPGA/VNA/ipcore_dir/DSP48.xise new file mode 100644 index 0000000..4da40c6 --- /dev/null +++ b/FPGA/VNA/ipcore_dir/DSP48.xise @@ -0,0 +1,73 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/FPGA/VNA/ipcore_dir/DSP_SLICE.xco b/FPGA/VNA/ipcore_dir/DSP_SLICE.xco new file mode 100644 index 0000000..b39e78b --- /dev/null +++ b/FPGA/VNA/ipcore_dir/DSP_SLICE.xco @@ -0,0 +1,136 @@ +############################################################## +# +# Xilinx Core Generator version 14.6 +# Date: Fri Nov 6 13:57:46 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:xbip_dsp48_macro:2.1 +# +############################################################## +# +# 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 DSP48_Macro xilinx.com:ip:xbip_dsp48_macro:2.1 +# END Select +# BEGIN Parameters +CSET a_binarywidth=0 +CSET a_width=18 +CSET areg_1=false +CSET areg_2=false +CSET areg_3=true +CSET areg_4=true +CSET b_binarywidth=0 +CSET b_width=18 +CSET breg_1=false +CSET breg_2=false +CSET breg_3=true +CSET breg_4=true +CSET c_binarywidth=0 +CSET c_width=48 +CSET cinreg_1=false +CSET cinreg_2=false +CSET cinreg_3=false +CSET cinreg_4=false +CSET cinreg_5=false +CSET component_name=DSP_SLICE +CSET concat_binarywidth=0 +CSET concat_width=48 +CSET concatreg_3=false +CSET concatreg_4=false +CSET concatreg_5=false +CSET creg_1=false +CSET creg_2=false +CSET creg_3=true +CSET creg_4=true +CSET creg_5=true +CSET d_binarywidth=0 +CSET d_width=18 +CSET dreg_1=false +CSET dreg_2=false +CSET dreg_3=false +CSET gui_behaviour=Coregen +CSET has_a_ce=false +CSET has_a_sclr=false +CSET has_acout=false +CSET has_b_ce=false +CSET has_b_sclr=false +CSET has_bcout=false +CSET has_c_ce=false +CSET has_c_sclr=false +CSET has_carrycascout=false +CSET has_carryout=false +CSET has_ce=true +CSET has_concat_ce=false +CSET has_concat_sclr=false +CSET has_d_ce=false +CSET has_d_sclr=false +CSET has_m_ce=false +CSET has_m_sclr=false +CSET has_p_ce=false +CSET has_p_sclr=false +CSET has_pcout=false +CSET has_sclr=false +CSET has_sel_ce=false +CSET has_sel_sclr=false +CSET instruction1=A*B +CSET instruction2=A*B+C +CSET instruction3=# +CSET instruction4=# +CSET instruction5=# +CSET instruction6=# +CSET instruction7=# +CSET instruction8=# +CSET instruction_list=# +CSET mreg_5=true +CSET opreg_1=false +CSET opreg_2=false +CSET opreg_3=true +CSET opreg_4=true +CSET opreg_5=true +CSET output_properties=Full_Precision +CSET p_binarywidth=0 +CSET p_full_width=48 +CSET p_width=48 +CSET pcin_binarywidth=0 +CSET pipeline_options=Automatic +CSET preg_6=true +CSET show_filtered=false +CSET tier_1=false +CSET tier_2=false +CSET tier_3=false +CSET tier_4=false +CSET tier_5=false +CSET tier_6=false +CSET use_dsp48=true +# END Parameters +# BEGIN Extra information +MISC pkg_timestamp=2012-11-05T14:23:53Z +# END Extra information +GENERATE +# CRC: 57648294 diff --git a/FPGA/VNA/ipcore_dir/DSP_SLICE.xise b/FPGA/VNA/ipcore_dir/DSP_SLICE.xise new file mode 100644 index 0000000..b4222ea --- /dev/null +++ b/FPGA/VNA/ipcore_dir/DSP_SLICE.xise @@ -0,0 +1,73 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/FPGA/VNA/top.bin b/FPGA/VNA/top.bin index a47191b..6ad41ec 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 d382670..41b0f72 100644 --- a/FPGA/VNA/top.vhd +++ b/FPGA/VNA/top.vhd @@ -145,6 +145,24 @@ architecture Behavioral of top is DEBUG_STATUS : out STD_LOGIC_VECTOR (10 downto 0) ); END COMPONENT; + + COMPONENT Windowing + PORT( + CLK : IN std_logic; + RESET : IN std_logic; + WINDOW_TYPE : IN std_logic_vector(1 downto 0); + PORT1_RAW : IN std_logic_vector(15 downto 0); + PORT2_RAW : IN std_logic_vector(15 downto 0); + REF_RAW : IN std_logic_vector(15 downto 0); + ADC_READY : IN std_logic; + NSAMPLES : IN std_logic_vector(12 downto 0); + PORT1_WINDOWED : OUT std_logic_vector(15 downto 0); + PORT2_WINDOWED : OUT std_logic_vector(15 downto 0); + REF_WINDOWED : OUT std_logic_vector(15 downto 0); + WINDOWING_DONE : OUT std_logic + ); + END COMPONENT; + COMPONENT Sampling Generic(CLK_CYCLES_PRE_DONE : integer); PORT( @@ -158,7 +176,6 @@ architecture Behavioral of top is NEW_SAMPLE : IN std_logic; START : IN std_logic; SAMPLES : IN std_logic_vector(12 downto 0); - WINDOW_TYPE : in STD_LOGIC_VECTOR (1 downto 0); ADC_START : OUT std_logic; DONE : OUT std_logic; PRE_DONE : OUT std_logic; @@ -245,10 +262,8 @@ architecture Behavioral of top is RESET_MINMAX : out STD_LOGIC; SWEEP_HALTED : in STD_LOGIC; SWEEP_RESUME : out STD_LOGIC; - DFT_NSAMPLES : out STD_LOGIC_VECTOR (15 downto 0); DFT_BIN1_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); DFT_DIFFBIN_PHASEINC : out STD_LOGIC_VECTOR (15 downto 0); - DFT_WINDOW_INC : out STD_LOGIC_VECTOR (15 downto 0); DFT_RESULT_READY : in STD_LOGIC; DFT_OUTPUT : in STD_LOGIC_VECTOR (191 downto 0); DFT_NEXT_OUTPUT : out STD_LOGIC; @@ -265,11 +280,9 @@ architecture Behavioral of top is PORT1 : IN std_logic_vector(15 downto 0); PORT2 : IN std_logic_vector(15 downto 0); NEW_SAMPLE : IN std_logic; - NSAMPLES : IN std_logic_vector(15 downto 0); + NSAMPLES : IN std_logic_vector(12 downto 0); BIN1_PHASEINC : IN std_logic_vector(15 downto 0); DIFFBIN_PHASEINC : IN std_logic_vector(15 downto 0); - WINDOW_INC : IN std_logic_vector(15 downto 0); - WINDOW_TYPE : IN std_logic_vector(1 downto 0); NEXT_OUTPUT : IN std_logic; RESULT_READY : OUT std_logic; OUTPUT : OUT std_logic_vector(191 downto 0) @@ -329,6 +342,11 @@ architecture Behavioral of top is signal adc_minmax : std_logic_vector(95 downto 0); signal adc_reset_minmax : std_logic; + signal port1_windowed : std_logic_vector(15 downto 0); + signal port2_windowed : std_logic_vector(15 downto 0); + signal ref_windowed : std_logic_vector(15 downto 0); + signal windowing_ready : std_logic; + -- Sampling signals signal sampling_busy : std_logic; signal sampling_done : std_logic; @@ -394,10 +412,8 @@ architecture Behavioral of top is signal intr : std_logic; -- DFT signals - signal dft_nsamples : std_logic_vector (15 downto 0); signal dft_bin1_phaseinc : std_logic_vector (15 downto 0); signal dft_diffbin_phaseinc : std_logic_vector (15 downto 0); - signal dft_window_inc : std_logic_vector (15 downto 0); signal dft_ready : std_logic; signal dft_output : std_logic_vector (191 downto 0); signal dft_next_output : std_logic; @@ -579,6 +595,22 @@ begin SCLK => REF_SCLK ); + + Windower: Windowing PORT MAP( + CLK => clk160, + RESET => sampling_start, + WINDOW_TYPE => sampling_window, + PORT1_RAW => adc_port1_data, + PORT2_RAW => adc_port2_data, + REF_RAW => adc_ref_data, + ADC_READY => adc_port1_ready, + PORT1_WINDOWED => port1_windowed, + PORT2_WINDOWED => port2_windowed, + REF_WINDOWED => ref_windowed, + WINDOWING_DONE => windowing_ready, + NSAMPLES => sampling_samples + ); + Sampler: Sampling GENERIC MAP(CLK_CYCLES_PRE_DONE => 0) PORT MAP( @@ -586,16 +618,15 @@ begin RESET => sweep_reset, ADC_PRESCALER => sampling_prescaler, PHASEINC => sampling_phaseinc, - PORT1 => adc_port1_data, - PORT2 => adc_port2_data, - REF => adc_ref_data, + PORT1 => port1_windowed, + PORT2 => port2_windowed, + REF => ref_windowed, ADC_START => adc_trigger_sample, - NEW_SAMPLE => adc_port1_ready, + NEW_SAMPLE => windowing_ready, DONE => sampling_done, PRE_DONE => open, START => sampling_start, SAMPLES => sampling_samples, - WINDOW_TYPE => sampling_window, PORT1_I => sampling_result(287 downto 240), PORT1_Q => sampling_result(239 downto 192), PORT2_I => sampling_result(191 downto 144), @@ -614,7 +645,7 @@ begin CONFIG_ADDRESS => sweep_config_address, CONFIG_DATA => sweep_config_data, USER_NSAMPLES => sampling_user_samples, - NSAMPLES => sampling_user_samples, + NSAMPLES => sampling_samples, SAMPLING_BUSY => sampling_busy, SAMPLING_DONE => sampling_done, START_SAMPLING => sampling_start, @@ -691,7 +722,7 @@ begin SWEEP_ADDRESS => sweep_config_write_address, SWEEP_WRITE => sweep_config_write, SWEEP_POINTS => sweep_points, - NSAMPLES => sampling_samples, + NSAMPLES => sampling_user_samples, PORT1_EN => port1mix_en, PORT2_EN => port2mix_en, REF_EN => refmix_en, @@ -711,10 +742,8 @@ begin SWEEP_RESUME => sweep_resume, EXCITE_PORT1 => sweep_excite_port1, EXCITE_PORT2 => sweep_excite_port2, - DFT_NSAMPLES => dft_nsamples, DFT_BIN1_PHASEINC => dft_bin1_phaseinc, DFT_DIFFBIN_PHASEINC => dft_diffbin_phaseinc, - DFT_WINDOW_INC => dft_window_inc, DFT_RESULT_READY => dft_ready, DFT_OUTPUT => dft_output, DFT_NEXT_OUTPUT => dft_next_output, @@ -728,14 +757,12 @@ begin PORT MAP( CLK => clk160, RESET => dft_reset, - PORT1 => adc_port1_data, - PORT2 => adc_port2_data, - NEW_SAMPLE => adc_port1_ready, - NSAMPLES => dft_nsamples, + PORT1 => port1_windowed, + PORT2 => port2_windowed, + NEW_SAMPLE => windowing_ready, + NSAMPLES => sampling_samples, BIN1_PHASEINC => dft_bin1_phaseinc, DIFFBIN_PHASEINC => dft_diffbin_phaseinc, - WINDOW_INC => dft_window_inc, - WINDOW_TYPE => sampling_window, RESULT_READY => dft_ready, OUTPUT => dft_output, NEXT_OUTPUT => dft_next_output diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp index b686adb..7997810 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp @@ -12,6 +12,7 @@ static FPGA::HaltedCallback halted_cb; static uint16_t SysCtrlReg = 0x0000; static uint16_t ISRMaskReg = 0x0000; +static uint32_t ADC_samplerate; using namespace FPGAHAL; @@ -27,6 +28,9 @@ void FPGA::WriteRegister(FPGA::Reg reg, uint16_t value) { Low(CS); HAL_SPI_Transmit(&FPGA_SPI, (uint8_t*) cmd, 4, 100); High(CS); + if(reg == Reg::ADCPrescaler) { + ADC_samplerate = Clockrate / value; + } } bool FPGA::Configure(Flash *f, uint32_t start_address, uint32_t bitstream_size) { @@ -371,6 +375,23 @@ void FPGA::ResumeHaltedSweep() { High(CS); } +void FPGA::SetupDFT(uint32_t f_firstBin, uint32_t f_binSpacing) { + // see FPGA protocol for formulas + uint16_t firstBin = f_firstBin * (1ULL << 16) / ADC_samplerate; + uint16_t binSpacing = f_binSpacing * (1ULL << 24) / ADC_samplerate; + WriteRegister(Reg::DFTFirstBin, firstBin); + WriteRegister(Reg::DFTFreqSpacing, binSpacing); +} + +void FPGA::StopDFT() { + DisableInterrupt(Interrupt::DFTReady); +} + +void FPGA::StartDFT() { + StopDFT(); + EnableInterrupt(Interrupt::DFTReady); +} + FPGA::DFTResult FPGA::ReadDFTResult() { uint8_t cmd[2] = {0xA0, 0x00}; uint8_t recv[24]; diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp index 07cafce..cae5e3e 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp @@ -7,6 +7,7 @@ namespace FPGA { static constexpr uint16_t MaxPoints = 4501; static constexpr uint16_t DFTbins = 64; +static constexpr uint32_t Clockrate = 102400000UL; enum class Reg { InterruptMask = 0x00, @@ -23,8 +24,6 @@ enum class Reg { MAX2871Def3MSB = 0x0D, MAX2871Def4LSB = 0x0E, MAX2871Def4MSB = 0x0F, - DFTSamples = 0x10, - DFTWindowInc = 0x11, DFTFirstBin = 0x12, DFTFreqSpacing = 0x13, }; @@ -124,6 +123,9 @@ void WriteSweepConfig(uint16_t pointnum, bool lowband, uint32_t *SourceRegs, uin uint8_t attenuation, uint64_t frequency, SettlingTime settling, Samples samples, bool halt = false, LowpassFilter filter = LowpassFilter::Auto); using ReadCallback = void(*)(const SamplingResult &result); bool InitiateSampleRead(ReadCallback cb); +void SetupDFT(uint32_t f_firstBin, uint32_t f_binSpacing); +void StopDFT(); +void StartDFT(); DFTResult ReadDFTResult(); ADCLimits GetADCLimits(); void ResetADCLimits(); diff --git a/Software/VNA_embedded/Application/Drivers/Si5351C.cpp b/Software/VNA_embedded/Application/Drivers/Si5351C.cpp index 34576cd..3997afb 100644 --- a/Software/VNA_embedded/Application/Drivers/Si5351C.cpp +++ b/Software/VNA_embedded/Application/Drivers/Si5351C.cpp @@ -315,29 +315,31 @@ void Si5351C::FindOptimalDivider(uint32_t f_pll, uint32_t f, uint32_t &P1, // see https://www.silabs.com/documents/public/application-notes/AN619.pdf (page 3/6) uint32_t a = f_pll / f; int32_t f_rem = f_pll - f * a; - uint32_t best_b, best_c; - uint32_t best_deviation = UINT32_MAX; - for (uint32_t c = (1UL << 20) - 1; c >= (1UL << 19); c--) { - uint32_t guess_b = (uint64_t) f_rem * c / f; - for (uint32_t b = guess_b; b <= guess_b + 1; b++) { - int32_t f_div = (uint64_t) f * b / c; - uint32_t deviation = abs(f_rem - f_div); - if (deviation < best_deviation) { - best_b = b; - best_c = c; - best_deviation = deviation; - if (deviation == 0) { - break; - } - } - } - if (best_deviation == 0) { - break; - } - } - LOG_DEBUG( - "Optimal divider for %luHz/%luHz is: a=%lu, b=%lu, c=%lu (%luHz deviation)", - f_pll, f, a, best_b, best_c, best_deviation); + // always using the highest modulus divider results in less than 1Hz deviation for all frequencies, that is good enough + uint32_t best_c = (1UL << 20) - 1; + uint32_t best_b = (uint64_t) f_rem * best_c / f; +// uint32_t best_deviation = UINT32_MAX; +// for (uint32_t c = (1UL << 20) - 1; c >= (1UL << 19); c--) { +// uint32_t guess_b = (uint64_t) f_rem * c / f; +// for (uint32_t b = guess_b; b <= guess_b + 1; b++) { +// int32_t f_div = (uint64_t) f * b / c; +// uint32_t deviation = abs(f_rem - f_div); +// if (deviation < best_deviation) { +// best_b = b; +// best_c = c; +// best_deviation = deviation; +// if (deviation <= 3) { +// break; +// } +// } +// } +// if (best_deviation <= 3) { +// break; +// } +// } +// LOG_DEBUG( +// "Optimal divider for %luHz/%luHz is: a=%lu, b=%lu, c=%lu (%luHz deviation)", +// f_pll, f, a, best_b, best_c, best_deviation); // convert to Si5351C parameters uint32_t floor = 128 * best_b / best_c; P1 = 128 * a + floor - 512; diff --git a/Software/VNA_embedded/Application/Hardware.cpp b/Software/VNA_embedded/Application/Hardware.cpp index d7e1ebf..b788a8b 100644 --- a/Software/VNA_embedded/Application/Hardware.cpp +++ b/Software/VNA_embedded/Application/Hardware.cpp @@ -131,10 +131,6 @@ bool HW::Init() { // Set phase increment according to FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::DFTphaseInc); - // Enable new data and sweep halt interrupt - FPGA::EnableInterrupt(FPGA::Interrupt::NewData); - FPGA::EnableInterrupt(FPGA::Interrupt::SweepHalted); - Exti::SetCallback(FPGA_INTR_GPIO_Port, FPGA_INTR_Pin, Exti::EdgeType::Rising, Exti::Pull::Down, FPGA_Interrupt); // Initialize PLLs and build VCO maps diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp index 6ace39a..2522051 100644 --- a/Software/VNA_embedded/Application/Hardware.hpp +++ b/Software/VNA_embedded/Application/Hardware.hpp @@ -2,6 +2,7 @@ #include #include "Protocol.hpp" +#include "FPGA/FPGA.hpp" #define USE_DEBUG_PINS @@ -31,10 +32,9 @@ static constexpr uint32_t LO1_minFreq = 25000000; static constexpr uint32_t MaxSamples = 130944; static constexpr uint32_t MinSamples = 16; static constexpr uint32_t PLLRef = 100000000; -static constexpr uint16_t MaxPoints = 4501; -static constexpr uint8_t ADCprescaler = 102400000UL / ADCSamplerate; -static_assert(ADCprescaler * ADCSamplerate == 102400000UL, "ADCSamplerate can not be reached exactly"); +static constexpr uint8_t ADCprescaler = FPGA::Clockrate / ADCSamplerate; +static_assert(ADCprescaler * ADCSamplerate == FPGA::Clockrate, "ADCSamplerate can not be reached exactly"); static constexpr uint16_t DFTphaseInc = 4096 * IF2 / ADCSamplerate; static_assert(DFTphaseInc * ADCSamplerate == 4096 * IF2, "DFT can not be computed for 2.IF"); @@ -43,7 +43,7 @@ static constexpr Protocol::DeviceLimits Limits = { .maxFreq = 6000000000, .minIFBW = ADCSamplerate / MaxSamples, .maxIFBW = ADCSamplerate / MinSamples, - .maxPoints = MaxPoints, + .maxPoints = FPGA::MaxPoints, .cdbm_min = -4000, .cdbm_max = 0, .minRBW = (uint32_t) (ADCSamplerate * 2.23f / MaxSamples), diff --git a/Software/VNA_embedded/Application/Manual.cpp b/Software/VNA_embedded/Application/Manual.cpp index 42cb1e2..41061f7 100644 --- a/Software/VNA_embedded/Application/Manual.cpp +++ b/Software/VNA_embedded/Application/Manual.cpp @@ -73,6 +73,9 @@ void Manual::Setup(Protocol::ManualControl m) { FPGA::Enable(FPGA::Periphery::ExcitePort2, m.PortSwitch == 1); FPGA::Enable(FPGA::Periphery::PortSwitch); + // Enable new data and sweep halt interrupt + FPGA::EnableInterrupt(FPGA::Interrupt::NewData); + active = true; FPGA::StartSweep(); } diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp index 78e5625..9f53e94 100644 --- a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp +++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp @@ -11,6 +11,8 @@ #define LOG_MODULE "SA" #include "Log.h" +using namespace HWHAL; + static Protocol::SpectrumAnalyzerSettings s; static uint32_t pointCnt; static uint32_t points; @@ -21,10 +23,11 @@ static Protocol::PacketInfo p; static bool active = false; static uint32_t lastLO2; static uint32_t actualRBW; +static bool usingDFT; +static uint16_t DFTpoints; +static bool negativeDFT; // if true, a positive frequency shift at input results in a negative shift at the 2.IF. Handle DFT accordingly -static float port1Measurement, port2Measurement; - -using namespace HWHAL; +static float port1Measurement[FPGA::DFTbins], port2Measurement[FPGA::DFTbins]; static void StartNextSample() { uint64_t freq = s.f_start + (s.f_stop - s.f_start) * pointCnt / (points - 1); @@ -34,16 +37,20 @@ static void StartNextSample() { case 0: default: // reset minimum amplitudes in first signal ID step - port1Measurement = std::numeric_limits::max(); - port2Measurement = std::numeric_limits::max(); + for (uint16_t i = 0; i < DFTpoints; i++) { + port1Measurement[i] = std::numeric_limits::max(); + port2Measurement[i] = std::numeric_limits::max(); + } // Use default LO frequencies LO1freq = freq + HW::IF1; LO2freq = HW::IF1 - HW::IF2; FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, 112); FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, 1120); + negativeDFT = true; break; case 1: LO2freq = HW::IF1 - HW::IF2; + negativeDFT = false; // Shift first LO to other side // depending on the measurement frequency this is not possible or additive mixing has to be used if(freq >= HW::IF1 + HW::LO1_minFreq) { @@ -59,13 +66,15 @@ static void StartNextSample() { signalIDstep++; /* no break */ case 2: - // Shift both LOs to other side + // Shift second LOs to other side LO1freq = freq + HW::IF1; LO2freq = HW::IF1 + HW::IF2; + negativeDFT = false; break; case 3: + // Shift both LO to other side LO2freq = HW::IF1 + HW::IF2; - // Shift second LO to other side + negativeDFT = true; // depending on the measurement frequency this is not possible or additive mixing has to be used if(freq >= HW::IF1 + HW::LO1_minFreq) { // frequency is high enough to shift 1.LO below measurement frequency @@ -81,6 +90,7 @@ static void StartNextSample() { /* no break */ case 4: // Use default frequencies with different ADC samplerate to remove images in final IF + negativeDFT = true; LO1freq = freq + HW::IF1; LO2freq = HW::IF1 - HW::IF2; FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, 120); @@ -96,6 +106,17 @@ static void StartNextSample() { Si5351.SetCLK(SiChannel::Port2LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); lastLO2 = LO2freq; } + if (usingDFT) { + uint32_t spacing = (s.f_stop - s.f_start) / (points - 1); + uint32_t start = HW::IF2; + if(negativeDFT) { + // needs to look below the start frequency, shift start + start -= spacing * (DFTpoints - 1); + } + FPGA::SetupDFT(start, spacing); + FPGA::StartDFT(); + } + // Configure the sampling in the FPGA FPGA::WriteSweepConfig(0, 0, Source.GetRegisters(), LO1.GetRegisters(), 0, 0, FPGA::SettlingTime::us20, FPGA::Samples::SPPRegister, 0, @@ -111,9 +132,6 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) { s = settings; HW::SetMode(HW::Mode::SA); FPGA::SetMode(FPGA::Mode::FPGA); - FPGA::DisableInterrupt(FPGA::Interrupt::NewData); - FPGA::DisableInterrupt(FPGA::Interrupt::SweepHalted); - FPGA::EnableInterrupt(FPGA::Interrupt::DFTReady); // in almost all cases a full sweep requires more points than the FPGA can handle at a time // individually start each point and do the sweep in the uC FPGA::SetNumberOfPoints(1); @@ -148,12 +166,17 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) { FPGA::Enable(FPGA::Periphery::Port1Mixer); FPGA::Enable(FPGA::Periphery::Port2Mixer); - // Configure DFT - LOG_INFO("DFT samples: %lu", sampleNum); - FPGA::WriteRegister(FPGA::Reg::DFTSamples, sampleNum - 1); - FPGA::WriteRegister(FPGA::Reg::DFTWindowInc, 65536 / sampleNum); - FPGA::WriteRegister(FPGA::Reg::DFTFirstBin, 17920); - FPGA::WriteRegister(FPGA::Reg::DFTFreqSpacing, 1147); + // automatically select DFT mode for lower RBWs + usingDFT = actualRBW <= 1000; + + if (usingDFT) { + DFTpoints = FPGA::DFTbins; // use full DFT in FPGA + FPGA::DisableInterrupt(FPGA::Interrupt::NewData); + } else { + DFTpoints = 1; // can only measure one point at a time + FPGA::StopDFT(); + FPGA::EnableInterrupt(FPGA::Interrupt::NewData); + } lastLO2 = 0; active = true; @@ -166,26 +189,39 @@ bool SA::MeasurementDone(const FPGA::SamplingResult &result) { } FPGA::AbortSweep(); - uint16_t i=0; - while(FPGA::GetStatus() & (uint16_t) FPGA::Interrupt::DFTReady) { - auto dft = FPGA::ReadDFTResult(); - dft.P1 /= sampleNum; - dft.P2 /= sampleNum; - LOG_INFO("DFT %d: %lu, %lu", i, (uint32_t) dft.P1, (uint32_t) dft.P2); - Log_Flush(); - i++; - } - FPGA::DisableInterrupt(FPGA::Interrupt::DFTReady); - FPGA::EnableInterrupt(FPGA::Interrupt::DFTReady); + for(uint16_t i=0;i(result.P1I, result.P1Q)); + port2 = abs(std::complex(result.P2I, result.P2Q)); + } + port1 /= sampleNum; + port2 /= sampleNum; - float port1 = abs(std::complex(result.P1I, result.P1Q))/sampleNum; - float port2 = abs(std::complex(result.P2I, result.P2Q))/sampleNum; - if(port1 < port1Measurement) { - port1Measurement = port1; + uint16_t index = i; + if (negativeDFT) { + // bin order is reversed + index = DFTpoints - i - 1; + } + + if(port1 < port1Measurement[index]) { + port1Measurement[index] = port1; + } + if(port2 < port2Measurement[index]) { + port2Measurement[index] = port2; + } } - if(port2 < port2Measurement) { - port2Measurement = port2; + + if (usingDFT) { + FPGA::StopDFT(); + // will be started again in StartNextSample } + // trigger work function return true; } @@ -196,74 +232,76 @@ void SA::Work() { } if(!s.SignalID || signalIDstep >= 4) { // this measurement point is done, handle result according to detector - uint16_t binIndex = pointCnt / binSize; - uint32_t pointInBin = pointCnt % binSize; - bool lastPointInBin = pointInBin >= binSize - 1; - auto det = (Detector) s.Detector; - if(det == Detector::Normal) { - det = binIndex & 0x01 ? Detector::PosPeak : Detector::NegPeak; - } - switch(det) { - case Detector::PosPeak: - if(pointInBin == 0) { - p.spectrumResult.port1 = std::numeric_limits::min(); - p.spectrumResult.port2 = std::numeric_limits::min(); + for(uint16_t i=0;i= binSize - 1; + auto det = (Detector) s.Detector; + if(det == Detector::Normal) { + det = binIndex & 0x01 ? Detector::PosPeak : Detector::NegPeak; } - if(port1Measurement > p.spectrumResult.port1) { - p.spectrumResult.port1 = port1Measurement; + switch(det) { + case Detector::PosPeak: + if(pointInBin == 0) { + p.spectrumResult.port1 = std::numeric_limits::min(); + p.spectrumResult.port2 = std::numeric_limits::min(); + } + if(port1Measurement[i] > p.spectrumResult.port1) { + p.spectrumResult.port1 = port1Measurement[i]; + } + if(port2Measurement[i] > p.spectrumResult.port2) { + p.spectrumResult.port2 = port2Measurement[i]; + } + break; + case Detector::NegPeak: + if(pointInBin == 0) { + p.spectrumResult.port1 = std::numeric_limits::max(); + p.spectrumResult.port2 = std::numeric_limits::max(); + } + if(port1Measurement[i] < p.spectrumResult.port1) { + p.spectrumResult.port1 = port1Measurement[i]; + } + if(port2Measurement[i] < p.spectrumResult.port2) { + p.spectrumResult.port2 = port2Measurement[i]; + } + break; + case Detector::Sample: + if(pointInBin <= binSize / 2) { + // still in first half of bin, simply overwrite + p.spectrumResult.port1 = port1Measurement[i]; + p.spectrumResult.port2 = port2Measurement[i]; + } + break; + case Detector::Average: + if(pointInBin == 0) { + p.spectrumResult.port1 = 0; + p.spectrumResult.port2 = 0; + } + p.spectrumResult.port1 += port1Measurement[i]; + p.spectrumResult.port2 += port2Measurement[i]; + if(lastPointInBin) { + // calculate average + p.spectrumResult.port1 /= binSize; + p.spectrumResult.port2 /= binSize; + } + break; + case Detector::Normal: + // nothing to do, normal detector handled by PosPeak or NegPeak in each sample + break; } - if(port2Measurement > p.spectrumResult.port2) { - p.spectrumResult.port2 = port2Measurement; - } - break; - case Detector::NegPeak: - if(pointInBin == 0) { - p.spectrumResult.port1 = std::numeric_limits::max(); - p.spectrumResult.port2 = std::numeric_limits::max(); - } - if(port1Measurement < p.spectrumResult.port1) { - p.spectrumResult.port1 = port1Measurement; - } - if(port2Measurement < p.spectrumResult.port2) { - p.spectrumResult.port2 = port2Measurement; - } - break; - case Detector::Sample: - if(pointInBin <= binSize / 2) { - // still in first half of bin, simply overwrite - p.spectrumResult.port1 = port1Measurement; - p.spectrumResult.port2 = port2Measurement; - } - break; - case Detector::Average: - if(pointInBin == 0) { - p.spectrumResult.port1 = 0; - p.spectrumResult.port2 = 0; - } - p.spectrumResult.port1 += port1Measurement; - p.spectrumResult.port2 += port2Measurement; if(lastPointInBin) { - // calculate average - p.spectrumResult.port1 /= binSize; - p.spectrumResult.port2 /= binSize; + // Send result to application + p.type = Protocol::PacketType::SpectrumAnalyzerResult; + // measurements are already up to date, fill remaining fields + p.spectrumResult.pointNum = binIndex; + p.spectrumResult.frequency = s.f_start + (s.f_stop - s.f_start) * binIndex / (s.pointNum - 1); + Communication::Send(p); } - break; - case Detector::Normal: - // nothing to do, normal detector handled by PosPeak or NegPeak in each sample - break; - } - if(lastPointInBin) { - // Send result to application - p.type = Protocol::PacketType::SpectrumAnalyzerResult; - // measurements are already up to date, fill remaining fields - p.spectrumResult.pointNum = binIndex; - p.spectrumResult.frequency = s.f_start + (s.f_stop - s.f_start) * binIndex / (s.pointNum - 1); - Communication::Send(p); } // setup for next step signalIDstep = 0; - if(pointCnt < points - 1) { - pointCnt++; + if(pointCnt < points - DFTpoints) { + pointCnt += DFTpoints; } else { pointCnt = 0; } diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp index 8d74732..450c10d 100644 --- a/Software/VNA_embedded/Application/VNA.cpp +++ b/Software/VNA_embedded/Application/VNA.cpp @@ -187,6 +187,9 @@ bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) { IFTableIndexCnt = 0; adcShifted = false; active = true; + // Enable new data and sweep halt interrupt + FPGA::EnableInterrupt(FPGA::Interrupt::NewData); + FPGA::EnableInterrupt(FPGA::Interrupt::SweepHalted); // Start the sweep FPGA::StartSweep(); return true;