Test analog acquisition chain
This commit is contained in:
		
							parent
							
								
									131fe91c67
								
							
						
					
					
						commit
						716d16e6a3
					
				|  | @ -0,0 +1,210 @@ | ||||||
|  | -- | ||||||
|  | --  Analog acquisition chain. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | use work.puzzlefw_pkg.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity acquisition_chain is | ||||||
|  | 
 | ||||||
|  |     generic ( | ||||||
|  |         -- Number of analog input channels. It should be either 2 or 4. | ||||||
|  |         -- If num_channels == 2, the acquisition always runs in 2-channel mode. | ||||||
|  |         -- IF num_channels == 4, the acquisition can run in 2-channel mode | ||||||
|  |         -- or 4-channel mode. | ||||||
|  |         num_channels:       integer range 2 to 4 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Reset, active high, synchronous to main clock. | ||||||
|  |         reset:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to enable data acquisition. | ||||||
|  |         -- Low to disable data acquisition, ignore triggers, stop emitting | ||||||
|  |         -- samples and clear the trigger status. | ||||||
|  |         acquisition_en:     in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Trigger delay in clock cycles. | ||||||
|  |         trigger_delay:      in  std_logic_vector(15 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Number of decimated samples per trigger minus 1. | ||||||
|  |         record_length:      in  std_logic_vector(15 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Decimation factor minus 1. | ||||||
|  |         decimation_factor:  in  std_logic_vector(17 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High to sum input samples; low to select single samples. | ||||||
|  |         averaging:          in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Number of right-shift steps to apply to ADC data. | ||||||
|  |         -- When set to zero, the least significant ADC sample bit aligns | ||||||
|  |         -- with the least significant decimated sample bit. | ||||||
|  |         shift_steps:        in  std_logic_vector(3 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High to enable 4-channel mode. | ||||||
|  |         -- Ignored if num_channels == 2. | ||||||
|  |         ch4_mode:           in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to use simulated samples in place of real ADC samples. | ||||||
|  |         simulate_adc:       in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to enable automatic (continuous) triggering. | ||||||
|  |         trig_auto_en:       in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to enable external triggering. | ||||||
|  |         trig_ext_en:        in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to force a trigger event (if the acquisition chain is ready). | ||||||
|  |         trig_force:         in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Select external input signal to use as trigger. | ||||||
|  |         trig_ext_select:    in  std_logic_vector(1 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High to trigger on falling edge, low to trigger on rising edge. | ||||||
|  |         trig_ext_falling:   in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Global timestamp counter. | ||||||
|  |         timestamp_in:       in  std_logic_vector(timestamp_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- ADC input data. | ||||||
|  |         adc_data_in:        in  adc_data_array(0 to num_channels - 1); | ||||||
|  | 
 | ||||||
|  |         -- External trigger signals, already synchronized and de-glitched. | ||||||
|  |         trig_ext_in:        in  std_logic_vector(3 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High if the acquisition chain is waiting for a trigger. | ||||||
|  |         trig_waiting:       out std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Output data stream. | ||||||
|  |         out_valid:          out std_logic; | ||||||
|  |         out_ready:          in  std_logic; | ||||||
|  |         out_empty:          in  std_logic; | ||||||
|  |         out_data:           out dma_data_type | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of acquisition_chain is | ||||||
|  | 
 | ||||||
|  |     -- Number of data bits for accumulating ADC samples when averaging. | ||||||
|  |     constant accum_data_bits: natural := 32; | ||||||
|  | 
 | ||||||
|  |     subtype accum_data_type is std_logic_vector(accum_data_bits - 1 downto 0); | ||||||
|  |     type accum_data_array is array(natural range <>) of accum_data_type; | ||||||
|  | 
 | ||||||
|  |     signal s_trigger:           std_logic; | ||||||
|  |     signal s_trig_ack:          std_logic; | ||||||
|  |     signal s_sample_start:      std_logic; | ||||||
|  |     signal s_sample_integrate:  std_logic; | ||||||
|  |     signal s_sample_done:       std_logic; | ||||||
|  |     signal s_adc_sample:        adc_data_array(0 to num_channels - 1); | ||||||
|  |     signal s_adc_shifted:       accum_data_array(0 to num_channels - 1); | ||||||
|  |     signal s_sample_decimated:  sample_data_array(0 to num_channels - 1); | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- External trigger detector. | ||||||
|  |     inst_trigger_detector: entity work.trigger_detector | ||||||
|  |         port map ( | ||||||
|  |             clk             => clk, | ||||||
|  |             reset           => reset, | ||||||
|  |             trig_auto_en    => trig_auto_en, | ||||||
|  |             trig_ext_en     => trig_ext_en, | ||||||
|  |             trig_force      => trig_force, | ||||||
|  |             trig_select     => trig_ext_select, | ||||||
|  |             trig_falling    => trig_ext_falling, | ||||||
|  |             trig_ext_in     => trig_ext_in, | ||||||
|  |             trig_out        => s_trigger ); | ||||||
|  | 
 | ||||||
|  |     -- Timing of sampling and decimation. | ||||||
|  |     inst_acquisition_manager: entity work.acquisition_manager | ||||||
|  |         port map ( | ||||||
|  |             clk             => clk, | ||||||
|  |             reset           => reset, | ||||||
|  |             acquisition_en  => acquisition_en, | ||||||
|  |             trigger_delay   => trigger_delay, | ||||||
|  |             record_length   => record_length, | ||||||
|  |             decimation_factor => decimation_factor, | ||||||
|  |             averaging       => averaging, | ||||||
|  |             trig_in         => s_trigger, | ||||||
|  |             trig_waiting    => trig_waiting, | ||||||
|  |             trig_ack        => s_trig_ack, | ||||||
|  |             sample_start    => s_sample_start, | ||||||
|  |             sample_integrate => s_sample_integrate, | ||||||
|  |             sample_done     => s_sample_done ); | ||||||
|  | 
 | ||||||
|  |     -- Optionally generate simulated ADC data. | ||||||
|  |     inst_adc_sample_stream: entity work.adc_sample_stream | ||||||
|  |         port map ( | ||||||
|  |             clk                 => clk, | ||||||
|  |             reset               => reset, | ||||||
|  |             simulate            => simulate_adc, | ||||||
|  |             in_data             => adc_data_in(0 to 1), | ||||||
|  |             out_data            => s_adc_sample(0 to 1) ); | ||||||
|  | 
 | ||||||
|  |     inst_adc_sample_stream_gen: if num_channels > 2 generate | ||||||
|  |         inst_adc_sample_stream2: entity work.adc_sample_stream | ||||||
|  |             port map ( | ||||||
|  |                 clk                 => clk, | ||||||
|  |                 reset               => reset, | ||||||
|  |                 simulate            => simulate_adc, | ||||||
|  |                 in_data             => adc_data_in(2 to 3), | ||||||
|  |                 out_data            => s_adc_sample(2 to 3) ); | ||||||
|  |     end generate; | ||||||
|  | 
 | ||||||
|  |     -- Shift and decimation chains for each ADC channel. | ||||||
|  |     inst_channels: for i in 0 to num_channels - 1 generate | ||||||
|  | 
 | ||||||
|  |         inst_shift: entity work.shift_engine | ||||||
|  |             generic map ( | ||||||
|  |                 input_data_bits => adc_data_bits, | ||||||
|  |                 output_data_bits => accum_data_bits, | ||||||
|  |                 pre_shift_left  => accum_data_bits - sample_data_bits, | ||||||
|  |                 signed_data     => false ) | ||||||
|  |             port map ( | ||||||
|  |                 clk             => clk, | ||||||
|  |                 in_data         => s_adc_sample(i), | ||||||
|  |                 in_shift        => shift_steps, | ||||||
|  |                 out_data        => s_adc_shifted(i) ); | ||||||
|  | 
 | ||||||
|  |         inst_decimation: entity work.sample_decimation | ||||||
|  |             generic map ( | ||||||
|  |                 input_data_bits => accum_data_bits, | ||||||
|  |                 output_data_bits => sample_data_bits ) | ||||||
|  |             port map ( | ||||||
|  |                 clk             => clk, | ||||||
|  |                 reset           => reset, | ||||||
|  |                 start           => s_sample_start, | ||||||
|  |                 integrate       => s_sample_integrate, | ||||||
|  |                 in_data         => s_adc_shifted(i), | ||||||
|  |                 out_data        => s_sample_decimated(i) ); | ||||||
|  |   | ||||||
|  |     end generate; | ||||||
|  | 
 | ||||||
|  |     -- Format sample data stream and insert trigger timestamps. | ||||||
|  |     inst_stream: entity work.acquisition_stream | ||||||
|  |         generic map ( | ||||||
|  |             num_channels    => num_channels ) | ||||||
|  |         port map ( | ||||||
|  |             clk             => clk, | ||||||
|  |             reset           => reset, | ||||||
|  |             ch4_mode        => ch4_mode, | ||||||
|  |             timestamp_in    => timestamp_in, | ||||||
|  |             trig_ack        => s_trig_ack, | ||||||
|  |             sample_valid    => s_sample_done, | ||||||
|  |             sample_data     => s_sample_decimated, | ||||||
|  |             out_valid       => out_valid, | ||||||
|  |             out_ready       => out_ready, | ||||||
|  |             out_empty       => out_empty, | ||||||
|  |             out_data        => out_data ); | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -0,0 +1,201 @@ | ||||||
|  | -- | ||||||
|  | --  Handle sample acquisition and triggering. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity acquisition_manager is | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Reset, active high, synchronous to main clock. | ||||||
|  |         reset:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to enable data acquisition. | ||||||
|  |         -- Low to disable data acquisition, ignore triggers, stop emitting | ||||||
|  |         -- samples and clear the trigger status. | ||||||
|  |         acquisition_en:     in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Trigger delay in clock cycles. | ||||||
|  |         trigger_delay:      in  std_logic_vector(15 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Number of decimated samples per trigger minus 1. | ||||||
|  |         record_length:      in  std_logic_vector(15 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Decimation factor minus 1. | ||||||
|  |         decimation_factor:  in  std_logic_vector(17 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High to sum input samples; low to select single samples. | ||||||
|  |         averaging:          in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High when a trigger condition occurs. | ||||||
|  |         trig_in:            in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High if the acquisition chain is waiting for a trigger. | ||||||
|  |         -- This not a handshake signal. It does not accurately predict | ||||||
|  |         -- whether a trigger will be accepted or ignored. | ||||||
|  |         trig_waiting:       out std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High for one cycle when a trigger occurs, after the configured | ||||||
|  |         -- trigger delay has passed. This indicates that a trigger timestamp | ||||||
|  |         -- record must be generated. | ||||||
|  |         trig_ack:           out std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to initiate the capture of a new decimated sample. | ||||||
|  |         sample_start:       out std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to accumulate additional raw samples into a decimated sample. | ||||||
|  |         sample_integrate:   out std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to emit a decimated sample. | ||||||
|  |         sample_done:        out std_logic | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of acquisition_manager is | ||||||
|  | 
 | ||||||
|  |     type state_type is (STATE_IDLE, STATE_DELAY, STATE_CAPTURE); | ||||||
|  | 
 | ||||||
|  |     type regs_type is record | ||||||
|  |         state:              state_type; | ||||||
|  |         delay_cnt:          unsigned(15 downto 0); | ||||||
|  |         record_cnt:         unsigned(15 downto 0); | ||||||
|  |         decimation_cnt:     unsigned(17 downto 0); | ||||||
|  |         trig_waiting:       std_logic; | ||||||
|  |         trig_ack:           std_logic; | ||||||
|  |         sample_start:       std_logic; | ||||||
|  |         sample_integrate:   std_logic; | ||||||
|  |         sample_done:        std_logic; | ||||||
|  |     end record; | ||||||
|  | 
 | ||||||
|  |     constant regs_init: regs_type := ( | ||||||
|  |         state               => STATE_IDLE, | ||||||
|  |         delay_cnt           => (others => '0'), | ||||||
|  |         record_cnt          => (others => '0'), | ||||||
|  |         decimation_cnt      => (others => '0'), | ||||||
|  |         trig_waiting        => '0', | ||||||
|  |         trig_ack            => '0', | ||||||
|  |         sample_start        => '0', | ||||||
|  |         sample_integrate    => '0', | ||||||
|  |         sample_done         => '0' | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     signal r: regs_type := regs_init; | ||||||
|  |     signal rnext: regs_type; | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output ports. | ||||||
|  |     trig_waiting        <= r.trig_waiting; | ||||||
|  |     trig_ack            <= r.trig_ack; | ||||||
|  |     sample_start        <= r.sample_start; | ||||||
|  |     sample_integrate    <= r.sample_integrate; | ||||||
|  |     sample_done         <= r.sample_done; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Combinatorial process. | ||||||
|  |     -- | ||||||
|  |     process (all) is | ||||||
|  |         variable v: regs_type; | ||||||
|  |     begin | ||||||
|  |         -- Load current register values. | ||||||
|  |         v := r; | ||||||
|  | 
 | ||||||
|  |         -- Default assignments. | ||||||
|  |         v.trig_ack := '0'; | ||||||
|  |         v.sample_start := '0'; | ||||||
|  |         v.sample_integrate := '0'; | ||||||
|  |         v.sample_done := '0'; | ||||||
|  | 
 | ||||||
|  |         -- State machine. | ||||||
|  |         case r.state is | ||||||
|  | 
 | ||||||
|  |             when STATE_IDLE => | ||||||
|  |                 -- Waiting for trigger. | ||||||
|  |                 v.delay_cnt := unsigned(trigger_delay); | ||||||
|  |                 v.record_cnt := unsigned(record_length); | ||||||
|  |                 v.decimation_cnt := unsigned(decimation_factor); | ||||||
|  |                 v.trig_waiting := acquisition_en; | ||||||
|  |                 if acquisition_en = '1' and trig_in = '1' then | ||||||
|  |                     if unsigned(trigger_delay) = 0 then | ||||||
|  |                         v.trig_ack := '1'; | ||||||
|  |                         v.sample_start := '1'; | ||||||
|  |                         v.state := STATE_CAPTURE; | ||||||
|  |                     else | ||||||
|  |                         v.state := STATE_DELAY; | ||||||
|  |                     end if; | ||||||
|  |                 end if; | ||||||
|  | 
 | ||||||
|  |             when STATE_DELAY => | ||||||
|  |                 -- Wait for end of trigger delay. | ||||||
|  |                 v.delay_cnt := r.delay_cnt - 1; | ||||||
|  |                 if r.delay_cnt = 1 then | ||||||
|  |                     v.trig_ack := '1'; | ||||||
|  |                     v.sample_start := '1'; | ||||||
|  |                     v.state := STATE_CAPTURE; | ||||||
|  |                 end if; | ||||||
|  | 
 | ||||||
|  |             when STATE_CAPTURE => | ||||||
|  |                 -- Capture samples. | ||||||
|  |                 v.delay_cnt := unsigned(trigger_delay); | ||||||
|  |                 v.decimation_cnt := r.decimation_cnt - 1; | ||||||
|  |                 if r.decimation_cnt = 0 then | ||||||
|  |                     v.sample_done := '1'; | ||||||
|  |                     v.record_cnt := r.record_cnt - 1; | ||||||
|  |                     v.decimation_cnt := unsigned(decimation_factor); | ||||||
|  |                     if r.record_cnt = 0 then | ||||||
|  |                         v.record_cnt := unsigned(record_length); | ||||||
|  |                         if trig_in = '1' then | ||||||
|  |                             if unsigned(trigger_delay) = 0 then | ||||||
|  |                                 v.trig_ack := '1'; | ||||||
|  |                                 v.sample_start := '1'; | ||||||
|  |                                 v.state := STATE_CAPTURE; | ||||||
|  |                             else | ||||||
|  |                                 v.state := STATE_DELAY; | ||||||
|  |                             end if; | ||||||
|  |                         else | ||||||
|  |                             v.state := STATE_IDLE; | ||||||
|  |                         end if; | ||||||
|  |                     else | ||||||
|  |                         v.sample_start := '1'; | ||||||
|  |                     end if; | ||||||
|  |                 else | ||||||
|  |                     v.sample_integrate := averaging; | ||||||
|  |                 end if; | ||||||
|  | 
 | ||||||
|  |         end case; | ||||||
|  | 
 | ||||||
|  |         -- Stop acquisition immediately when disabled. | ||||||
|  |         if acquisition_en = '0' then | ||||||
|  |             v.state := STATE_IDLE; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Synchronous reset. | ||||||
|  |         if reset = '1' then | ||||||
|  |             v := regs_init; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Drive new register values to synchronous process. | ||||||
|  |         rnext <= v; | ||||||
|  |      | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  |             r <= rnext; | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -0,0 +1,201 @@ | ||||||
|  | -- | ||||||
|  | --  Format analog acquisition data into a stream of 64-bit words. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | use work.puzzlefw_pkg.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity acquisition_stream is | ||||||
|  | 
 | ||||||
|  |     generic ( | ||||||
|  |         -- Number of analog input channels. It should be either 2 or 4. | ||||||
|  |         num_channels:       integer range 2 to 4 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Reset, active high, synchronous to main clock. | ||||||
|  |         reset:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to enable 4-channel mode. | ||||||
|  |         -- Ignored if num_channels == 2. | ||||||
|  |         ch4_mode:           in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Global timestamp counter. | ||||||
|  |         timestamp_in:       in  std_logic_vector(timestamp_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High for one cycle when a trigger occurs. | ||||||
|  |         trig_ack:           in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Decimated sample stream. | ||||||
|  |         sample_valid:       in  std_logic; | ||||||
|  |         sample_data:        in  sample_data_array(0 to num_channels - 1); | ||||||
|  | 
 | ||||||
|  |         -- Output data stream. | ||||||
|  |         out_valid:          out std_logic; | ||||||
|  |         out_ready:          in  std_logic; | ||||||
|  |         out_empty:          in  std_logic; | ||||||
|  |         out_data:           out dma_data_type | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of acquisition_stream is | ||||||
|  | 
 | ||||||
|  |     type regs_type is record | ||||||
|  |         overflow:           std_logic; | ||||||
|  |         trig_pending:       std_logic; | ||||||
|  |         trig_timestamp:     std_logic_vector(timestamp_bits - 1 downto 0); | ||||||
|  |         sample_pending:     std_logic; | ||||||
|  |         sample_data:        sample_data_array(0 to num_channels - 3); | ||||||
|  |         out_valid:          std_logic; | ||||||
|  |         out_data:           dma_data_type; | ||||||
|  |     end record; | ||||||
|  | 
 | ||||||
|  |     constant regs_init: regs_type := ( | ||||||
|  |         overflow            => '0', | ||||||
|  |         trig_pending        => '0', | ||||||
|  |         trig_timestamp      => (others => '0'), | ||||||
|  |         sample_pending      => '0', | ||||||
|  |         sample_data         => (others => (others => '0')), | ||||||
|  |         out_valid           => '0', | ||||||
|  |         out_data            => (others => '0') | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     signal r: regs_type := regs_init; | ||||||
|  |     signal rnext: regs_type; | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output ports. | ||||||
|  |     out_valid           <= r.out_valid; | ||||||
|  |     out_data            <= r.out_data; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Combinatorial process. | ||||||
|  |     -- | ||||||
|  |     process (all) is | ||||||
|  |         variable v: regs_type; | ||||||
|  |     begin | ||||||
|  |         -- Load current register values. | ||||||
|  |         v := r; | ||||||
|  | 
 | ||||||
|  |         -- By default, do not emit data. | ||||||
|  |         v.out_valid := '0'; | ||||||
|  | 
 | ||||||
|  |         if sample_valid = '1' then | ||||||
|  | 
 | ||||||
|  |             -- Emit first sample data word. | ||||||
|  |             v.out_valid := '1'; | ||||||
|  |             v.out_data(63 downto 56) := msg_adc_data; | ||||||
|  |             v.out_data(55 downto 48) := x"10"; | ||||||
|  |             v.out_data(23 downto 0) := sample_data(0); | ||||||
|  |             v.out_data(47 downto 24) := sample_data(1); | ||||||
|  | 
 | ||||||
|  |             -- Detect internal overflow. | ||||||
|  |             if (r.sample_pending = '1') or (r.trig_pending = '1') then | ||||||
|  |                 v.overflow := '1'; | ||||||
|  |             end if; | ||||||
|  | 
 | ||||||
|  |         elsif r.sample_pending = '1' then | ||||||
|  | 
 | ||||||
|  |             -- Emit second sample data word. | ||||||
|  |             v.sample_pending := '0'; | ||||||
|  |             v.out_valid := '1'; | ||||||
|  |             v.out_data(63 downto 56) := msg_adc_data; | ||||||
|  |             v.out_data(55 downto 48) := x"32"; | ||||||
|  |             v.out_data(47 downto 0) := (others => '0'); | ||||||
|  |             if num_channels > 2 then | ||||||
|  |                 v.out_data(23 downto 0) := r.sample_data(0); | ||||||
|  |             end if; | ||||||
|  |             if num_channels > 3 then | ||||||
|  |                 v.out_data(47 downto 24) := r.sample_data(1); | ||||||
|  |             end if; | ||||||
|  | 
 | ||||||
|  |         elsif r.trig_pending = '1' then | ||||||
|  | 
 | ||||||
|  |             -- Emit pending trigger record. | ||||||
|  |             v.trig_pending := '0'; | ||||||
|  |             v.out_valid := '1'; | ||||||
|  |             v.out_data(63 downto 56) := msg_trigger; | ||||||
|  |             v.out_data(55 downto 48) := x"00"; | ||||||
|  |             v.out_data(47 downto 0) := r.trig_timestamp; | ||||||
|  | 
 | ||||||
|  |         elsif trig_ack = '1' then | ||||||
|  | 
 | ||||||
|  |             -- Emit trigger record. | ||||||
|  |             v.out_valid := '1'; | ||||||
|  |             v.out_data(63 downto 56) := msg_trigger; | ||||||
|  |             v.out_data(55 downto 48) := x"00"; | ||||||
|  |             v.out_data(47 downto 0) := timestamp_in; | ||||||
|  | 
 | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Latch second sample data word. | ||||||
|  |         if (num_channels > 2) and (sample_valid = '1') then | ||||||
|  |             v.sample_pending := '1'; | ||||||
|  |             v.sample_data := sample_data(2 to num_channels - 1); | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Latch timestamp when a trigger occurs. | ||||||
|  |         if trig_ack = '1' then | ||||||
|  |             v.trig_timestamp := timestamp_in; | ||||||
|  |             if (sample_valid = '1') or (r.sample_pending = '1') then | ||||||
|  |                 v.trig_pending := '1'; | ||||||
|  |             end if; | ||||||
|  | 
 | ||||||
|  |             -- Detect internal overflow. | ||||||
|  |             if r.trig_pending = '1' then | ||||||
|  |                 v.overflow := '1'; | ||||||
|  |             end if; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Detect overflow of external data buffer. | ||||||
|  |         if (r.out_valid = '1') and (out_ready = '0') then | ||||||
|  |             v.overflow := '1'; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- If there is a pending overflow, discard data until the buffer | ||||||
|  |         -- is empty, then emit an overflow record. | ||||||
|  |         if r.overflow = '1' then | ||||||
|  |             v.sample_pending := '0'; | ||||||
|  |             v.trig_pending := '0'; | ||||||
|  |             v.out_valid := '0'; | ||||||
|  |             v.out_data(63 downto 56) := msg_overflow; | ||||||
|  |             v.out_data(55 downto 0) := (others => '0'); | ||||||
|  | 
 | ||||||
|  |             if out_empty = '1' then | ||||||
|  |                 v.out_valid := '1'; | ||||||
|  |                 v.overflow := '0'; | ||||||
|  |             end if; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Synchronous reset. | ||||||
|  |         if reset = '1' then | ||||||
|  |             v := regs_init; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Drive new register values to synchronous process. | ||||||
|  |         rnext <= v; | ||||||
|  |      | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  |             r <= rnext; | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -0,0 +1,61 @@ | ||||||
|  | -- | ||||||
|  | --  Capture ADC sample data from FPGA input ports. | ||||||
|  | -- | ||||||
|  | --  Input signals are captured on the rising edge of CLK. | ||||||
|  | --  This entity adds 2 clock cycles delay. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | use work.puzzlefw_pkg.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity adc_capture is | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Input signals. | ||||||
|  |         in_data:            in  std_logic_vector(adc_data_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Output sample stream. | ||||||
|  |         -- Produces one new ADC sample per clock cycle. | ||||||
|  |         out_data:           out adc_data_type | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of adc_capture is | ||||||
|  | 
 | ||||||
|  |     signal r_stage1:    std_logic_vector(adc_data_bits - 1 downto 0); | ||||||
|  |     signal r_stage2:    std_logic_vector(adc_data_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output ports. | ||||||
|  |     out_data <= r_stage2; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  | 
 | ||||||
|  |             -- Capture input signals into registers. | ||||||
|  |             -- These registers will be placed in IO flipflops through IOB constraints. | ||||||
|  |             r_stage1 <= in_data; | ||||||
|  | 
 | ||||||
|  |             -- Second register stage. | ||||||
|  |             -- These can be placed anywhere in the FPGA to optimize timing. | ||||||
|  |             r_stage2 <= r_stage1; | ||||||
|  | 
 | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -0,0 +1,80 @@ | ||||||
|  | -- | ||||||
|  | --  Manage the ADC sample stream. | ||||||
|  | -- | ||||||
|  | --  Optionally generates simulated samples in place of real ADC data. | ||||||
|  | --  The simulated sample stream works as follows: | ||||||
|  | --   - Both simulated channels output a simple increasing ramp. | ||||||
|  | --   - The output of channel 0 increments at a rate of 1 per clock cycle. | ||||||
|  | --   - The output of channel 1 increments by 1 whenever channel 0 wraps around. | ||||||
|  | -- | ||||||
|  | --  This entity adds 1 clock cycle delay in the ADC sample stream. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | use work.puzzlefw_pkg.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity adc_sample_stream is | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Reset, active high, synchronous to main clock. | ||||||
|  |         reset:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to select simulated samples in place of ADC data. | ||||||
|  |         simulate:           in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Input sample stream from ADC. | ||||||
|  |         in_data:            in  adc_data_array(0 to 1); | ||||||
|  | 
 | ||||||
|  |         -- Output sample stream. | ||||||
|  |         out_data:           out adc_data_array(0 to 1) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of adc_sample_stream is | ||||||
|  | 
 | ||||||
|  |     signal r_counter:   unsigned(2 * adc_data_bits - 1 downto 0); | ||||||
|  |     signal r_out_data:  adc_data_array(0 to 1); | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output ports. | ||||||
|  |     out_data <= r_out_data; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  | 
 | ||||||
|  |             -- Select ADC sample or simulated sample. | ||||||
|  |             if simulate = '1' then | ||||||
|  |                 -- Output simulated sample. | ||||||
|  |                 r_out_data(0) <= std_logic_vector(r_counter(adc_data_bits - 1 downto 0)); | ||||||
|  |                 r_out_data(1) <= std_logic_vector(r_counter(2 * adc_data_bits - 1 downto adc_data_bits)); | ||||||
|  |             else | ||||||
|  |                 -- Output real ADC sample. | ||||||
|  |                 r_out_data <= in_data; | ||||||
|  |             end if; | ||||||
|  | 
 | ||||||
|  |             -- Update simulated sample stream. | ||||||
|  |             if reset = '1' then | ||||||
|  |                 r_counter <= (others => '0'); | ||||||
|  |             else | ||||||
|  |                 r_counter <= r_counter + 1; | ||||||
|  |             end if; | ||||||
|  | 
 | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -83,6 +83,7 @@ entity dma_write_channel is | ||||||
|         -- Input data stream to the channel. |         -- Input data stream to the channel. | ||||||
|         in_valid:           in  std_logic; |         in_valid:           in  std_logic; | ||||||
|         in_ready:           out std_logic; |         in_ready:           out std_logic; | ||||||
|  |         in_empty:           out std_logic; | ||||||
|         in_data:            in  dma_data_type; |         in_data:            in  dma_data_type; | ||||||
| 
 | 
 | ||||||
|         -- Signals to AXI master. |         -- Signals to AXI master. | ||||||
|  | @ -173,6 +174,7 @@ begin | ||||||
|     channel_busy        <= r.channel_busy; |     channel_busy        <= r.channel_busy; | ||||||
|     addr_pointer        <= r.addr_pointer; |     addr_pointer        <= r.addr_pointer; | ||||||
|     intr_out            <= r.intr_out; |     intr_out            <= r.intr_out; | ||||||
|  |     in_empty            <= not s_fifo_valid; | ||||||
|     write_cmd_addr      <= r.cmd_addr; |     write_cmd_addr      <= r.cmd_addr; | ||||||
|     write_cmd_length    <= "0000" when (r.cmd_full_burst = '0') else |     write_cmd_length    <= "0000" when (r.cmd_full_burst = '0') else | ||||||
|                            std_logic_vector(to_unsigned(transfer_size - 1, 4)); |                            std_logic_vector(to_unsigned(transfer_size - 1, 4)); | ||||||
|  | @ -226,7 +228,6 @@ begin | ||||||
| 
 | 
 | ||||||
|                     v.cmd_valid := '1'; |                     v.cmd_valid := '1'; | ||||||
|                     v.channel_busy := '1'; |                     v.channel_busy := '1'; | ||||||
|                     v.pending_beats := to_unsigned(0, v.pending_beats'length); |  | ||||||
|                     v.state := STATE_SINGLE_START; |                     v.state := STATE_SINGLE_START; | ||||||
| 
 | 
 | ||||||
|                 end if; |                 end if; | ||||||
|  | @ -260,7 +261,7 @@ begin | ||||||
|             when STATE_SINGLE_DATA => |             when STATE_SINGLE_DATA => | ||||||
| 
 | 
 | ||||||
|                 -- Wait until data accepted. |                 -- Wait until data accepted. | ||||||
|                 if (write_data_ready = '1') and (r.pending_beats = 0) then |                 if write_data_ready = '1' then | ||||||
|                     v.state := STATE_SINGLE_IDLE; |                     v.state := STATE_SINGLE_IDLE; | ||||||
|                 end if; |                 end if; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -36,6 +36,9 @@ package puzzlefw_pkg is | ||||||
|     -- 48-bit timestamp. |     -- 48-bit timestamp. | ||||||
|     constant timestamp_bits: integer := 48; |     constant timestamp_bits: integer := 48; | ||||||
| 
 | 
 | ||||||
|  |     -- ADC input port type. | ||||||
|  |     type adc_data_input_type is array(0 to 1) of std_logic_vector(15 downto 0); | ||||||
|  | 
 | ||||||
|     -- Register addresses. |     -- Register addresses. | ||||||
|     constant reg_addr_mask:         std_logic_vector(31 downto 0) := x"0010fffc"; |     constant reg_addr_mask:         std_logic_vector(31 downto 0) := x"0010fffc"; | ||||||
|     constant reg_info:              natural := 16#000000#; |     constant reg_info:              natural := 16#000000#; | ||||||
|  | @ -44,6 +47,9 @@ package puzzlefw_pkg is | ||||||
|     constant reg_dma_en:            natural := 16#000100#; |     constant reg_dma_en:            natural := 16#000100#; | ||||||
|     constant reg_dma_status:        natural := 16#000104#; |     constant reg_dma_status:        natural := 16#000104#; | ||||||
|     constant reg_dma_clear:         natural := 16#000108#; |     constant reg_dma_clear:         natural := 16#000108#; | ||||||
|  |     constant reg_timestamp_lo:      natural := 16#000180#; | ||||||
|  |     constant reg_timestamp_hi:      natural := 16#000184#; | ||||||
|  |     constant reg_timestamp_clear:   natural := 16#000188#; | ||||||
|     constant reg_acq_addr_start:    natural := 16#000200#; |     constant reg_acq_addr_start:    natural := 16#000200#; | ||||||
|     constant reg_acq_addr_end:      natural := 16#000204#; |     constant reg_acq_addr_end:      natural := 16#000204#; | ||||||
|     constant reg_acq_addr_limit:    natural := 16#000208#; |     constant reg_acq_addr_limit:    natural := 16#000208#; | ||||||
|  | @ -51,6 +57,16 @@ package puzzlefw_pkg is | ||||||
|     constant reg_acq_addr_ptr:      natural := 16#000210#; |     constant reg_acq_addr_ptr:      natural := 16#000210#; | ||||||
|     constant reg_acq_channel_ctrl:  natural := 16#000214#; |     constant reg_acq_channel_ctrl:  natural := 16#000214#; | ||||||
|     constant reg_acq_intr_ctrl:     natural := 16#000218#; |     constant reg_acq_intr_ctrl:     natural := 16#000218#; | ||||||
|  |     constant reg_acquisition_en:    natural := 16#000220#; | ||||||
|  |     constant reg_record_length:     natural := 16#000224#; | ||||||
|  |     constant reg_decimation_factor: natural := 16#000228#; | ||||||
|  |     constant reg_shift_steps:       natural := 16#00022c#; | ||||||
|  |     constant reg_averaging_en:      natural := 16#000230#; | ||||||
|  |     constant reg_ch4_mode:          natural := 16#000234#; | ||||||
|  |     constant reg_simulate_adc:      natural := 16#000238#; | ||||||
|  |     constant reg_trigger_mode:      natural := 16#000240#; | ||||||
|  |     constant reg_trigger_delay:     natural := 16#000244#; | ||||||
|  |     constant reg_trigger_status:    natural := 16#000248#; | ||||||
|     constant reg_test_led:          natural := 16#000404#; |     constant reg_test_led:          natural := 16#000404#; | ||||||
|     constant reg_test_divider:      natural := 16#000408#; |     constant reg_test_divider:      natural := 16#000408#; | ||||||
|     constant reg_dma_buf_addr:      natural := 16#100000#; |     constant reg_dma_buf_addr:      natural := 16#100000#; | ||||||
|  | @ -71,21 +87,35 @@ package puzzlefw_pkg is | ||||||
|     constant msg_trigger:           std_logic_vector(7 downto 0) := x"02"; |     constant msg_trigger:           std_logic_vector(7 downto 0) := x"02"; | ||||||
|     constant msg_overflow:          std_logic_vector(7 downto 0) := x"10"; |     constant msg_overflow:          std_logic_vector(7 downto 0) := x"10"; | ||||||
| 
 | 
 | ||||||
|     -- ADC input port type. |  | ||||||
|     type adc_data_input_type is array(0 to 1) of std_logic_vector(15 downto 0); |  | ||||||
| 
 |  | ||||||
|     -- Control registers: read/write access by processor, output signals to FPGA. |     -- Control registers: read/write access by processor, output signals to FPGA. | ||||||
|     type registers_control is record |     type registers_control is record | ||||||
|         irq_enable:         std_logic; |         irq_enable:         std_logic; | ||||||
|         test_led:           std_logic_vector(7 downto 0); |         test_led:           std_logic_vector(7 downto 0); | ||||||
|         test_divider:       std_logic_vector(15 downto 0); |         test_divider:       std_logic_vector(15 downto 0); | ||||||
|         dma_en:             std_logic; |         dma_en:             std_logic; | ||||||
|  |         dma_clear:          std_logic;              -- single cycle | ||||||
|  |         timestamp_clear:    std_logic;              -- single cycle | ||||||
|         acq_addr_start:     std_logic_vector(31 downto 7); |         acq_addr_start:     std_logic_vector(31 downto 7); | ||||||
|         acq_addr_end:       std_logic_vector(31 downto 7); |         acq_addr_end:       std_logic_vector(31 downto 7); | ||||||
|         acq_addr_limit:     std_logic_vector(31 downto 7); |         acq_addr_limit:     std_logic_vector(31 downto 7); | ||||||
|         acq_addr_intr:      std_logic_vector(31 downto 3); |         acq_addr_intr:      std_logic_vector(31 downto 3); | ||||||
|         acq_channel_en:     std_logic; |         acq_channel_en:     std_logic; | ||||||
|  |         acq_channel_init:   std_logic;              -- single cycle | ||||||
|         acq_intr_en:        std_logic; |         acq_intr_en:        std_logic; | ||||||
|  |         acq_intr_clear:     std_logic;              -- single cycle | ||||||
|  |         acquisition_en:     std_logic; | ||||||
|  |         record_length:      std_logic_vector(15 downto 0); | ||||||
|  |         decimation_factor:  std_logic_vector(17 downto 0); | ||||||
|  |         shift_steps:        std_logic_vector(3 downto 0); | ||||||
|  |         averaging_en:       std_logic; | ||||||
|  |         ch4_mode:           std_logic; | ||||||
|  |         simulate_adc:       std_logic; | ||||||
|  |         trig_auto_en:       std_logic; | ||||||
|  |         trig_ext_en:        std_logic; | ||||||
|  |         trig_force:         std_logic;              -- single cycle | ||||||
|  |         trig_ext_select:    std_logic_vector(1 downto 0); | ||||||
|  |         trig_ext_falling:   std_logic; | ||||||
|  |         trigger_delay:      std_logic_vector(15 downto 0); | ||||||
|         dma_buf_addr:       std_logic_vector(31 downto 12); |         dma_buf_addr:       std_logic_vector(31 downto 12); | ||||||
|         dma_buf_size:       std_logic_vector(31 downto 12); |         dma_buf_size:       std_logic_vector(31 downto 12); | ||||||
|     end record; |     end record; | ||||||
|  | @ -98,15 +128,10 @@ package puzzlefw_pkg is | ||||||
|         dma_err_write:      std_logic; |         dma_err_write:      std_logic; | ||||||
|         dma_err_address:    std_logic; |         dma_err_address:    std_logic; | ||||||
|         dma_err_any:        std_logic; |         dma_err_any:        std_logic; | ||||||
|  |         timestamp:          std_logic_vector(timestamp_bits - 1 downto 0); | ||||||
|         acq_addr_ptr:       std_logic_vector(31 downto 3); |         acq_addr_ptr:       std_logic_vector(31 downto 3); | ||||||
|         acq_channel_busy:   std_logic; |         acq_channel_busy:   std_logic; | ||||||
|     end record; |         trig_waiting:       std_logic; | ||||||
| 
 |  | ||||||
|     -- Trigger registers: write-only access by processor, single-cycle pulse signals to FPGA. |  | ||||||
|     type registers_trigger is record |  | ||||||
|         dma_clear:          std_logic; |  | ||||||
|         acq_channel_init:   std_logic; |  | ||||||
|         acq_intr_clear:     std_logic; |  | ||||||
|     end record; |     end record; | ||||||
| 
 | 
 | ||||||
|     constant registers_control_init: registers_control := ( |     constant registers_control_init: registers_control := ( | ||||||
|  | @ -114,20 +139,31 @@ package puzzlefw_pkg is | ||||||
|         test_led            => (others => '0'), |         test_led            => (others => '0'), | ||||||
|         test_divider        => (others => '0'), |         test_divider        => (others => '0'), | ||||||
|         dma_en              => '0', |         dma_en              => '0', | ||||||
|  |         dma_clear           => '0', | ||||||
|  |         timestamp_clear     => '0', | ||||||
|         acq_addr_start      => (others => '0'), |         acq_addr_start      => (others => '0'), | ||||||
|         acq_addr_end        => (others => '0'), |         acq_addr_end        => (others => '0'), | ||||||
|         acq_addr_limit      => (others => '0'), |         acq_addr_limit      => (others => '0'), | ||||||
|         acq_addr_intr       => (others => '0'), |         acq_addr_intr       => (others => '0'), | ||||||
|         acq_channel_en      => '0', |         acq_channel_en      => '0', | ||||||
|  |         acq_channel_init    => '0', | ||||||
|         acq_intr_en         => '0', |         acq_intr_en         => '0', | ||||||
|  |         acq_intr_clear      => '0', | ||||||
|  |         acquisition_en      => '0', | ||||||
|  |         record_length       => (others => '0'), | ||||||
|  |         decimation_factor   => (others => '0'), | ||||||
|  |         shift_steps         => (others => '0'), | ||||||
|  |         averaging_en        => '0', | ||||||
|  |         ch4_mode            => '0', | ||||||
|  |         simulate_adc        => '0', | ||||||
|  |         trig_auto_en        => '0', | ||||||
|  |         trig_ext_en         => '0', | ||||||
|  |         trig_force          => '0', | ||||||
|  |         trig_ext_select     => (others => '0'), | ||||||
|  |         trig_ext_falling    => '0', | ||||||
|  |         trigger_delay       => (others => '0'), | ||||||
|         dma_buf_addr        => (others => '0'), |         dma_buf_addr        => (others => '0'), | ||||||
|         dma_buf_size        => (others => '0') |         dma_buf_size        => (others => '0') | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     constant registers_trigger_init: registers_trigger := ( |  | ||||||
|         dma_clear       => '0', |  | ||||||
|         acq_channel_init => '0', |  | ||||||
|         acq_intr_clear  => '0' |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
| end package; | end package; | ||||||
|  |  | ||||||
|  | @ -74,7 +74,9 @@ architecture arch of puzzlefw_top is | ||||||
|     -- Main reset signal, derived from FCLK_RESET0, active high, synchronous to clk_adc. |     -- Main reset signal, derived from FCLK_RESET0, active high, synchronous to clk_adc. | ||||||
|     signal s_reset:         std_logic; |     signal s_reset:         std_logic; | ||||||
| 
 | 
 | ||||||
|  |     -- Internal clock signal. | ||||||
|     signal s_adc_clk_ibuf:  std_logic; |     signal s_adc_clk_ibuf:  std_logic; | ||||||
|  | 
 | ||||||
|     signal r_fclk_cnt:      unsigned(26 downto 0); |     signal r_fclk_cnt:      unsigned(26 downto 0); | ||||||
|     signal r_fclk_led:      std_logic; |     signal r_fclk_led:      std_logic; | ||||||
|     signal r_adcclk_cnt:    unsigned(26 downto 0); |     signal r_adcclk_cnt:    unsigned(26 downto 0); | ||||||
|  | @ -128,13 +130,11 @@ architecture arch of puzzlefw_top is | ||||||
|     signal s_axi_rvalid:    std_logic; |     signal s_axi_rvalid:    std_logic; | ||||||
|     signal s_axi_rready:    std_logic; |     signal s_axi_rready:    std_logic; | ||||||
| 
 | 
 | ||||||
|     signal s_dma_interrupt: std_logic; |  | ||||||
|     signal s_irq_pending:   std_logic_vector(0 downto 0); |     signal s_irq_pending:   std_logic_vector(0 downto 0); | ||||||
|     signal s_irq_f2p:       std_logic_vector(7 downto 0); |     signal s_irq_f2p:       std_logic_vector(7 downto 0); | ||||||
| 
 | 
 | ||||||
|     signal s_reg_control:   registers_control; |     signal s_reg_control:   registers_control; | ||||||
|     signal s_reg_status:    registers_status; |     signal s_reg_status:    registers_status; | ||||||
|     signal s_reg_trigger:   registers_trigger; |  | ||||||
| 
 | 
 | ||||||
|     signal s_dma_write_cmd_addr:  dma_address_array(0 downto 0); |     signal s_dma_write_cmd_addr:  dma_address_array(0 downto 0); | ||||||
|     signal s_dma_write_cmd_length: dma_burst_length_array(0 to 0); |     signal s_dma_write_cmd_length: dma_burst_length_array(0 to 0); | ||||||
|  | @ -144,9 +144,13 @@ architecture arch of puzzlefw_top is | ||||||
|     signal s_dma_write_data_ready: std_logic_vector(0 downto 0); |     signal s_dma_write_data_ready: std_logic_vector(0 downto 0); | ||||||
|     signal s_dma_write_finished:  std_logic_vector(0 downto 0); |     signal s_dma_write_finished:  std_logic_vector(0 downto 0); | ||||||
| 
 | 
 | ||||||
|     signal s_dma_in_valid:  std_logic; |     signal s_timestamp:     std_logic_vector(timestamp_bits - 1 downto 0); | ||||||
|     signal s_dma_in_ready:  std_logic; |     signal s_adc_data:      adc_data_array(0 to 1); | ||||||
|     signal s_dma_in_data:   std_logic_vector(63 downto 0); | 
 | ||||||
|  |     signal s_acq_dma_valid:     std_logic; | ||||||
|  |     signal s_acq_dma_ready:     std_logic; | ||||||
|  |     signal s_acq_dma_empty:     std_logic; | ||||||
|  |     signal s_acq_dma_data:      dma_data_type; | ||||||
| 
 | 
 | ||||||
|     signal r_test_prefetch: std_logic; |     signal r_test_prefetch: std_logic; | ||||||
|     signal r_test_raddr:    std_logic_vector(9 downto 0); |     signal r_test_raddr:    std_logic_vector(9 downto 0); | ||||||
|  | @ -276,8 +280,7 @@ begin | ||||||
|             apb_pslverr         => s_apb_pslverr, |             apb_pslverr         => s_apb_pslverr, | ||||||
|             apb_prdata          => s_apb_prdata, |             apb_prdata          => s_apb_prdata, | ||||||
|             reg_control         => s_reg_control, |             reg_control         => s_reg_control, | ||||||
|             reg_status          => s_reg_status, |             reg_status          => s_reg_status | ||||||
|             reg_trigger         => s_reg_trigger |  | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|     -- AXI master. |     -- AXI master. | ||||||
|  | @ -296,7 +299,7 @@ begin | ||||||
|             err_write           => s_reg_status.dma_err_write, |             err_write           => s_reg_status.dma_err_write, | ||||||
|             err_address         => s_reg_status.dma_err_address, |             err_address         => s_reg_status.dma_err_address, | ||||||
|             err_any             => s_reg_status.dma_err_any, |             err_any             => s_reg_status.dma_err_any, | ||||||
|             clear_errors        => s_reg_trigger.dma_clear, |             clear_errors        => s_reg_control.dma_clear, | ||||||
|             read_cmd_addr       => (others => (others => '0')), |             read_cmd_addr       => (others => (others => '0')), | ||||||
|             read_cmd_length     => (others => (others => '0')), |             read_cmd_length     => (others => (others => '0')), | ||||||
|             read_cmd_valid      => (others => '0'), |             read_cmd_valid      => (others => '0'), | ||||||
|  | @ -361,18 +364,19 @@ begin | ||||||
|             reset               => s_reset, |             reset               => s_reset, | ||||||
|             channel_en          => s_reg_control.acq_channel_en, |             channel_en          => s_reg_control.acq_channel_en, | ||||||
|             channel_busy        => s_reg_status.acq_channel_busy, |             channel_busy        => s_reg_status.acq_channel_busy, | ||||||
|             channel_init        => s_reg_trigger.acq_channel_init, |             channel_init        => s_reg_control.acq_channel_init, | ||||||
|             addr_start          => s_reg_control.acq_addr_start, |             addr_start          => s_reg_control.acq_addr_start, | ||||||
|             addr_end            => s_reg_control.acq_addr_end, |             addr_end            => s_reg_control.acq_addr_end, | ||||||
|             addr_limit          => s_reg_control.acq_addr_limit, |             addr_limit          => s_reg_control.acq_addr_limit, | ||||||
|             addr_interrupt      => s_reg_control.acq_addr_intr, |             addr_interrupt      => s_reg_control.acq_addr_intr, | ||||||
|             addr_pointer        => s_reg_status.acq_addr_ptr, |             addr_pointer        => s_reg_status.acq_addr_ptr, | ||||||
|             intr_en             => s_reg_control.acq_intr_en, |             intr_en             => s_reg_control.acq_intr_en, | ||||||
|             intr_clear          => s_reg_trigger.acq_intr_clear, |             intr_clear          => s_reg_control.acq_intr_clear, | ||||||
|             intr_out            => s_dma_interrupt, |             intr_out            => s_irq_pending(0), | ||||||
|             in_valid            => s_dma_in_valid, |             in_valid            => s_acq_dma_valid, | ||||||
|             in_ready            => s_dma_in_ready, |             in_ready            => s_acq_dma_ready, | ||||||
|             in_data             => s_dma_in_data, |             in_empty            => s_acq_dma_empty, | ||||||
|  |             in_data             => s_acq_dma_data, | ||||||
|             write_cmd_addr      => s_dma_write_cmd_addr(0), |             write_cmd_addr      => s_dma_write_cmd_addr(0), | ||||||
|             write_cmd_length    => s_dma_write_cmd_length(0), |             write_cmd_length    => s_dma_write_cmd_length(0), | ||||||
|             write_cmd_valid     => s_dma_write_cmd_valid(0), |             write_cmd_valid     => s_dma_write_cmd_valid(0), | ||||||
|  | @ -381,39 +385,64 @@ begin | ||||||
|             write_data_ready    => s_dma_write_data_ready(0), |             write_data_ready    => s_dma_write_data_ready(0), | ||||||
|             write_finished      => s_dma_write_finished(0) ); |             write_finished      => s_dma_write_finished(0) ); | ||||||
| 
 | 
 | ||||||
|  |     -- Timestamp generator. | ||||||
|  |     inst_timestamp_gen: entity work.timestamp_gen | ||||||
|  |         port map ( | ||||||
|  |             clk                 => clk_adc, | ||||||
|  |             reset               => s_reset, | ||||||
|  |             clear               => s_reg_control.timestamp_clear, | ||||||
|  |             timestamp           => s_timestamp ); | ||||||
|  | 
 | ||||||
|  |     s_reg_status.timestamp <= s_timestamp; | ||||||
|  | 
 | ||||||
|  |     -- Capture ADC data. | ||||||
|  |     -- Ignore the 2 LSB bits which are not-connected on the ADC side. | ||||||
|  |     inst_adc_capture1: entity work.adc_capture | ||||||
|  |         port map ( | ||||||
|  |             clk                 => clk_adc, | ||||||
|  |             in_data             => adc_dat_i(0)(15 downto 2), | ||||||
|  |             out_data            => s_adc_data(0) ); | ||||||
|  | 
 | ||||||
|  |     inst_adc_capture2: entity work.adc_capture | ||||||
|  |         port map ( | ||||||
|  |             clk                 => clk_adc, | ||||||
|  |             in_data             => adc_dat_i(1)(15 downto 2), | ||||||
|  |             out_data            => s_adc_data(1) ); | ||||||
|  | 
 | ||||||
|  |     -- Analog acquisition data chain. | ||||||
|  |     inst_acquisition_chain: entity work.acquisition_chain | ||||||
|  |         generic map ( | ||||||
|  |             num_channels        => 2 ) | ||||||
|  |         port map ( | ||||||
|  |             clk                 => clk_adc, | ||||||
|  |             reset               => s_reset, | ||||||
|  |             acquisition_en      => s_reg_control.acquisition_en, | ||||||
|  |             trigger_delay       => s_reg_control.trigger_delay, | ||||||
|  |             record_length       => s_reg_control.record_length, | ||||||
|  |             decimation_factor   => s_reg_control.decimation_factor, | ||||||
|  |             averaging           => s_reg_control.averaging_en, | ||||||
|  |             shift_steps         => s_reg_control.shift_steps, | ||||||
|  |             ch4_mode            => s_reg_control.ch4_mode, | ||||||
|  |             simulate_adc        => s_reg_control.simulate_adc, | ||||||
|  |             trig_auto_en        => s_reg_control.trig_auto_en, | ||||||
|  |             trig_ext_en         => s_reg_control.trig_ext_en, | ||||||
|  |             trig_force          => s_reg_control.trig_force, | ||||||
|  |             trig_ext_select     => s_reg_control.trig_ext_select, | ||||||
|  |             trig_ext_falling    => s_reg_control.trig_ext_falling, | ||||||
|  |             timestamp_in        => s_timestamp, | ||||||
|  |             adc_data_in         => s_adc_data, | ||||||
|  |             trig_ext_in         => "0000",  -- TODO | ||||||
|  |             trig_waiting        => s_reg_status.trig_waiting, | ||||||
|  |             out_valid           => s_acq_dma_valid, | ||||||
|  |             out_ready           => s_acq_dma_ready, | ||||||
|  |             out_empty           => s_acq_dma_empty, | ||||||
|  |             out_data            => s_acq_dma_data ); | ||||||
|  | 
 | ||||||
|     -- Collect interrupt signals from peripherals and generate interrupt to PS. |     -- Collect interrupt signals from peripherals and generate interrupt to PS. | ||||||
|     s_irq_pending(0) <= s_dma_interrupt; |  | ||||||
|     s_reg_status.irq_pending <= s_irq_pending; |     s_reg_status.irq_pending <= s_irq_pending; | ||||||
|     s_irq_f2p(0) <= s_reg_control.irq_enable and or_reduce(s_irq_pending); |     s_irq_f2p(0) <= s_reg_control.irq_enable and or_reduce(s_irq_pending); | ||||||
|     s_irq_f2p(7 downto 1) <= (others => '0'); |     s_irq_f2p(7 downto 1) <= (others => '0'); | ||||||
| 
 | 
 | ||||||
| -- little test machine for DMA |  | ||||||
|     process (clk_adc) is |  | ||||||
|     begin |  | ||||||
|         if rising_edge(clk_adc) then |  | ||||||
|             if s_reg_control.test_divider = x"ffff" then |  | ||||||
|                 s_dma_in_valid <= '1'; |  | ||||||
|                 if s_dma_in_ready = '1' then |  | ||||||
|                     s_dma_in_data(47 downto 0) <= std_logic_vector(unsigned(s_dma_in_data(47 downto 0)) + 1); |  | ||||||
|                     s_dma_in_data(63 downto 48) <= (others => '0'); |  | ||||||
|                 end if; |  | ||||||
|             else |  | ||||||
|                 if s_dma_in_ready = '1' then |  | ||||||
|                     s_dma_in_valid <= '0'; |  | ||||||
|                 end if; |  | ||||||
|                 if r_test_cnt >= unsigned(s_reg_control.test_divider) then |  | ||||||
|                     r_test_cnt <= (others => '0'); |  | ||||||
|                     s_dma_in_valid <= '1'; |  | ||||||
|                     s_dma_in_data(47 downto 0) <= std_logic_vector(unsigned(s_dma_in_data(47 downto 0)) + 1); |  | ||||||
|                     s_dma_in_data(63 downto 48) <= (others => '0'); |  | ||||||
|                 else |  | ||||||
|                     r_test_cnt <= r_test_cnt + 1; |  | ||||||
|                 end if; |  | ||||||
|             end if; |  | ||||||
|         end if; |  | ||||||
|     end process; |  | ||||||
| -- END test |  | ||||||
| 
 |  | ||||||
|     process (clk_fclk) is |     process (clk_fclk) is | ||||||
|     begin |     begin | ||||||
|         if rising_edge(clk_fclk) then |         if rising_edge(clk_fclk) then | ||||||
|  |  | ||||||
|  | @ -32,8 +32,7 @@ entity registers is | ||||||
| 
 | 
 | ||||||
|         -- FPGA interface. |         -- FPGA interface. | ||||||
|         reg_control:        out registers_control; |         reg_control:        out registers_control; | ||||||
|         reg_status:         in  registers_status; |         reg_status:         in  registers_status | ||||||
|         reg_trigger:        out registers_trigger |  | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| end entity; | end entity; | ||||||
|  | @ -44,14 +43,12 @@ architecture arch of registers is | ||||||
|         pready:         std_logic; |         pready:         std_logic; | ||||||
|         prdata:         std_logic_vector(31 downto 0); |         prdata:         std_logic_vector(31 downto 0); | ||||||
|         reg_control:    registers_control; |         reg_control:    registers_control; | ||||||
|         reg_trigger:    registers_trigger; |  | ||||||
|     end record; |     end record; | ||||||
| 
 | 
 | ||||||
|     constant regs_init: regs_type := ( |     constant regs_init: regs_type := ( | ||||||
|         pready          => '0', |         pready          => '0', | ||||||
|         prdata          => (others => '0'), |         prdata          => (others => '0'), | ||||||
|         reg_control     => registers_control_init, |         reg_control     => registers_control_init | ||||||
|         reg_trigger     => registers_trigger_init |  | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     signal r: regs_type := regs_init; |     signal r: regs_type := regs_init; | ||||||
|  | @ -63,9 +60,7 @@ begin | ||||||
|     apb_pready      <= r.pready; |     apb_pready      <= r.pready; | ||||||
|     apb_pslverr     <= '0';  -- never signal errors |     apb_pslverr     <= '0';  -- never signal errors | ||||||
|     apb_prdata      <= r.prdata; |     apb_prdata      <= r.prdata; | ||||||
| 
 |  | ||||||
|     reg_control     <= r.reg_control; |     reg_control     <= r.reg_control; | ||||||
|     reg_trigger     <= r.reg_trigger; |  | ||||||
| 
 | 
 | ||||||
|     -- Combinatorial process. |     -- Combinatorial process. | ||||||
|     process (all) is |     process (all) is | ||||||
|  | @ -75,7 +70,11 @@ begin | ||||||
|         v := r; |         v := r; | ||||||
| 
 | 
 | ||||||
|         -- Clear single-cycle trigger pulses. |         -- Clear single-cycle trigger pulses. | ||||||
|         v.reg_trigger   := registers_trigger_init; |         v.reg_control.dma_clear         := '0'; | ||||||
|  |         v.reg_control.timestamp_clear   := '0'; | ||||||
|  |         v.reg_control.acq_channel_init  := '0'; | ||||||
|  |         v.reg_control.acq_intr_clear    := '0'; | ||||||
|  |         v.reg_control.trig_force        := '0'; | ||||||
| 
 | 
 | ||||||
|         -- Respond to each APB access on the next clock cycle (no wait states). |         -- Respond to each APB access on the next clock cycle (no wait states). | ||||||
|         v.pready        := apb_psel and (not apb_penable); |         v.pready        := apb_psel and (not apb_penable); | ||||||
|  | @ -98,6 +97,8 @@ begin | ||||||
|                     v.prdata(2) := reg_status.dma_err_write; |                     v.prdata(2) := reg_status.dma_err_write; | ||||||
|                     v.prdata(3) := reg_status.dma_err_address; |                     v.prdata(3) := reg_status.dma_err_address; | ||||||
|                     v.prdata(4) := reg_status.dma_err_any; |                     v.prdata(4) := reg_status.dma_err_any; | ||||||
|  |                 when reg_timestamp_lo =>    v.prdata := reg_status.timestamp(31 downto 0); | ||||||
|  |                 when reg_timestamp_hi =>    v.prdata(15 downto 0) := reg_status.timestamp(47 downto 32); | ||||||
|                 when reg_acq_addr_start =>  v.prdata(31 downto 7) := r.reg_control.acq_addr_start; |                 when reg_acq_addr_start =>  v.prdata(31 downto 7) := r.reg_control.acq_addr_start; | ||||||
|                 when reg_acq_addr_end =>    v.prdata(31 downto 7) := r.reg_control.acq_addr_end; |                 when reg_acq_addr_end =>    v.prdata(31 downto 7) := r.reg_control.acq_addr_end; | ||||||
|                 when reg_acq_addr_limit =>  v.prdata(31 downto 7) := r.reg_control.acq_addr_limit; |                 when reg_acq_addr_limit =>  v.prdata(31 downto 7) := r.reg_control.acq_addr_limit; | ||||||
|  | @ -107,6 +108,20 @@ begin | ||||||
|                     v.prdata(0) := r.reg_control.acq_channel_en; |                     v.prdata(0) := r.reg_control.acq_channel_en; | ||||||
|                     v.prdata(8) := reg_status.acq_channel_busy; |                     v.prdata(8) := reg_status.acq_channel_busy; | ||||||
|                 when reg_acq_intr_ctrl =>   v.prdata(0) := r.reg_control.acq_intr_en; |                 when reg_acq_intr_ctrl =>   v.prdata(0) := r.reg_control.acq_intr_en; | ||||||
|  |                 when reg_acquisition_en =>  v.prdata(0) := r.reg_control.acquisition_en; | ||||||
|  |                 when reg_record_length =>   v.prdata(15 downto 0) := r.reg_control.record_length; | ||||||
|  |                 when reg_decimation_factor => v.prdata(17 downto 0) := r.reg_control.decimation_factor; | ||||||
|  |                 when reg_shift_steps =>     v.prdata(3 downto 0) := r.reg_control.shift_steps; | ||||||
|  |                 when reg_averaging_en =>    v.prdata(0) := r.reg_control.averaging_en; | ||||||
|  |                 when reg_ch4_mode =>        v.prdata(0) := r.reg_control.ch4_mode; | ||||||
|  |                 when reg_simulate_adc =>    v.prdata(0) := r.reg_control.simulate_adc; | ||||||
|  |                 when reg_trigger_mode => | ||||||
|  |                     v.prdata(0) := r.reg_control.trig_auto_en; | ||||||
|  |                     v.prdata(1) := r.reg_control.trig_ext_en; | ||||||
|  |                     v.prdata(5 downto 4) := r.reg_control.trig_ext_select; | ||||||
|  |                     v.prdata(7) := r.reg_control.trig_ext_falling; | ||||||
|  |                 when reg_trigger_delay =>   v.prdata(15 downto 0) := r.reg_control.trigger_delay; | ||||||
|  |                 when reg_trigger_status =>  v.prdata(0) := reg_status.trig_waiting; | ||||||
|                 when reg_test_led =>        v.prdata(7 downto 0) := r.reg_control.test_led; |                 when reg_test_led =>        v.prdata(7 downto 0) := r.reg_control.test_led; | ||||||
|                 when reg_test_divider =>    v.prdata(15 downto 0) := r.reg_control.test_divider; |                 when reg_test_divider =>    v.prdata(15 downto 0) := r.reg_control.test_divider; | ||||||
|                 when reg_dma_buf_addr =>    v.prdata(31 downto 12) := r.reg_control.dma_buf_addr; |                 when reg_dma_buf_addr =>    v.prdata(31 downto 12) := r.reg_control.dma_buf_addr; | ||||||
|  | @ -121,17 +136,32 @@ begin | ||||||
|             case to_integer(unsigned(apb_paddr and reg_addr_mask)) is |             case to_integer(unsigned(apb_paddr and reg_addr_mask)) is | ||||||
|                 when reg_irq_enable =>      v.reg_control.irq_enable := apb_pwdata(0); |                 when reg_irq_enable =>      v.reg_control.irq_enable := apb_pwdata(0); | ||||||
|                 when reg_dma_en =>          v.reg_control.dma_en := apb_pwdata(0); |                 when reg_dma_en =>          v.reg_control.dma_en := apb_pwdata(0); | ||||||
|                 when reg_dma_clear =>       v.reg_trigger.dma_clear := apb_pwdata(0); |                 when reg_dma_clear =>       v.reg_control.dma_clear := apb_pwdata(0); | ||||||
|  |                 when reg_timestamp_clear => v.reg_control.timestamp_clear := apb_pwdata(0); | ||||||
|                 when reg_acq_addr_start =>  v.reg_control.acq_addr_start := apb_pwdata(31 downto 7); |                 when reg_acq_addr_start =>  v.reg_control.acq_addr_start := apb_pwdata(31 downto 7); | ||||||
|                 when reg_acq_addr_end =>    v.reg_control.acq_addr_end := apb_pwdata(31 downto 7); |                 when reg_acq_addr_end =>    v.reg_control.acq_addr_end := apb_pwdata(31 downto 7); | ||||||
|                 when reg_acq_addr_limit =>  v.reg_control.acq_addr_limit := apb_pwdata(31 downto 7); |                 when reg_acq_addr_limit =>  v.reg_control.acq_addr_limit := apb_pwdata(31 downto 7); | ||||||
|                 when reg_acq_addr_intr =>   v.reg_control.acq_addr_intr := apb_pwdata(31 downto 3); |                 when reg_acq_addr_intr =>   v.reg_control.acq_addr_intr := apb_pwdata(31 downto 3); | ||||||
|                 when reg_acq_channel_ctrl => |                 when reg_acq_channel_ctrl => | ||||||
|                     v.reg_control.acq_channel_en := apb_pwdata(0); |                     v.reg_control.acq_channel_en := apb_pwdata(0); | ||||||
|                     v.reg_trigger.acq_channel_init := apb_pwdata(1); |                     v.reg_control.acq_channel_init := apb_pwdata(1); | ||||||
|                 when reg_acq_intr_ctrl => |                 when reg_acq_intr_ctrl => | ||||||
|                     v.reg_control.acq_intr_en := apb_pwdata(0); |                     v.reg_control.acq_intr_en := apb_pwdata(0); | ||||||
|                     v.reg_trigger.acq_intr_clear := apb_pwdata(1); |                     v.reg_control.acq_intr_clear := apb_pwdata(1); | ||||||
|  |                 when reg_acquisition_en =>  v.reg_control.acquisition_en := apb_pwdata(0); | ||||||
|  |                 when reg_record_length =>   v.reg_control.record_length := apb_pwdata(15 downto 0); | ||||||
|  |                 when reg_decimation_factor => v.reg_control.decimation_factor := apb_pwdata(17 downto 0); | ||||||
|  |                 when reg_shift_steps =>     v.reg_control.shift_steps := apb_pwdata(3 downto 0); | ||||||
|  |                 when reg_averaging_en =>    v.reg_control.averaging_en := apb_pwdata(0); | ||||||
|  |                 when reg_ch4_mode =>        v.reg_control.ch4_mode := apb_pwdata(0); | ||||||
|  |                 when reg_simulate_adc =>    v.reg_control.simulate_adc := apb_pwdata(0); | ||||||
|  |                 when reg_trigger_mode => | ||||||
|  |                     v.reg_control.trig_auto_en := apb_pwdata(0); | ||||||
|  |                     v.reg_control.trig_ext_en := apb_pwdata(1); | ||||||
|  |                     v.reg_control.trig_ext_select := apb_pwdata(5 downto 4); | ||||||
|  |                     v.reg_control.trig_ext_falling := apb_pwdata(7); | ||||||
|  |                     v.reg_control.trig_force := apb_pwdata(8); | ||||||
|  |                 when reg_trigger_delay =>   v.reg_control.trigger_delay := apb_pwdata(15 downto 0); | ||||||
|                 when reg_test_led =>        v.reg_control.test_led := apb_pwdata(7 downto 0); |                 when reg_test_led =>        v.reg_control.test_led := apb_pwdata(7 downto 0); | ||||||
|                 when reg_test_divider =>    v.reg_control.test_divider := apb_pwdata(15 downto 0); |                 when reg_test_divider =>    v.reg_control.test_divider := apb_pwdata(15 downto 0); | ||||||
|                 when reg_dma_buf_addr =>    v.reg_control.dma_buf_addr := apb_pwdata(31 downto 12); |                 when reg_dma_buf_addr =>    v.reg_control.dma_buf_addr := apb_pwdata(31 downto 12); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,124 @@ | ||||||
|  | -- | ||||||
|  | --  Decimation or averaging of ADC samples. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity sample_decimation is | ||||||
|  | 
 | ||||||
|  |     generic ( | ||||||
|  |         -- Input word length. | ||||||
|  |         -- This is also the word length of the internal accumulator. | ||||||
|  |         input_data_bits:    integer range 4 to 64; | ||||||
|  | 
 | ||||||
|  |         -- Output word length. | ||||||
|  |         -- If the output has fewer bits than the input, it represents the | ||||||
|  |         -- most significant bits of the accumulator, rounded to the | ||||||
|  |         -- nearest integer. | ||||||
|  |         output_data_bits:   integer range 4 to 64 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Reset, active high, synchronous to main clock. | ||||||
|  |         reset:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to initialize the accumulator to the input word plus rounding bias. | ||||||
|  |         start:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to add the input word to the accumulator. | ||||||
|  |         integrate:          in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Input data word. | ||||||
|  |         in_data:            in  std_logic_vector(input_data_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Output data word. | ||||||
|  |         -- Results from input signals appear on the output after 1 clock cycle. | ||||||
|  |         out_data:           out std_logic_vector(output_data_bits - 1 downto 0) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of sample_decimation is | ||||||
|  | 
 | ||||||
|  |     -- Return the rounding bias to add to the accumulator before truncating | ||||||
|  |     -- the least significant bits. | ||||||
|  |     function rounding_bias return unsigned | ||||||
|  |     is begin | ||||||
|  |         if output_data_bits < input_data_bits then | ||||||
|  |             -- Set the most significant truncated bit to '1'. | ||||||
|  |             return shift_left(to_unsigned(1, input_data_bits), | ||||||
|  |                               input_data_bits - output_data_bits - 1); | ||||||
|  |         else | ||||||
|  |             -- No truncation. | ||||||
|  |             return to_unsigned(0, input_data_bits); | ||||||
|  |         end if; | ||||||
|  |     end function; | ||||||
|  | 
 | ||||||
|  |     type regs_type is record | ||||||
|  |         accumulator:        std_logic_vector(input_data_bits - 1 downto 0); | ||||||
|  |     end record; | ||||||
|  | 
 | ||||||
|  |     constant regs_init: regs_type := ( | ||||||
|  |         accumulator     => (others => '0') | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     signal r: regs_type := regs_init; | ||||||
|  |     signal rnext: regs_type; | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output from accumulator. | ||||||
|  |     out_data <= r.accumulator(input_data_bits - 1 downto input_data_bits - output_data_bits) | ||||||
|  |         when (output_data_bits <= input_data_bits) | ||||||
|  |         else r.accumulator & std_logic_vector(to_unsigned(0, output_data_bits - input_data_bits)); | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Combinatorial process. | ||||||
|  |     -- | ||||||
|  |     process (all) is | ||||||
|  |         variable v: regs_type; | ||||||
|  |     begin | ||||||
|  |         -- Load current register values. | ||||||
|  |         v := r; | ||||||
|  | 
 | ||||||
|  |         if start = '1' then | ||||||
|  | 
 | ||||||
|  |             -- Initialize accumulator and add input word. | ||||||
|  |             v.accumulator := std_logic_vector(rounding_bias + unsigned(in_data)); | ||||||
|  | 
 | ||||||
|  |         elsif integrate = '1' then | ||||||
|  | 
 | ||||||
|  |             -- Add input word to the accumulator. | ||||||
|  |             v.accumulator := std_logic_vector(unsigned(r.accumulator) + unsigned(in_data)); | ||||||
|  | 
 | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Synchronous reset. | ||||||
|  |         if reset = '1' then | ||||||
|  |             v := regs_init; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Drive new register values to synchronous process. | ||||||
|  |         rnext <= v; | ||||||
|  |      | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  |             r <= rnext; | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -0,0 +1,90 @@ | ||||||
|  | -- | ||||||
|  | --  Signed or unsigned variable right-shift. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity shift_engine is | ||||||
|  | 
 | ||||||
|  |     generic ( | ||||||
|  |         -- Input word length. | ||||||
|  |         input_data_bits:    integer range 4 to 64; | ||||||
|  | 
 | ||||||
|  |         -- Output word length. | ||||||
|  |         output_data_bits:   integer range 4 to 64; | ||||||
|  | 
 | ||||||
|  |         -- Number of bit positions to pre-shift left before shifting right. | ||||||
|  |         pre_shift_left:     integer range 0 to 16; | ||||||
|  | 
 | ||||||
|  |         -- True to apply sign extension when shifting. | ||||||
|  |         -- False to apply zero extension. | ||||||
|  |         signed_data:        boolean | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Data input operand. | ||||||
|  |         -- A new input word is accepted on every clock cycle. | ||||||
|  |         in_data:            in  std_logic_vector(input_data_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Shift input operand. | ||||||
|  |         -- It indicates the number of bit positions to shift right, expressed | ||||||
|  |         -- as an unsigned integer in range 0 to 15. | ||||||
|  |         -- A new input word is accepted on every clock cycle. | ||||||
|  |         in_shift:           in  std_logic_vector(3 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- Shifted output data. | ||||||
|  |         -- The output corresponds to the input delayed by 1 clock cycle. | ||||||
|  |         out_data:           out std_logic_vector(output_data_bits - 1 downto 0) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of shift_engine is | ||||||
|  | 
 | ||||||
|  |     -- Output register. | ||||||
|  |     signal r_data:      std_logic_vector(output_data_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output. | ||||||
|  |     out_data <= r_data; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  | 
 | ||||||
|  |             if signed_data then | ||||||
|  | 
 | ||||||
|  |                 r_data <= std_logic_vector( | ||||||
|  |                     shift_right( | ||||||
|  |                         shift_left( | ||||||
|  |                             resize(signed(in_data), output_data_bits), | ||||||
|  |                             pre_shift_left), | ||||||
|  |                         to_integer(unsigned(in_shift)))); | ||||||
|  | 
 | ||||||
|  |             else | ||||||
|  | 
 | ||||||
|  |                 r_data <= std_logic_vector( | ||||||
|  |                     shift_right( | ||||||
|  |                         shift_left( | ||||||
|  |                             resize(unsigned(in_data), output_data_bits), | ||||||
|  |                             pre_shift_left), | ||||||
|  |                         to_integer(unsigned(in_shift)))); | ||||||
|  | 
 | ||||||
|  |             end if; | ||||||
|  | 
 | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -0,0 +1,57 @@ | ||||||
|  | -- | ||||||
|  | --  Generate timestamps. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | use work.puzzlefw_pkg.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity timestamp_gen is | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Reset, active high, synchronous to main clock. | ||||||
|  |         reset:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to reset the timestamp counter to 0. | ||||||
|  |         clear:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Timestamp. | ||||||
|  |         timestamp:          out std_logic_vector(timestamp_bits - 1 downto 0) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of timestamp_gen is | ||||||
|  | 
 | ||||||
|  |     signal r_counter: unsigned(timestamp_bits - 1 downto 0); | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output. | ||||||
|  |     timestamp <= std_logic_vector(r_counter); | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  | 
 | ||||||
|  |             if (reset = '1') or (clear = '1') then | ||||||
|  |                 r_counter <= (others => '0'); | ||||||
|  |             else | ||||||
|  |                 r_counter <= r_counter + 1; | ||||||
|  |             end if; | ||||||
|  | 
 | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -0,0 +1,112 @@ | ||||||
|  | -- | ||||||
|  | --  Detect external triggers. | ||||||
|  | -- | ||||||
|  | --  Joris van Rantwijk 2024 | ||||||
|  | -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | use work.puzzlefw_pkg.all; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | entity trigger_detector is | ||||||
|  | 
 | ||||||
|  |     port ( | ||||||
|  |         -- Main clock, active on rising edge. | ||||||
|  |         clk:                in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Reset, active high, synchronous to main clock. | ||||||
|  |         reset:              in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to enable automatic (continuous) triggering. | ||||||
|  |         trig_auto_en:       in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to enable external triggering. | ||||||
|  |         trig_ext_en:        in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- High to force a trigger event (if the acquisition chain is ready). | ||||||
|  |         trig_force:         in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Select external input signal to use as trigger. | ||||||
|  |         trig_select:        in  std_logic_vector(1 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High to trigger on falling edge, low to trigger on rising edge. | ||||||
|  |         trig_falling:       in  std_logic; | ||||||
|  | 
 | ||||||
|  |         -- Digital input signals. | ||||||
|  |         trig_ext_in:        in  std_logic_vector(3 downto 0); | ||||||
|  | 
 | ||||||
|  |         -- High in the clock cycle following the occurrence of a trigger. | ||||||
|  |         trig_out:           out std_logic | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | end entity; | ||||||
|  | 
 | ||||||
|  | architecture arch of trigger_detector is | ||||||
|  | 
 | ||||||
|  |     type regs_type is record | ||||||
|  |         prev_level:         std_logic; | ||||||
|  |         ext_trig:           std_logic; | ||||||
|  |         trig_out:           std_logic; | ||||||
|  |     end record; | ||||||
|  | 
 | ||||||
|  |     constant regs_init: regs_type := ( | ||||||
|  |         prev_level  => '0', | ||||||
|  |         ext_trig        => '0', | ||||||
|  |         trig_out        => '0' | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     signal r: regs_type := regs_init; | ||||||
|  |     signal rnext: regs_type; | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 
 | ||||||
|  |     -- Drive output. | ||||||
|  |     trig_out <= r.trig_out; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Combinatorial process. | ||||||
|  |     -- | ||||||
|  |     process (all) is | ||||||
|  |         variable v: regs_type; | ||||||
|  |         variable v_trig: std_logic; | ||||||
|  |     begin | ||||||
|  |         -- Load current register values. | ||||||
|  |         v := r; | ||||||
|  | 
 | ||||||
|  |         -- Select external input signal. | ||||||
|  |         v.prev_level := trig_ext_in(to_integer(unsigned(trig_select))); | ||||||
|  | 
 | ||||||
|  |         -- Detect active edge. | ||||||
|  |         v.ext_trig := (not (r.prev_level xor trig_falling)) and | ||||||
|  |                       (v.prev_level xor trig_falling); | ||||||
|  | 
 | ||||||
|  |         -- Combine trigger sources. | ||||||
|  |         v.trig_out := trig_auto_en or | ||||||
|  |                       (trig_ext_en and r.ext_trig) or | ||||||
|  |                       trig_force; | ||||||
|  | 
 | ||||||
|  |         -- Synchronous reset. | ||||||
|  |         if reset = '1' then | ||||||
|  |             v.ext_trig := '0'; | ||||||
|  |             v.trig_out := '0'; | ||||||
|  |         end if; | ||||||
|  | 
 | ||||||
|  |         -- Drive new register values to synchronous process. | ||||||
|  |         rnext <= v; | ||||||
|  | 
 | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  |     -- | ||||||
|  |     -- Synchronous process. | ||||||
|  |     -- | ||||||
|  |     process (clk) is | ||||||
|  |     begin | ||||||
|  |         if rising_edge(clk) then | ||||||
|  |             r <= rnext; | ||||||
|  |         end if; | ||||||
|  |     end process; | ||||||
|  | 
 | ||||||
|  | end architecture; | ||||||
|  | @ -19,10 +19,19 @@ set_property target_language VHDL [current_project] | ||||||
| 
 | 
 | ||||||
| # Load VHDL files. | # Load VHDL files. | ||||||
| read_vhdl -vhdl2008 ../rtl/puzzlefw_pkg.vhd | read_vhdl -vhdl2008 ../rtl/puzzlefw_pkg.vhd | ||||||
| read_vhdl -vhdl2008 ../rtl/simple_fifo.vhd | read_vhdl -vhdl2008 ../rtl/acquisition_chain.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/acquisition_manager.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/acquisition_stream.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/adc_capture.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/adc_sample_stream.vhd | ||||||
| read_vhdl -vhdl2008 ../rtl/dma_axi_master.vhd | read_vhdl -vhdl2008 ../rtl/dma_axi_master.vhd | ||||||
| read_vhdl -vhdl2008 ../rtl/dma_write_channel.vhd | read_vhdl -vhdl2008 ../rtl/dma_write_channel.vhd | ||||||
| read_vhdl -vhdl2008 ../rtl/registers.vhd | read_vhdl -vhdl2008 ../rtl/registers.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/sample_decimation.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/shift_engine.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/simple_fifo.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/timestamp_gen.vhd | ||||||
|  | read_vhdl -vhdl2008 ../rtl/trigger_detector.vhd | ||||||
| read_vhdl -vhdl2008 ../rtl/puzzlefw_top.vhd | read_vhdl -vhdl2008 ../rtl/puzzlefw_top.vhd | ||||||
| 
 | 
 | ||||||
| # Load Zynq block design. | # Load Zynq block design. | ||||||
|  |  | ||||||
|  | @ -31,14 +31,26 @@ | ||||||
| #define REG_DMA_EN              0x0100 | #define REG_DMA_EN              0x0100 | ||||||
| #define REG_DMA_STATUS          0x0104 | #define REG_DMA_STATUS          0x0104 | ||||||
| #define REG_DMA_CLEAR           0x0108 | #define REG_DMA_CLEAR           0x0108 | ||||||
| #define REG_DMA_ADDR_START      0x0200 | #define REG_TIMESTAMP_LO        0x0180 | ||||||
| #define REG_DMA_ADDR_END        0x0204 | #define REG_TIMESTAMP_HI        0x0184 | ||||||
| #define REG_DMA_ADDR_LIMIT      0x0208 | #define REG_TIMESTAMP_CLEAR     0x0188 | ||||||
| #define REG_DMA_ADDR_INTR       0x020c | #define REG_ACQ_ADDR_START      0x0200 | ||||||
| #define REG_DMA_ADDR_PTR        0x0210 | #define REG_ACQ_ADDR_END        0x0204 | ||||||
| #define REG_DMA_CHANNEL_CTRL    0x0214 | #define REG_ACQ_ADDR_LIMIT      0x0208 | ||||||
| #define REG_DMA_INTR_CTRL       0x0218 | #define REG_ACQ_ADDR_INTR       0x020c | ||||||
| #define REG_TEST_DIVIDER        0x0408 | #define REG_ACQ_ADDR_PTR        0x0210 | ||||||
|  | #define REG_ACQ_CHANNEL_CTRL    0x0214 | ||||||
|  | #define REG_ACQ_INTR_CTRL       0x0218 | ||||||
|  | #define REG_ACQUISITION_EN      0x0220 | ||||||
|  | #define REG_RECORD_LENGTH       0x0224 | ||||||
|  | #define REG_DECIMATION_FACTOR   0x0228 | ||||||
|  | #define REG_SHIFT_STEPS         0x022c | ||||||
|  | #define REG_AVERAGING_EN        0x0230 | ||||||
|  | #define REG_CH4_MODE            0x0234 | ||||||
|  | #define REG_SIMULATE_ADC        0x0238 | ||||||
|  | #define REG_TRIGGER_MODE        0x0240 | ||||||
|  | #define REG_TRIGGER_DELAY       0x0244 | ||||||
|  | #define REG_TRIGGER_STATUS      0x0248 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| struct puzzlefw_context { | struct puzzlefw_context { | ||||||
|  | @ -51,6 +63,12 @@ struct puzzlefw_context { | ||||||
|     char                uio_path[128]; |     char                uio_path[128]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | enum puzzlefw_trigger_mode { | ||||||
|  |     TRIG_NONE = 0, | ||||||
|  |     TRIG_AUTO = 1, | ||||||
|  |     TRIG_EXTERNAL = 2 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Find PuzzleFW device in /sys filesystem. |  * Find PuzzleFW device in /sys filesystem. | ||||||
|  | @ -395,6 +413,123 @@ void puzzlefw_copy_from_dma(void *dst, volatile void *src, size_t len) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Return the current value of the timestamp counter. | ||||||
|  |  */ | ||||||
|  | uint64_t puzzlefw_get_timestamp(struct puzzlefw_context *ctx) | ||||||
|  | { | ||||||
|  |     uint32_t vhi0, vlo, vhi; | ||||||
|  |     vhi0 = puzzlefw_read_reg(ctx, REG_TIMESTAMP_HI); | ||||||
|  |     vlo = puzzlefw_read_reg(ctx, REG_TIMESTAMP_LO); | ||||||
|  |     vhi = puzzlefw_read_reg(ctx, REG_TIMESTAMP_HI); | ||||||
|  |     if (vhi != vhi0) { | ||||||
|  |         vlo = puzzlefw_read_reg(ctx, REG_TIMESTAMP_LO); | ||||||
|  |     } | ||||||
|  |     return (((uint64_t)vhi) << 32) | vlo; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Clear the timestamp counter. | ||||||
|  |  */ | ||||||
|  | void puzzlefw_clear_timestamp(struct puzzlefw_context *ctx) | ||||||
|  | { | ||||||
|  |     puzzlefw_write_reg(ctx, REG_TIMESTAMP_CLEAR, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Return the current trigger mode. | ||||||
|  |  */ | ||||||
|  | enum puzzlefw_trigger_mode puzzlefw_get_trigger_mode( | ||||||
|  |     struct puzzlefw_context *ctx) | ||||||
|  | { | ||||||
|  |     uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); | ||||||
|  |     if ((v & 0x01) != 0) { | ||||||
|  |         return TRIG_AUTO; | ||||||
|  |     } | ||||||
|  |     if ((v & 0x02) != 0) { | ||||||
|  |         return TRIG_EXTERNAL; | ||||||
|  |     } | ||||||
|  |     return TRIG_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Set trigger mode. | ||||||
|  |  */ | ||||||
|  | void puzzlefw_set_trigger_mode( | ||||||
|  |     struct puzzlefw_context *ctx, | ||||||
|  |     enum puzzlefw_trigger_mode mode) | ||||||
|  | { | ||||||
|  |     uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); | ||||||
|  |     v &= 0xfc; | ||||||
|  |     if (mode == TRIG_AUTO) { | ||||||
|  |         v |= 0x01; | ||||||
|  |     } | ||||||
|  |     if (mode == TRIG_EXTERNAL) { | ||||||
|  |         v |= 0x02; | ||||||
|  |     } | ||||||
|  |     puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Return selected external trigger channel. | ||||||
|  |  */ | ||||||
|  | int puzzlefw_get_trigger_ext_channel(struct puzzlefw_context *ctx) | ||||||
|  | { | ||||||
|  |     uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); | ||||||
|  |     return ((v >> 4) & 7); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Select external trigger channel. | ||||||
|  |  */ | ||||||
|  | void puzzlefw_set_trigger_ext_channel(struct puzzlefw_context *ctx, int channel) | ||||||
|  | { | ||||||
|  |     uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); | ||||||
|  |     v &= 0x8f; | ||||||
|  |     v |= (channel << 4); | ||||||
|  |     puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Return external trigger polarity. | ||||||
|  |  */ | ||||||
|  | int puzzlefw_get_trigger_ext_falling(struct puzzlefw_context *ctx) | ||||||
|  | { | ||||||
|  |     uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); | ||||||
|  |     return ((v & 0x80) != 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Select external trigger polarity. | ||||||
|  |  */ | ||||||
|  | void puzzlefw_set_trigger_ext_falling(struct puzzlefw_context *ctx, int falling) | ||||||
|  | { | ||||||
|  |     uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); | ||||||
|  |     v &= 0x7f; | ||||||
|  |     if (falling) { | ||||||
|  |         v |= 0x80; | ||||||
|  |     } | ||||||
|  |     puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Force a single trigger event. | ||||||
|  |  */ | ||||||
|  | void puzzlefw_trigger_force(struct puzzlefw_context *ctx) | ||||||
|  | { | ||||||
|  |     uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); | ||||||
|  |     puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v | 0x100); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| static void show_status(struct puzzlefw_context *ctx) | static void show_status(struct puzzlefw_context *ctx) | ||||||
| { | { | ||||||
|     uint32_t v; |     uint32_t v; | ||||||
|  | @ -415,29 +550,57 @@ static void show_status(struct puzzlefw_context *ctx) | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_STATUS); |     v = puzzlefw_read_reg(ctx, REG_DMA_STATUS); | ||||||
|     printf("    dma_status =     0x%08x\n", v); |     printf("    dma_status =     0x%08x\n", v); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_START); |     uint64_t ts = puzzlefw_get_timestamp(ctx); | ||||||
|     printf("    dma_addr_start = 0x%08x\n", v); |     printf("    timestamp =      %llu\n", (unsigned long long)ts); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_END); |     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_START); | ||||||
|     printf("    dma_addr_end =   0x%08x\n", v); |     printf("    acq_addr_start = 0x%08x\n", v); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_LIMIT); |     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_END); | ||||||
|     printf("    dma_addr_limit = 0x%08x\n", v); |     printf("    acq_addr_end =   0x%08x\n", v); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_INTR); |     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_LIMIT); | ||||||
|     printf("    dma_addr_intr =  0x%08x\n", v); |     printf("    acq_addr_limit = 0x%08x\n", v); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR); |     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_INTR); | ||||||
|     printf("    dma_addr_ptr =   0x%08x\n", v); |     printf("    acq_addr_intr =  0x%08x\n", v); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_CHANNEL_CTRL); |     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_PTR); | ||||||
|     printf("    dma_channel_ctrl = 0x%08x\n", v); |     printf("    acq_addr_ptr =   0x%08x\n", v); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_INTR_CTRL); |     v = puzzlefw_read_reg(ctx, REG_ACQ_CHANNEL_CTRL); | ||||||
|     printf("    dma_intr_ctrl =  0x%08x\n", v); |     printf("    acq_channel_ctrl = 0x%08x\n", v); | ||||||
| 
 | 
 | ||||||
|     v = puzzlefw_read_reg(ctx, REG_TEST_DIVIDER); |     v = puzzlefw_read_reg(ctx, REG_ACQ_INTR_CTRL); | ||||||
|     printf("    test_divider  =  0x%08x\n", v); |     printf("    acq_intr_ctrl =  0x%08x\n", v); | ||||||
|  | 
 | ||||||
|  |     v = puzzlefw_read_reg(ctx, REG_SIMULATE_ADC); | ||||||
|  |     printf("    simulate_adc =   0x%08x\n", v); | ||||||
|  | 
 | ||||||
|  |     enum puzzlefw_trigger_mode trigger_mode = puzzlefw_get_trigger_mode(ctx); | ||||||
|  |     int trigger_channel = puzzlefw_get_trigger_ext_channel(ctx); | ||||||
|  |     int trigger_falling = puzzlefw_get_trigger_ext_falling(ctx); | ||||||
|  |     printf("    trigger mode =   %s, channel=%d, falling=%d\n", | ||||||
|  |            (trigger_mode == TRIG_AUTO) ? "auto" : | ||||||
|  |            (trigger_mode == TRIG_EXTERNAL) ?"external" : | ||||||
|  |            "none", | ||||||
|  |            trigger_channel, | ||||||
|  |            trigger_falling); | ||||||
|  | 
 | ||||||
|  |     v = puzzlefw_read_reg(ctx, REG_TRIGGER_DELAY); | ||||||
|  |     printf("    trigger_delay =  0x%08x\n", v); | ||||||
|  | 
 | ||||||
|  |     v = puzzlefw_read_reg(ctx, REG_RECORD_LENGTH); | ||||||
|  |     printf("    record_length =  0x%08x\n", v); | ||||||
|  | 
 | ||||||
|  |     v = puzzlefw_read_reg(ctx, REG_DECIMATION_FACTOR); | ||||||
|  |     printf("    decimation_fac = 0x%08x\n", v); | ||||||
|  | 
 | ||||||
|  |     v = puzzlefw_read_reg(ctx, REG_SHIFT_STEPS); | ||||||
|  |     printf("    shift_steps =    0x%08x\n", v); | ||||||
|  | 
 | ||||||
|  |     v = puzzlefw_read_reg(ctx, REG_AVERAGING_EN); | ||||||
|  |     printf("    averaging_en =   0x%08x\n", v); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -489,20 +652,20 @@ static void blast_dma(struct puzzlefw_context *ctx) | ||||||
|     printf("Starting DMA blaster ...\n"); |     printf("Starting DMA blaster ...\n"); | ||||||
| 
 | 
 | ||||||
|     // Disable DMA writer.
 |     // Disable DMA writer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 0); | ||||||
| 
 | 
 | ||||||
|     // Setup DMA buffer.
 |     // Setup DMA buffer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_START, 0); |     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_START, 0); | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_END, ctx->dma_buf_size); |     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_END, ctx->dma_buf_size); | ||||||
| 
 | 
 | ||||||
|     // Set invalid limit to keep the writer blasting.
 |     // Set invalid limit to keep the writer blasting.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_LIMIT, 0xffffffff); |     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_LIMIT, 0xffffffff); | ||||||
| 
 | 
 | ||||||
|     // Initialize DMA writer.
 |     // Initialize DMA writer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 2); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 2); | ||||||
| 
 | 
 | ||||||
|     // Enable DMA writer.
 |     // Enable DMA writer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 1); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 1); | ||||||
| 
 | 
 | ||||||
|     struct timespec tp; |     struct timespec tp; | ||||||
|     tp.tv_sec = 10; |     tp.tv_sec = 10; | ||||||
|  | @ -510,7 +673,7 @@ static void blast_dma(struct puzzlefw_context *ctx) | ||||||
|     clock_nanosleep(CLOCK_MONOTONIC, 0, &tp, NULL); |     clock_nanosleep(CLOCK_MONOTONIC, 0, &tp, NULL); | ||||||
| 
 | 
 | ||||||
|     // Disable DMA writer.
 |     // Disable DMA writer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 0); | ||||||
| 
 | 
 | ||||||
|     printf("Stopped DMA blaster\n"); |     printf("Stopped DMA blaster\n"); | ||||||
| } | } | ||||||
|  | @ -590,20 +753,20 @@ static int wait_dma_data( | ||||||
|     if (addr_intr >= ctx->dma_buf_size) { |     if (addr_intr >= ctx->dma_buf_size) { | ||||||
|         addr_intr -= ctx->dma_buf_size; |         addr_intr -= ctx->dma_buf_size; | ||||||
|     } |     } | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_INTR, addr_intr); |     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_INTR, addr_intr); | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 3); |     puzzlefw_write_reg(ctx, REG_ACQ_INTR_CTRL, 3); | ||||||
| 
 | 
 | ||||||
|     // Check if data are already available.
 |     // Check if data are already available.
 | ||||||
|     // This is necessary to prevent a race condition when data becomes
 |     // This is necessary to prevent a race condition when data becomes
 | ||||||
|     // available just before the interrupt is enabled.
 |     // available just before the interrupt is enabled.
 | ||||||
|     uint32_t write_pointer = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR); |     uint32_t write_pointer = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_PTR); | ||||||
|     uint32_t navail = |     uint32_t navail = | ||||||
|         (write_pointer >= read_pointer) ? |         (write_pointer >= read_pointer) ? | ||||||
|             (write_pointer - read_pointer) : |             (write_pointer - read_pointer) : | ||||||
|             (ctx->dma_buf_size + write_pointer - read_pointer); |             (ctx->dma_buf_size + write_pointer - read_pointer); | ||||||
|     if (navail >= wait_avail) { |     if (navail >= wait_avail) { | ||||||
|         // Data already available; disable DMA writer interrupts.
 |         // Data already available; disable DMA writer interrupts.
 | ||||||
|         puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2); |         puzzlefw_write_reg(ctx, REG_ACQ_INTR_CTRL, 2); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -615,7 +778,7 @@ static int wait_dma_data( | ||||||
|     fds[0].fd = ctx->uio_fd; |     fds[0].fd = ctx->uio_fd; | ||||||
|     fds[0].events = POLLIN; |     fds[0].events = POLLIN; | ||||||
|     fds[1].fd = conn; |     fds[1].fd = conn; | ||||||
|     fds[1].events = 0; |     fds[1].events = POLLIN; | ||||||
| 
 | 
 | ||||||
|     int ret = poll(fds, 2, timeout_ms); |     int ret = poll(fds, 2, timeout_ms); | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|  | @ -624,7 +787,7 @@ static int wait_dma_data( | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Disable DMA writer interrupt and clear pending interrupt.
 |     // Disable DMA writer interrupt and clear pending interrupt.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2); |     puzzlefw_write_reg(ctx, REG_ACQ_INTR_CTRL, 2); | ||||||
| 
 | 
 | ||||||
|     if ((fds[0].revents & POLLIN) != 0) { |     if ((fds[0].revents & POLLIN) != 0) { | ||||||
|         // Interrupt occurred.
 |         // Interrupt occurred.
 | ||||||
|  | @ -633,8 +796,10 @@ static int wait_dma_data( | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ((fds[1].revents & POLLHUP) != 0) { |     if ((fds[1].revents & (POLLHUP | POLLIN)) != 0) { | ||||||
|         // Connection closed.
 |         // Connection closed, or client sent unexpected data.
 | ||||||
|  |         // POLLHUP does not happen on Linux as long as our side of the
 | ||||||
|  |         // connection remains open, but check it anyway to be sure.
 | ||||||
|         return 2; |         return 2; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -671,16 +836,16 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_EN, 0); |     puzzlefw_write_reg(ctx, REG_DMA_EN, 0); | ||||||
| 
 | 
 | ||||||
|     // Disable DMA write channel.
 |     // Disable DMA write channel.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 0); | ||||||
| 
 | 
 | ||||||
|     // Initialize DMA write buffer.
 |     // Initialize DMA write buffer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_START, 0); |     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_START, 0); | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_END, ctx->dma_buf_size); |     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_END, ctx->dma_buf_size); | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_LIMIT, |     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_LIMIT, | ||||||
|                        ctx->dma_buf_size - pointer_margin); |                        ctx->dma_buf_size - pointer_margin); | ||||||
| 
 | 
 | ||||||
|     // Disable DMA writer interrupts; clear interrupt status.
 |     // Disable DMA writer interrupts; clear interrupt status.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2); |     puzzlefw_write_reg(ctx, REG_ACQ_INTR_CTRL, 2); | ||||||
| 
 | 
 | ||||||
|     // Clear AXI DMA state.
 |     // Clear AXI DMA state.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CLEAR, 1); |     puzzlefw_write_reg(ctx, REG_DMA_CLEAR, 1); | ||||||
|  | @ -689,10 +854,10 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_EN, 1); |     puzzlefw_write_reg(ctx, REG_DMA_EN, 1); | ||||||
| 
 | 
 | ||||||
|     // Initialize DMA writer.
 |     // Initialize DMA writer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 2); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 2); | ||||||
| 
 | 
 | ||||||
|     // Enable DMA writer.
 |     // Enable DMA writer.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 1); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 1); | ||||||
| 
 | 
 | ||||||
|     uint32_t read_pointer = 0; |     uint32_t read_pointer = 0; | ||||||
|     int ret; |     int ret; | ||||||
|  | @ -709,7 +874,7 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Determine number of bytes available.
 |         // Determine number of bytes available.
 | ||||||
|         uint32_t write_pointer = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR); |         uint32_t write_pointer = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_PTR); | ||||||
|         uint32_t navail = (write_pointer >= read_pointer) ? |         uint32_t navail = (write_pointer >= read_pointer) ? | ||||||
|             (write_pointer - read_pointer) : |             (write_pointer - read_pointer) : | ||||||
|             (ctx->dma_buf_size + write_pointer - read_pointer); |             (ctx->dma_buf_size + write_pointer - read_pointer); | ||||||
|  | @ -729,7 +894,7 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             write_pointer = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR); |             write_pointer = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_PTR); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Determine number of bytes available.
 |         // Determine number of bytes available.
 | ||||||
|  | @ -759,7 +924,7 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | ||||||
|             // Update read pointer and update DMA writer limit.
 |             // Update read pointer and update DMA writer limit.
 | ||||||
|             read_pointer += block_size; |             read_pointer += block_size; | ||||||
|             if (read_pointer > pointer_margin) { |             if (read_pointer > pointer_margin) { | ||||||
|                 puzzlefw_write_reg(ctx, REG_DMA_ADDR_LIMIT, |                 puzzlefw_write_reg(ctx, REG_ACQ_ADDR_LIMIT, | ||||||
|                                    read_pointer - pointer_margin); |                                    read_pointer - pointer_margin); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -786,10 +951,10 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_EN, 0); |     puzzlefw_write_reg(ctx, REG_DMA_EN, 0); | ||||||
| 
 | 
 | ||||||
|     // Disable DMA write channel.
 |     // Disable DMA write channel.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0); |     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 0); | ||||||
| 
 | 
 | ||||||
|     // Disable DMA writer interrupts; clear interrupt status.
 |     // Disable DMA writer interrupts; clear interrupt status.
 | ||||||
|     puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2); |     puzzlefw_write_reg(ctx, REG_ACQ_INTR_CTRL, 2); | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  | @ -860,8 +1025,18 @@ int main(int argc, char **argv) | ||||||
|     int dmaclear = 0; |     int dmaclear = 0; | ||||||
|     int blastdma = 0; |     int blastdma = 0; | ||||||
|     int server = 0; |     int server = 0; | ||||||
|     int set_divider = 0; |     int tsclear = 0; | ||||||
|     int divider; |     int acqon = 0; | ||||||
|  |     int acqoff = 0; | ||||||
|  |     int trigger = 0; | ||||||
|  |     int trigauto = 0; | ||||||
|  |     int trignone = 0; | ||||||
|  |     int set_trigdelay = 0, trigdelay = 0; | ||||||
|  |     int set_reclen = 0, reclen = 0; | ||||||
|  |     int set_decimate = 0, decimate = 0; | ||||||
|  |     int set_shift = 0, shift_steps = 0; | ||||||
|  |     int avgon = 0, avgoff = 0; | ||||||
|  |     int simon = 0, simoff = 0; | ||||||
| 
 | 
 | ||||||
|     if (argc == 2 && strcmp(argv[1], "show") == 0) { |     if (argc == 2 && strcmp(argv[1], "show") == 0) { | ||||||
|         show = 1; |         show = 1; | ||||||
|  | @ -877,9 +1052,38 @@ int main(int argc, char **argv) | ||||||
|         dmaclear = 1; |         dmaclear = 1; | ||||||
|     } else if (argc == 2 && strcmp(argv[1], "blastdma") == 0) { |     } else if (argc == 2 && strcmp(argv[1], "blastdma") == 0) { | ||||||
|         blastdma = 1; |         blastdma = 1; | ||||||
|     } else if (argc == 3 && strcmp(argv[1], "divider") == 0) { |     } else if (argc == 2 && strcmp(argv[1], "tsclear") == 0) { | ||||||
|         set_divider = 1; |         tsclear = 1; | ||||||
|         divider = atoi(argv[2]); |     } else if (argc == 2 && strcmp(argv[1], "acqon") == 0) { | ||||||
|  |         acqon = 1; | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "acqoff") == 0) { | ||||||
|  |         acqoff = 1; | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "trigger") == 0) { | ||||||
|  |         trigger = 1; | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "trigauto") == 0) { | ||||||
|  |         trigauto = 1; | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "trignone") == 0) { | ||||||
|  |         trignone = 1; | ||||||
|  |     } else if (argc == 3 && strcmp(argv[1], "trigdelay") == 0) { | ||||||
|  |         set_trigdelay = 1; | ||||||
|  |         trigdelay = atoi(argv[2]); | ||||||
|  |     } else if (argc == 3 && strcmp(argv[1], "reclen") == 0) { | ||||||
|  |         set_reclen = 1; | ||||||
|  |         reclen = atoi(argv[2]); | ||||||
|  |     } else if (argc == 3 && strcmp(argv[1], "decimate") == 0) { | ||||||
|  |         set_decimate = 1; | ||||||
|  |         decimate = atoi(argv[2]); | ||||||
|  |     } else if (argc == 3 && strcmp(argv[1], "shift") == 0) { | ||||||
|  |         set_shift = 1; | ||||||
|  |         shift_steps = atoi(argv[2]); | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "avgon") == 0) { | ||||||
|  |         avgon = 1; | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "avgoff") == 0) { | ||||||
|  |         avgoff = 1; | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "simon") == 0) { | ||||||
|  |         simon = 1; | ||||||
|  |     } else if (argc == 2 && strcmp(argv[1], "simoff") == 0) { | ||||||
|  |         simoff = 1; | ||||||
|     } else if (argc == 2 && strcmp(argv[1], "server") == 0) { |     } else if (argc == 2 && strcmp(argv[1], "server") == 0) { | ||||||
|         server = 1; |         server = 1; | ||||||
|     } else { |     } else { | ||||||
|  | @ -906,6 +1110,48 @@ int main(int argc, char **argv) | ||||||
|             "    testje blastdma\n" |             "    testje blastdma\n" | ||||||
|             "        Run unlimited DMA into buffer for 10 seconds.\n" |             "        Run unlimited DMA into buffer for 10 seconds.\n" | ||||||
|             "\n" |             "\n" | ||||||
|  |             "    testje tsclear\n" | ||||||
|  |             "        Clear timestamp counter.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje acqon\n" | ||||||
|  |             "        Enable acquisition.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje acqoff\n" | ||||||
|  |             "        Disable acquisition.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje trigger\n" | ||||||
|  |             "        Trigger once.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje trigauto\n" | ||||||
|  |             "        Enable continuous triggering.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje trignone\n" | ||||||
|  |             "        Disable triggering.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje trigdelay N\n" | ||||||
|  |             "        Set trigger delay N cycles.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje reclen N\n" | ||||||
|  |             "        Set record length N + 1.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje decimate N\n" | ||||||
|  |             "        Set decimation factor N + 1.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje shift N\n" | ||||||
|  |             "        Set shift-right by N.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje avgon\n" | ||||||
|  |             "        Enable averaging.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje avgoff\n" | ||||||
|  |             "        Disable averaging.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje simon\n" | ||||||
|  |             "        Use simulated ADC data.\n" | ||||||
|  |             "\n" | ||||||
|  |             "    testje simoff\n" | ||||||
|  |             "        Use real ADC data.\n" | ||||||
|  |             "\n" | ||||||
|             "    testje server\n" |             "    testje server\n" | ||||||
|             "        Open TCP port 5001 to stream DMA data.\n" |             "        Open TCP port 5001 to stream DMA data.\n" | ||||||
|             "\n"); |             "\n"); | ||||||
|  | @ -949,8 +1195,60 @@ int main(int argc, char **argv) | ||||||
|         blast_dma(&ctx); |         blast_dma(&ctx); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (set_divider) { |     if (tsclear) { | ||||||
|         puzzlefw_write_reg(&ctx, REG_TEST_DIVIDER, divider); |         puzzlefw_clear_timestamp(&ctx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (acqon) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_ACQUISITION_EN, 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (acqoff) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_ACQUISITION_EN, 0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (trigger) { | ||||||
|  |         puzzlefw_trigger_force(&ctx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (trigauto) { | ||||||
|  |         puzzlefw_set_trigger_mode(&ctx, TRIG_AUTO); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (trignone) { | ||||||
|  |         puzzlefw_set_trigger_mode(&ctx, TRIG_NONE); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (set_trigdelay) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_TRIGGER_DELAY, trigdelay); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (set_reclen) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_RECORD_LENGTH, reclen); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (set_decimate) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_DECIMATION_FACTOR, decimate); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (set_shift) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_SHIFT_STEPS, shift_steps); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (avgon) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_AVERAGING_EN, 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (avgoff) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_AVERAGING_EN, 0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (simon) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_SIMULATE_ADC, 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (simoff) { | ||||||
|  |         puzzlefw_write_reg(&ctx, REG_SIMULATE_ADC, 0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (server) { |     if (server) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue