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. | ||||
|         in_valid:           in  std_logic; | ||||
|         in_ready:           out std_logic; | ||||
|         in_empty:           out std_logic; | ||||
|         in_data:            in  dma_data_type; | ||||
| 
 | ||||
|         -- Signals to AXI master. | ||||
|  | @ -173,6 +174,7 @@ begin | |||
|     channel_busy        <= r.channel_busy; | ||||
|     addr_pointer        <= r.addr_pointer; | ||||
|     intr_out            <= r.intr_out; | ||||
|     in_empty            <= not s_fifo_valid; | ||||
|     write_cmd_addr      <= r.cmd_addr; | ||||
|     write_cmd_length    <= "0000" when (r.cmd_full_burst = '0') else | ||||
|                            std_logic_vector(to_unsigned(transfer_size - 1, 4)); | ||||
|  | @ -226,7 +228,6 @@ begin | |||
| 
 | ||||
|                     v.cmd_valid := '1'; | ||||
|                     v.channel_busy := '1'; | ||||
|                     v.pending_beats := to_unsigned(0, v.pending_beats'length); | ||||
|                     v.state := STATE_SINGLE_START; | ||||
| 
 | ||||
|                 end if; | ||||
|  | @ -260,7 +261,7 @@ begin | |||
|             when STATE_SINGLE_DATA => | ||||
| 
 | ||||
|                 -- 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; | ||||
|                 end if; | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,6 +36,9 @@ package puzzlefw_pkg is | |||
|     -- 48-bit timestamp. | ||||
|     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. | ||||
|     constant reg_addr_mask:         std_logic_vector(31 downto 0) := x"0010fffc"; | ||||
|     constant reg_info:              natural := 16#000000#; | ||||
|  | @ -44,6 +47,9 @@ package puzzlefw_pkg is | |||
|     constant reg_dma_en:            natural := 16#000100#; | ||||
|     constant reg_dma_status:        natural := 16#000104#; | ||||
|     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_end:      natural := 16#000204#; | ||||
|     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_channel_ctrl:  natural := 16#000214#; | ||||
|     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_divider:      natural := 16#000408#; | ||||
|     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_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. | ||||
|     type registers_control is record | ||||
|         irq_enable:         std_logic; | ||||
|         test_led:           std_logic_vector(7 downto 0); | ||||
|         test_divider:       std_logic_vector(15 downto 0); | ||||
|         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_end:       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_channel_en:     std_logic; | ||||
|         acq_channel_init:   std_logic;              -- single cycle | ||||
|         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_size:       std_logic_vector(31 downto 12); | ||||
|     end record; | ||||
|  | @ -98,15 +128,10 @@ package puzzlefw_pkg is | |||
|         dma_err_write:      std_logic; | ||||
|         dma_err_address:    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_channel_busy:   std_logic; | ||||
|     end record; | ||||
| 
 | ||||
|     -- 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; | ||||
|         trig_waiting:       std_logic; | ||||
|     end record; | ||||
| 
 | ||||
|     constant registers_control_init: registers_control := ( | ||||
|  | @ -114,20 +139,31 @@ package puzzlefw_pkg is | |||
|         test_led            => (others => '0'), | ||||
|         test_divider        => (others => '0'), | ||||
|         dma_en              => '0', | ||||
|         dma_clear           => '0', | ||||
|         timestamp_clear     => '0', | ||||
|         acq_addr_start      => (others => '0'), | ||||
|         acq_addr_end        => (others => '0'), | ||||
|         acq_addr_limit      => (others => '0'), | ||||
|         acq_addr_intr       => (others => '0'), | ||||
|         acq_channel_en      => '0', | ||||
|         acq_channel_init    => '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_size        => (others => '0') | ||||
|     ); | ||||
| 
 | ||||
|     constant registers_trigger_init: registers_trigger := ( | ||||
|         dma_clear       => '0', | ||||
|         acq_channel_init => '0', | ||||
|         acq_intr_clear  => '0' | ||||
|     ); | ||||
| 
 | ||||
| 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. | ||||
|     signal s_reset:         std_logic; | ||||
| 
 | ||||
|     -- Internal clock signal. | ||||
|     signal s_adc_clk_ibuf:  std_logic; | ||||
| 
 | ||||
|     signal r_fclk_cnt:      unsigned(26 downto 0); | ||||
|     signal r_fclk_led:      std_logic; | ||||
|     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_rready:    std_logic; | ||||
| 
 | ||||
|     signal s_dma_interrupt: std_logic; | ||||
|     signal s_irq_pending:   std_logic_vector(0 downto 0); | ||||
|     signal s_irq_f2p:       std_logic_vector(7 downto 0); | ||||
| 
 | ||||
|     signal s_reg_control:   registers_control; | ||||
|     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_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_finished:  std_logic_vector(0 downto 0); | ||||
| 
 | ||||
|     signal s_dma_in_valid:  std_logic; | ||||
|     signal s_dma_in_ready:  std_logic; | ||||
|     signal s_dma_in_data:   std_logic_vector(63 downto 0); | ||||
|     signal s_timestamp:     std_logic_vector(timestamp_bits - 1 downto 0); | ||||
|     signal s_adc_data:      adc_data_array(0 to 1); | ||||
| 
 | ||||
|     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_raddr:    std_logic_vector(9 downto 0); | ||||
|  | @ -276,8 +280,7 @@ begin | |||
|             apb_pslverr         => s_apb_pslverr, | ||||
|             apb_prdata          => s_apb_prdata, | ||||
|             reg_control         => s_reg_control, | ||||
|             reg_status          => s_reg_status, | ||||
|             reg_trigger         => s_reg_trigger | ||||
|             reg_status          => s_reg_status | ||||
|         ); | ||||
| 
 | ||||
|     -- AXI master. | ||||
|  | @ -296,7 +299,7 @@ begin | |||
|             err_write           => s_reg_status.dma_err_write, | ||||
|             err_address         => s_reg_status.dma_err_address, | ||||
|             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_length     => (others => (others => '0')), | ||||
|             read_cmd_valid      => (others => '0'), | ||||
|  | @ -361,18 +364,19 @@ begin | |||
|             reset               => s_reset, | ||||
|             channel_en          => s_reg_control.acq_channel_en, | ||||
|             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_end            => s_reg_control.acq_addr_end, | ||||
|             addr_limit          => s_reg_control.acq_addr_limit, | ||||
|             addr_interrupt      => s_reg_control.acq_addr_intr, | ||||
|             addr_pointer        => s_reg_status.acq_addr_ptr, | ||||
|             intr_en             => s_reg_control.acq_intr_en, | ||||
|             intr_clear          => s_reg_trigger.acq_intr_clear, | ||||
|             intr_out            => s_dma_interrupt, | ||||
|             in_valid            => s_dma_in_valid, | ||||
|             in_ready            => s_dma_in_ready, | ||||
|             in_data             => s_dma_in_data, | ||||
|             intr_clear          => s_reg_control.acq_intr_clear, | ||||
|             intr_out            => s_irq_pending(0), | ||||
|             in_valid            => s_acq_dma_valid, | ||||
|             in_ready            => s_acq_dma_ready, | ||||
|             in_empty            => s_acq_dma_empty, | ||||
|             in_data             => s_acq_dma_data, | ||||
|             write_cmd_addr      => s_dma_write_cmd_addr(0), | ||||
|             write_cmd_length    => s_dma_write_cmd_length(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_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. | ||||
|     s_irq_pending(0) <= s_dma_interrupt; | ||||
|     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(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 | ||||
|     begin | ||||
|         if rising_edge(clk_fclk) then | ||||
|  |  | |||
|  | @ -32,8 +32,7 @@ entity registers is | |||
| 
 | ||||
|         -- FPGA interface. | ||||
|         reg_control:        out registers_control; | ||||
|         reg_status:         in  registers_status; | ||||
|         reg_trigger:        out registers_trigger | ||||
|         reg_status:         in  registers_status | ||||
|     ); | ||||
| 
 | ||||
| end entity; | ||||
|  | @ -44,14 +43,12 @@ architecture arch of registers is | |||
|         pready:         std_logic; | ||||
|         prdata:         std_logic_vector(31 downto 0); | ||||
|         reg_control:    registers_control; | ||||
|         reg_trigger:    registers_trigger; | ||||
|     end record; | ||||
| 
 | ||||
|     constant regs_init: regs_type := ( | ||||
|         pready          => '0', | ||||
|         prdata          => (others => '0'), | ||||
|         reg_control     => registers_control_init, | ||||
|         reg_trigger     => registers_trigger_init | ||||
|         reg_control     => registers_control_init | ||||
|     ); | ||||
| 
 | ||||
|     signal r: regs_type := regs_init; | ||||
|  | @ -63,9 +60,7 @@ begin | |||
|     apb_pready      <= r.pready; | ||||
|     apb_pslverr     <= '0';  -- never signal errors | ||||
|     apb_prdata      <= r.prdata; | ||||
| 
 | ||||
|     reg_control     <= r.reg_control; | ||||
|     reg_trigger     <= r.reg_trigger; | ||||
| 
 | ||||
|     -- Combinatorial process. | ||||
|     process (all) is | ||||
|  | @ -75,7 +70,11 @@ begin | |||
|         v := r; | ||||
| 
 | ||||
|         -- 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). | ||||
|         v.pready        := apb_psel and (not apb_penable); | ||||
|  | @ -98,6 +97,8 @@ begin | |||
|                     v.prdata(2) := reg_status.dma_err_write; | ||||
|                     v.prdata(3) := reg_status.dma_err_address; | ||||
|                     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_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; | ||||
|  | @ -107,6 +108,20 @@ begin | |||
|                     v.prdata(0) := r.reg_control.acq_channel_en; | ||||
|                     v.prdata(8) := reg_status.acq_channel_busy; | ||||
|                 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_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; | ||||
|  | @ -121,17 +136,32 @@ begin | |||
|             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_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_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_intr =>   v.reg_control.acq_addr_intr := apb_pwdata(31 downto 3); | ||||
|                 when reg_acq_channel_ctrl => | ||||
|                     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 => | ||||
|                     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_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); | ||||
|  |  | |||
|  | @ -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. | ||||
| 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_write_channel.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 | ||||
| 
 | ||||
| # Load Zynq block design. | ||||
|  |  | |||
|  | @ -31,14 +31,26 @@ | |||
| #define REG_DMA_EN              0x0100 | ||||
| #define REG_DMA_STATUS          0x0104 | ||||
| #define REG_DMA_CLEAR           0x0108 | ||||
| #define REG_DMA_ADDR_START      0x0200 | ||||
| #define REG_DMA_ADDR_END        0x0204 | ||||
| #define REG_DMA_ADDR_LIMIT      0x0208 | ||||
| #define REG_DMA_ADDR_INTR       0x020c | ||||
| #define REG_DMA_ADDR_PTR        0x0210 | ||||
| #define REG_DMA_CHANNEL_CTRL    0x0214 | ||||
| #define REG_DMA_INTR_CTRL       0x0218 | ||||
| #define REG_TEST_DIVIDER        0x0408 | ||||
| #define REG_TIMESTAMP_LO        0x0180 | ||||
| #define REG_TIMESTAMP_HI        0x0184 | ||||
| #define REG_TIMESTAMP_CLEAR     0x0188 | ||||
| #define REG_ACQ_ADDR_START      0x0200 | ||||
| #define REG_ACQ_ADDR_END        0x0204 | ||||
| #define REG_ACQ_ADDR_LIMIT      0x0208 | ||||
| #define REG_ACQ_ADDR_INTR       0x020c | ||||
| #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 { | ||||
|  | @ -51,6 +63,12 @@ struct puzzlefw_context { | |||
|     char                uio_path[128]; | ||||
| }; | ||||
| 
 | ||||
| enum puzzlefw_trigger_mode { | ||||
|     TRIG_NONE = 0, | ||||
|     TRIG_AUTO = 1, | ||||
|     TRIG_EXTERNAL = 2 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * 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) | ||||
| { | ||||
|     uint32_t v; | ||||
|  | @ -415,29 +550,57 @@ static void show_status(struct puzzlefw_context *ctx) | |||
|     v = puzzlefw_read_reg(ctx, REG_DMA_STATUS); | ||||
|     printf("    dma_status =     0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_START); | ||||
|     printf("    dma_addr_start = 0x%08x\n", v); | ||||
|     uint64_t ts = puzzlefw_get_timestamp(ctx); | ||||
|     printf("    timestamp =      %llu\n", (unsigned long long)ts); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_END); | ||||
|     printf("    dma_addr_end =   0x%08x\n", v); | ||||
|     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_START); | ||||
|     printf("    acq_addr_start = 0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_LIMIT); | ||||
|     printf("    dma_addr_limit = 0x%08x\n", v); | ||||
|     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_END); | ||||
|     printf("    acq_addr_end =   0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_INTR); | ||||
|     printf("    dma_addr_intr =  0x%08x\n", v); | ||||
|     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_LIMIT); | ||||
|     printf("    acq_addr_limit = 0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR); | ||||
|     printf("    dma_addr_ptr =   0x%08x\n", v); | ||||
|     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_INTR); | ||||
|     printf("    acq_addr_intr =  0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_CHANNEL_CTRL); | ||||
|     printf("    dma_channel_ctrl = 0x%08x\n", v); | ||||
|     v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_PTR); | ||||
|     printf("    acq_addr_ptr =   0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_DMA_INTR_CTRL); | ||||
|     printf("    dma_intr_ctrl =  0x%08x\n", v); | ||||
|     v = puzzlefw_read_reg(ctx, REG_ACQ_CHANNEL_CTRL); | ||||
|     printf("    acq_channel_ctrl = 0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_TEST_DIVIDER); | ||||
|     printf("    test_divider  =  0x%08x\n", v); | ||||
|     v = puzzlefw_read_reg(ctx, REG_ACQ_INTR_CTRL); | ||||
|     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"); | ||||
| 
 | ||||
|     // Disable DMA writer.
 | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 0); | ||||
| 
 | ||||
|     // Setup DMA buffer.
 | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_START, 0); | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_END, ctx->dma_buf_size); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_START, 0); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_END, ctx->dma_buf_size); | ||||
| 
 | ||||
|     // 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.
 | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 2); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 2); | ||||
| 
 | ||||
|     // Enable DMA writer.
 | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 1); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 1); | ||||
| 
 | ||||
|     struct timespec tp; | ||||
|     tp.tv_sec = 10; | ||||
|  | @ -510,7 +673,7 @@ static void blast_dma(struct puzzlefw_context *ctx) | |||
|     clock_nanosleep(CLOCK_MONOTONIC, 0, &tp, NULL); | ||||
| 
 | ||||
|     // 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"); | ||||
| } | ||||
|  | @ -590,20 +753,20 @@ static int wait_dma_data( | |||
|     if (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_DMA_INTR_CTRL, 3); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_INTR, addr_intr); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_INTR_CTRL, 3); | ||||
| 
 | ||||
|     // Check if data are already available.
 | ||||
|     // This is necessary to prevent a race condition when data becomes
 | ||||
|     // 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 = | ||||
|         (write_pointer >= read_pointer) ? | ||||
|             (write_pointer - read_pointer) : | ||||
|             (ctx->dma_buf_size + write_pointer - read_pointer); | ||||
|     if (navail >= wait_avail) { | ||||
|         // 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; | ||||
|     } | ||||
| 
 | ||||
|  | @ -615,7 +778,7 @@ static int wait_dma_data( | |||
|     fds[0].fd = ctx->uio_fd; | ||||
|     fds[0].events = POLLIN; | ||||
|     fds[1].fd = conn; | ||||
|     fds[1].events = 0; | ||||
|     fds[1].events = POLLIN; | ||||
| 
 | ||||
|     int ret = poll(fds, 2, timeout_ms); | ||||
|     if (ret < 0) { | ||||
|  | @ -624,7 +787,7 @@ static int wait_dma_data( | |||
|     } | ||||
| 
 | ||||
|     // 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) { | ||||
|         // Interrupt occurred.
 | ||||
|  | @ -633,8 +796,10 @@ static int wait_dma_data( | |||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     if ((fds[1].revents & POLLHUP) != 0) { | ||||
|         // Connection closed.
 | ||||
|     if ((fds[1].revents & (POLLHUP | POLLIN)) != 0) { | ||||
|         // 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; | ||||
|     } | ||||
| 
 | ||||
|  | @ -671,16 +836,16 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | |||
|     puzzlefw_write_reg(ctx, REG_DMA_EN, 0); | ||||
| 
 | ||||
|     // 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.
 | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_START, 0); | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_END, ctx->dma_buf_size); | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_ADDR_LIMIT, | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_START, 0); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_END, ctx->dma_buf_size); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_ADDR_LIMIT, | ||||
|                        ctx->dma_buf_size - pointer_margin); | ||||
| 
 | ||||
|     // 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.
 | ||||
|     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); | ||||
| 
 | ||||
|     // Initialize DMA writer.
 | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 2); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_CHANNEL_CTRL, 2); | ||||
| 
 | ||||
|     // 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; | ||||
|     int ret; | ||||
|  | @ -709,7 +874,7 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | |||
|         } | ||||
| 
 | ||||
|         // 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) ? | ||||
|             (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; | ||||
|             } | ||||
| 
 | ||||
|             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.
 | ||||
|  | @ -759,7 +924,7 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | |||
|             // Update read pointer and update DMA writer limit.
 | ||||
|             read_pointer += block_size; | ||||
|             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); | ||||
|             } | ||||
| 
 | ||||
|  | @ -786,10 +951,10 @@ int transmit_dma_data(struct puzzlefw_context *ctx, int conn) | |||
|     puzzlefw_write_reg(ctx, REG_DMA_EN, 0); | ||||
| 
 | ||||
|     // 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.
 | ||||
|     puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2); | ||||
|     puzzlefw_write_reg(ctx, REG_ACQ_INTR_CTRL, 2); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
|  | @ -860,8 +1025,18 @@ int main(int argc, char **argv) | |||
|     int dmaclear = 0; | ||||
|     int blastdma = 0; | ||||
|     int server = 0; | ||||
|     int set_divider = 0; | ||||
|     int divider; | ||||
|     int tsclear = 0; | ||||
|     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) { | ||||
|         show = 1; | ||||
|  | @ -877,9 +1052,38 @@ int main(int argc, char **argv) | |||
|         dmaclear = 1; | ||||
|     } else if (argc == 2 && strcmp(argv[1], "blastdma") == 0) { | ||||
|         blastdma = 1; | ||||
|     } else if (argc == 3 && strcmp(argv[1], "divider") == 0) { | ||||
|         set_divider = 1; | ||||
|         divider = atoi(argv[2]); | ||||
|     } else if (argc == 2 && strcmp(argv[1], "tsclear") == 0) { | ||||
|         tsclear = 1; | ||||
|     } 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) { | ||||
|         server = 1; | ||||
|     } else { | ||||
|  | @ -906,6 +1110,48 @@ int main(int argc, char **argv) | |||
|             "    testje blastdma\n" | ||||
|             "        Run unlimited DMA into buffer for 10 seconds.\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" | ||||
|             "        Open TCP port 5001 to stream DMA data.\n" | ||||
|             "\n"); | ||||
|  | @ -949,8 +1195,60 @@ int main(int argc, char **argv) | |||
|         blast_dma(&ctx); | ||||
|     } | ||||
| 
 | ||||
|     if (set_divider) { | ||||
|         puzzlefw_write_reg(&ctx, REG_TEST_DIVIDER, divider); | ||||
|     if (tsclear) { | ||||
|         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) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue