Add monitoring of ADC sample and min/max range
This commit is contained in:
		
							parent
							
								
									716d16e6a3
								
							
						
					
					
						commit
						393d87f9d2
					
				|  | @ -54,9 +54,6 @@ entity acquisition_chain is | |||
|         -- 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; | ||||
| 
 | ||||
|  | @ -142,25 +139,6 @@ begin | |||
|             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 | ||||
| 
 | ||||
|  | @ -172,7 +150,7 @@ begin | |||
|                 signed_data     => false ) | ||||
|             port map ( | ||||
|                 clk             => clk, | ||||
|                 in_data         => s_adc_sample(i), | ||||
|                 in_data         => adc_data_in(i), | ||||
|                 in_shift        => shift_steps, | ||||
|                 out_data        => s_adc_shifted(i) ); | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,107 @@ | |||
| -- | ||||
| --  Monitor min/max sample values from ADC. | ||||
| -- | ||||
| --  Joris van Rantwijk 2024 | ||||
| -- | ||||
| 
 | ||||
| library ieee; | ||||
| use ieee.std_logic_1164.all; | ||||
| use ieee.numeric_std.all; | ||||
| 
 | ||||
| use work.puzzlefw_pkg.all; | ||||
| 
 | ||||
| 
 | ||||
| entity adc_range_monitor is | ||||
| 
 | ||||
|     generic ( | ||||
|         -- True if ADC samples are signed values. | ||||
|         -- False if ADC samples are unsigned binary offset values. | ||||
|         signed_data:        boolean | ||||
|     ); | ||||
| 
 | ||||
|     port ( | ||||
|         -- Main clock, active on rising edge. | ||||
|         clk:                in  std_logic; | ||||
| 
 | ||||
|         -- Reset, active high, synchronous to main clock. | ||||
|         reset:              in  std_logic; | ||||
| 
 | ||||
|         -- High to clear min/max sample values. | ||||
|         clear:              in  std_logic; | ||||
| 
 | ||||
|         -- Input sample stream. | ||||
|         in_data:            in  adc_data_type; | ||||
| 
 | ||||
|         -- Minimum and maximum sample value observed. | ||||
|         min_value:          out adc_data_type; | ||||
|         max_value:          out adc_data_type | ||||
|     ); | ||||
| 
 | ||||
| end entity; | ||||
| 
 | ||||
| architecture arch of adc_range_monitor is | ||||
| 
 | ||||
|     type regs_type is record | ||||
|         min_value:          adc_data_type; | ||||
|         max_value:          adc_data_type; | ||||
|     end record; | ||||
| 
 | ||||
|     signal r: regs_type; | ||||
|     signal rnext: regs_type; | ||||
| 
 | ||||
|     -- Return True if X is less than Y. | ||||
|     function sample_less(x: adc_data_type; y: adc_data_type) | ||||
|         return boolean | ||||
|     is begin | ||||
|         if signed_data then | ||||
|             return signed(x) < signed(y); | ||||
|         else | ||||
|             return unsigned(x) < unsigned(y); | ||||
|         end if; | ||||
|     end function; | ||||
| 
 | ||||
| begin | ||||
| 
 | ||||
|     -- Drive output. | ||||
|     min_value <= r.min_value; | ||||
|     max_value <= r.max_value; | ||||
| 
 | ||||
|     -- | ||||
|     -- Combinatorial process. | ||||
|     -- | ||||
|     process (all) is | ||||
|         variable v: regs_type; | ||||
|     begin | ||||
|         -- Load current register values. | ||||
|         v := r; | ||||
| 
 | ||||
|         -- Update min value. | ||||
|         if (reset = '1') | ||||
|                 or (clear = '1') | ||||
|                 or sample_less(in_data, r.min_value) then | ||||
|             v.min_value := in_data; | ||||
|         end if; | ||||
| 
 | ||||
|         -- Update max value. | ||||
|         if (reset = '1') | ||||
|                 or (clear = '1') | ||||
|                 or sample_less(r.max_value, in_data) then | ||||
|             v.max_value := in_data; | ||||
|         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; | ||||
|  | @ -67,6 +67,13 @@ package puzzlefw_pkg is | |||
|     constant reg_trigger_mode:      natural := 16#000240#; | ||||
|     constant reg_trigger_delay:     natural := 16#000244#; | ||||
|     constant reg_trigger_status:    natural := 16#000248#; | ||||
|     constant reg_adc_sample:        natural := 16#000280#; | ||||
|     constant reg_adc23_sample:      natural := 16#000284#; | ||||
|     constant reg_adc_range_clear:   natural := 16#00028c#; | ||||
|     constant reg_adc0_minmax:       natural := 16#000290#; | ||||
|     constant reg_adc1_minmax:       natural := 16#000294#; | ||||
|     constant reg_adc2_minmax:       natural := 16#000298#; | ||||
|     constant reg_adc3_minmax:       natural := 16#00029c#; | ||||
|     constant reg_test_led:          natural := 16#000404#; | ||||
|     constant reg_test_divider:      natural := 16#000408#; | ||||
|     constant reg_dma_buf_addr:      natural := 16#100000#; | ||||
|  | @ -75,7 +82,7 @@ package puzzlefw_pkg is | |||
|     -- Firmware info word. | ||||
|     constant fw_api_version:        natural := 1; | ||||
|     constant fw_version_major:      natural := 0; | ||||
|     constant fw_version_minor:      natural := 3; | ||||
|     constant fw_version_minor:      natural := 4; | ||||
|     constant fw_info_word:          std_logic_vector(31 downto 0) := | ||||
|         x"4a" | ||||
|         & std_logic_vector(to_unsigned(fw_api_version, 8)) | ||||
|  | @ -116,6 +123,7 @@ package puzzlefw_pkg is | |||
|         trig_ext_select:    std_logic_vector(1 downto 0); | ||||
|         trig_ext_falling:   std_logic; | ||||
|         trigger_delay:      std_logic_vector(15 downto 0); | ||||
|         adc_range_clear:    std_logic; | ||||
|         dma_buf_addr:       std_logic_vector(31 downto 12); | ||||
|         dma_buf_size:       std_logic_vector(31 downto 12); | ||||
|     end record; | ||||
|  | @ -132,6 +140,9 @@ package puzzlefw_pkg is | |||
|         acq_addr_ptr:       std_logic_vector(31 downto 3); | ||||
|         acq_channel_busy:   std_logic; | ||||
|         trig_waiting:       std_logic; | ||||
|         adc_sample:         adc_data_array(0 to 3); | ||||
|         adc_min_value:      adc_data_array(0 to 3); | ||||
|         adc_max_value:      adc_data_array(0 to 3); | ||||
|     end record; | ||||
| 
 | ||||
|     constant registers_control_init: registers_control := ( | ||||
|  | @ -162,6 +173,7 @@ package puzzlefw_pkg is | |||
|         trig_ext_select     => (others => '0'), | ||||
|         trig_ext_falling    => '0', | ||||
|         trigger_delay       => (others => '0'), | ||||
|         adc_range_clear     => '0', | ||||
|         dma_buf_addr        => (others => '0'), | ||||
|         dma_buf_size        => (others => '0') | ||||
|     ); | ||||
|  |  | |||
|  | @ -146,6 +146,7 @@ architecture arch of puzzlefw_top is | |||
| 
 | ||||
|     signal s_timestamp:     std_logic_vector(timestamp_bits - 1 downto 0); | ||||
|     signal s_adc_data:      adc_data_array(0 to 1); | ||||
|     signal s_adc_sample:    adc_data_array(0 to 1); | ||||
| 
 | ||||
|     signal s_acq_dma_valid: std_logic; | ||||
|     signal s_acq_dma_ready: std_logic; | ||||
|  | @ -397,17 +398,44 @@ begin | |||
| 
 | ||||
|     -- Capture ADC data. | ||||
|     -- Ignore the 2 LSB bits which are not-connected on the ADC side. | ||||
|     inst_adc_capture1: entity work.adc_capture | ||||
|     inst_capture_gen: for i in 0 to 1 generate | ||||
|         inst_adc_capture: entity work.adc_capture | ||||
|             port map ( | ||||
|                 clk                 => clk_adc, | ||||
|             in_data             => adc_dat_i(0)(15 downto 2), | ||||
|             out_data            => s_adc_data(0) ); | ||||
|                 in_data             => adc_dat_i(i)(15 downto 2), | ||||
|                 out_data            => s_adc_data(i) ); | ||||
|     end generate; | ||||
| 
 | ||||
|     inst_adc_capture2: entity work.adc_capture | ||||
|     -- Optionally generate simulated ADC samples. | ||||
|     inst_adc_sample_stream: entity work.adc_sample_stream | ||||
|         port map ( | ||||
|             clk                 => clk_adc, | ||||
|             in_data             => adc_dat_i(1)(15 downto 2), | ||||
|             out_data            => s_adc_data(1) ); | ||||
|             reset               => s_reset, | ||||
|             simulate            => s_reg_control.simulate_adc, | ||||
|             in_data             => s_adc_data(0 to 1), | ||||
|             out_data            => s_adc_sample(0 to 1) ); | ||||
| 
 | ||||
|     -- Monitor range of ADC samples. | ||||
|     inst_monitor_gen: for i in 0 to 1 generate | ||||
|         inst_range_monitor: entity work.adc_range_monitor | ||||
|             generic map ( | ||||
|                 signed_data         => false ) | ||||
|             port map ( | ||||
|                 clk                 => clk_adc, | ||||
|                 reset               => s_reset, | ||||
|                 clear               => s_reg_control.adc_range_clear, | ||||
|                 in_data             => s_adc_sample(i), | ||||
|                 min_value           => s_reg_status.adc_min_value(i), | ||||
|                 max_value           => s_reg_status.adc_max_value(i) ); | ||||
|     end generate; | ||||
| 
 | ||||
|     -- Monitor current ADC sample value. | ||||
|     s_reg_status.adc_sample(0 to 1) <= s_adc_sample(0 to 1); | ||||
| 
 | ||||
|     -- Drive dummy values to not-implemented channels 2, 3. | ||||
|     s_reg_status.adc_sample(2 to 3) <= (others => (others => '0')); | ||||
|     s_reg_status.adc_min_value(2 to 3) <= (others => (others => '0')); | ||||
|     s_reg_status.adc_max_value(2 to 3) <= (others => (others => '0')); | ||||
| 
 | ||||
|     -- Analog acquisition data chain. | ||||
|     inst_acquisition_chain: entity work.acquisition_chain | ||||
|  | @ -423,14 +451,13 @@ begin | |||
|             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, | ||||
|             adc_data_in         => s_adc_sample, | ||||
|             trig_ext_in         => "0000",  -- TODO | ||||
|             trig_waiting        => s_reg_status.trig_waiting, | ||||
|             out_valid           => s_acq_dma_valid, | ||||
|  |  | |||
|  | @ -75,6 +75,7 @@ begin | |||
|         v.reg_control.acq_channel_init  := '0'; | ||||
|         v.reg_control.acq_intr_clear    := '0'; | ||||
|         v.reg_control.trig_force        := '0'; | ||||
|         v.reg_control.adc_range_clear   := '0'; | ||||
| 
 | ||||
|         -- Respond to each APB access on the next clock cycle (no wait states). | ||||
|         v.pready        := apb_psel and (not apb_penable); | ||||
|  | @ -122,6 +123,24 @@ begin | |||
|                     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_adc_sample => | ||||
|                     v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_sample(0); | ||||
|                     v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_sample(1); | ||||
|                 when reg_adc23_sample => | ||||
|                     v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_sample(2); | ||||
|                     v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_sample(3); | ||||
|                 when reg_adc0_minmax => | ||||
|                     v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(0); | ||||
|                     v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(0); | ||||
|                 when reg_adc1_minmax => | ||||
|                     v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(1); | ||||
|                     v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(1); | ||||
|                 when reg_adc2_minmax => | ||||
|                     v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(2); | ||||
|                     v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(2); | ||||
|                 when reg_adc3_minmax => | ||||
|                     v.prdata(adc_data_bits - 1 downto 0) := reg_status.adc_min_value(3); | ||||
|                     v.prdata(adc_data_bits + 15 downto 16) := reg_status.adc_max_value(3); | ||||
|                 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; | ||||
|  | @ -162,6 +181,7 @@ begin | |||
|                     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_adc_range_clear => v.reg_control.adc_range_clear := apb_pwdata(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); | ||||
|  |  | |||
|  | @ -52,13 +52,7 @@ architecture arch of trigger_detector is | |||
|         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 r: regs_type; | ||||
|     signal rnext: regs_type; | ||||
| 
 | ||||
| begin | ||||
|  | @ -71,7 +65,6 @@ begin | |||
|     -- | ||||
|     process (all) is | ||||
|         variable v: regs_type; | ||||
|         variable v_trig: std_logic; | ||||
|     begin | ||||
|         -- Load current register values. | ||||
|         v := r; | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ 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_range_monitor.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 | ||||
|  |  | |||
|  | @ -51,6 +51,13 @@ | |||
| #define REG_TRIGGER_MODE        0x0240 | ||||
| #define REG_TRIGGER_DELAY       0x0244 | ||||
| #define REG_TRIGGER_STATUS      0x0248 | ||||
| #define REG_ADC_SAMPLE          0x0280 | ||||
| #define REG_ADC23_SAMPLE        0x0284 | ||||
| #define REG_ADC_RANGE_CLEAR     0x028c | ||||
| #define REG_ADC0_MINMAX         0x0290 | ||||
| #define REG_ADC1_MINMAX         0x0294 | ||||
| #define REG_ADC2_MINMAX         0x0298 | ||||
| #define REG_ADC3_MINMAX         0x029c | ||||
| 
 | ||||
| 
 | ||||
| struct puzzlefw_context { | ||||
|  | @ -601,6 +608,19 @@ static void show_status(struct puzzlefw_context *ctx) | |||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_AVERAGING_EN); | ||||
|     printf("    averaging_en =   0x%08x\n", v); | ||||
| 
 | ||||
|     v = puzzlefw_read_reg(ctx, REG_ADC_SAMPLE); | ||||
|     uint32_t adc_range = puzzlefw_read_reg(ctx, REG_ADC0_MINMAX); | ||||
|     printf("    channel 0    =   %5u  (min = %u, max = %u)\n", | ||||
|            v & 0xffff, | ||||
|            adc_range & 0xffff, | ||||
|            (adc_range >> 16) & 0xffff); | ||||
| 
 | ||||
|     adc_range = puzzlefw_read_reg(ctx, REG_ADC1_MINMAX); | ||||
|     printf("    channel 1    =   %5u  (min = %u, max = %u)\n", | ||||
|            (v >> 16) & 0xffff, | ||||
|            adc_range & 0xffff, | ||||
|            (adc_range >> 16) & 0xffff); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1037,6 +1057,7 @@ int main(int argc, char **argv) | |||
|     int set_shift = 0, shift_steps = 0; | ||||
|     int avgon = 0, avgoff = 0; | ||||
|     int simon = 0, simoff = 0; | ||||
|     int rangeclear = 0; | ||||
| 
 | ||||
|     if (argc == 2 && strcmp(argv[1], "show") == 0) { | ||||
|         show = 1; | ||||
|  | @ -1084,6 +1105,8 @@ int main(int argc, char **argv) | |||
|         simon = 1; | ||||
|     } else if (argc == 2 && strcmp(argv[1], "simoff") == 0) { | ||||
|         simoff = 1; | ||||
|     } else if (argc == 2 && strcmp(argv[1], "rangeclear") == 0) { | ||||
|         rangeclear = 1; | ||||
|     } else if (argc == 2 && strcmp(argv[1], "server") == 0) { | ||||
|         server = 1; | ||||
|     } else { | ||||
|  | @ -1152,6 +1175,9 @@ int main(int argc, char **argv) | |||
|             "    testje simoff\n" | ||||
|             "        Use real ADC data.\n" | ||||
|             "\n" | ||||
|             "    testje rangeclear\n" | ||||
|             "        Clear min/max ADC sample monitor.\n" | ||||
|             "\n" | ||||
|             "    testje server\n" | ||||
|             "        Open TCP port 5001 to stream DMA data.\n" | ||||
|             "\n"); | ||||
|  | @ -1251,6 +1277,10 @@ int main(int argc, char **argv) | |||
|         puzzlefw_write_reg(&ctx, REG_SIMULATE_ADC, 0); | ||||
|     } | ||||
| 
 | ||||
|     if (rangeclear) { | ||||
|         puzzlefw_write_reg(&ctx, REG_ADC_RANGE_CLEAR, 1); | ||||
|     } | ||||
| 
 | ||||
|     if (server) { | ||||
|         run_server(&ctx); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue