Add monitoring of ADC sample and min/max range

This commit is contained in:
Joris van Rantwijk 2024-08-26 23:11:16 +02:00
parent 716d16e6a3
commit 393d87f9d2
8 changed files with 221 additions and 53 deletions

View File

@ -54,9 +54,6 @@ entity acquisition_chain is
-- Ignored if num_channels == 2. -- Ignored if num_channels == 2.
ch4_mode: in std_logic; 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. -- High to enable automatic (continuous) triggering.
trig_auto_en: in std_logic; trig_auto_en: in std_logic;
@ -142,25 +139,6 @@ begin
sample_integrate => s_sample_integrate, sample_integrate => s_sample_integrate,
sample_done => s_sample_done ); 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. -- Shift and decimation chains for each ADC channel.
inst_channels: for i in 0 to num_channels - 1 generate inst_channels: for i in 0 to num_channels - 1 generate
@ -172,7 +150,7 @@ begin
signed_data => false ) signed_data => false )
port map ( port map (
clk => clk, clk => clk,
in_data => s_adc_sample(i), in_data => adc_data_in(i),
in_shift => shift_steps, in_shift => shift_steps,
out_data => s_adc_shifted(i) ); out_data => s_adc_shifted(i) );

View File

@ -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;

View File

@ -67,6 +67,13 @@ package puzzlefw_pkg is
constant reg_trigger_mode: natural := 16#000240#; constant reg_trigger_mode: natural := 16#000240#;
constant reg_trigger_delay: natural := 16#000244#; constant reg_trigger_delay: natural := 16#000244#;
constant reg_trigger_status: natural := 16#000248#; 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_led: natural := 16#000404#;
constant reg_test_divider: natural := 16#000408#; constant reg_test_divider: natural := 16#000408#;
constant reg_dma_buf_addr: natural := 16#100000#; constant reg_dma_buf_addr: natural := 16#100000#;
@ -75,7 +82,7 @@ package puzzlefw_pkg is
-- Firmware info word. -- Firmware info word.
constant fw_api_version: natural := 1; constant fw_api_version: natural := 1;
constant fw_version_major: natural := 0; 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) := constant fw_info_word: std_logic_vector(31 downto 0) :=
x"4a" x"4a"
& std_logic_vector(to_unsigned(fw_api_version, 8)) & 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_select: std_logic_vector(1 downto 0);
trig_ext_falling: std_logic; trig_ext_falling: std_logic;
trigger_delay: std_logic_vector(15 downto 0); trigger_delay: std_logic_vector(15 downto 0);
adc_range_clear: std_logic;
dma_buf_addr: std_logic_vector(31 downto 12); dma_buf_addr: std_logic_vector(31 downto 12);
dma_buf_size: std_logic_vector(31 downto 12); dma_buf_size: std_logic_vector(31 downto 12);
end record; end record;
@ -132,6 +140,9 @@ package puzzlefw_pkg is
acq_addr_ptr: std_logic_vector(31 downto 3); acq_addr_ptr: std_logic_vector(31 downto 3);
acq_channel_busy: std_logic; acq_channel_busy: std_logic;
trig_waiting: 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; end record;
constant registers_control_init: registers_control := ( constant registers_control_init: registers_control := (
@ -162,6 +173,7 @@ package puzzlefw_pkg is
trig_ext_select => (others => '0'), trig_ext_select => (others => '0'),
trig_ext_falling => '0', trig_ext_falling => '0',
trigger_delay => (others => '0'), trigger_delay => (others => '0'),
adc_range_clear => '0',
dma_buf_addr => (others => '0'), dma_buf_addr => (others => '0'),
dma_buf_size => (others => '0') dma_buf_size => (others => '0')
); );

View File

@ -146,6 +146,7 @@ architecture arch of puzzlefw_top is
signal s_timestamp: std_logic_vector(timestamp_bits - 1 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_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_valid: std_logic;
signal s_acq_dma_ready: std_logic; signal s_acq_dma_ready: std_logic;
@ -397,17 +398,44 @@ begin
-- Capture ADC data. -- Capture ADC data.
-- Ignore the 2 LSB bits which are not-connected on the ADC side. -- 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 ( port map (
clk => clk_adc, clk => clk_adc,
in_data => adc_dat_i(0)(15 downto 2), in_data => adc_dat_i(i)(15 downto 2),
out_data => s_adc_data(0) ); 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 ( port map (
clk => clk_adc, clk => clk_adc,
in_data => adc_dat_i(1)(15 downto 2), reset => s_reset,
out_data => s_adc_data(1) ); 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. -- Analog acquisition data chain.
inst_acquisition_chain: entity work.acquisition_chain inst_acquisition_chain: entity work.acquisition_chain
@ -423,14 +451,13 @@ begin
averaging => s_reg_control.averaging_en, averaging => s_reg_control.averaging_en,
shift_steps => s_reg_control.shift_steps, shift_steps => s_reg_control.shift_steps,
ch4_mode => s_reg_control.ch4_mode, ch4_mode => s_reg_control.ch4_mode,
simulate_adc => s_reg_control.simulate_adc,
trig_auto_en => s_reg_control.trig_auto_en, trig_auto_en => s_reg_control.trig_auto_en,
trig_ext_en => s_reg_control.trig_ext_en, trig_ext_en => s_reg_control.trig_ext_en,
trig_force => s_reg_control.trig_force, trig_force => s_reg_control.trig_force,
trig_ext_select => s_reg_control.trig_ext_select, trig_ext_select => s_reg_control.trig_ext_select,
trig_ext_falling => s_reg_control.trig_ext_falling, trig_ext_falling => s_reg_control.trig_ext_falling,
timestamp_in => s_timestamp, timestamp_in => s_timestamp,
adc_data_in => s_adc_data, adc_data_in => s_adc_sample,
trig_ext_in => "0000", -- TODO trig_ext_in => "0000", -- TODO
trig_waiting => s_reg_status.trig_waiting, trig_waiting => s_reg_status.trig_waiting,
out_valid => s_acq_dma_valid, out_valid => s_acq_dma_valid,

View File

@ -75,6 +75,7 @@ begin
v.reg_control.acq_channel_init := '0'; v.reg_control.acq_channel_init := '0';
v.reg_control.acq_intr_clear := '0'; v.reg_control.acq_intr_clear := '0';
v.reg_control.trig_force := '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). -- Respond to each APB access on the next clock cycle (no wait states).
v.pready := apb_psel and (not apb_penable); v.pready := apb_psel and (not apb_penable);
@ -122,6 +123,24 @@ begin
v.prdata(7) := r.reg_control.trig_ext_falling; 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_delay => v.prdata(15 downto 0) := r.reg_control.trigger_delay;
when reg_trigger_status => v.prdata(0) := reg_status.trig_waiting; 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_led => v.prdata(7 downto 0) := r.reg_control.test_led;
when reg_test_divider => v.prdata(15 downto 0) := r.reg_control.test_divider; when reg_test_divider => v.prdata(15 downto 0) := r.reg_control.test_divider;
when reg_dma_buf_addr => v.prdata(31 downto 12) := r.reg_control.dma_buf_addr; when reg_dma_buf_addr => v.prdata(31 downto 12) := r.reg_control.dma_buf_addr;
@ -162,6 +181,7 @@ begin
v.reg_control.trig_ext_falling := apb_pwdata(7); v.reg_control.trig_ext_falling := apb_pwdata(7);
v.reg_control.trig_force := apb_pwdata(8); v.reg_control.trig_force := apb_pwdata(8);
when reg_trigger_delay => v.reg_control.trigger_delay := apb_pwdata(15 downto 0); 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_led => v.reg_control.test_led := apb_pwdata(7 downto 0);
when reg_test_divider => v.reg_control.test_divider := apb_pwdata(15 downto 0); when reg_test_divider => v.reg_control.test_divider := apb_pwdata(15 downto 0);
when reg_dma_buf_addr => v.reg_control.dma_buf_addr := apb_pwdata(31 downto 12); when reg_dma_buf_addr => v.reg_control.dma_buf_addr := apb_pwdata(31 downto 12);

View File

@ -52,13 +52,7 @@ architecture arch of trigger_detector is
trig_out: std_logic; trig_out: std_logic;
end record; end record;
constant regs_init: regs_type := ( signal r: regs_type;
prev_level => '0',
ext_trig => '0',
trig_out => '0'
);
signal r: regs_type := regs_init;
signal rnext: regs_type; signal rnext: regs_type;
begin begin
@ -71,7 +65,6 @@ begin
-- --
process (all) is process (all) is
variable v: regs_type; variable v: regs_type;
variable v_trig: std_logic;
begin begin
-- Load current register values. -- Load current register values.
v := r; v := r;

View File

@ -23,6 +23,7 @@ read_vhdl -vhdl2008 ../rtl/acquisition_chain.vhd
read_vhdl -vhdl2008 ../rtl/acquisition_manager.vhd read_vhdl -vhdl2008 ../rtl/acquisition_manager.vhd
read_vhdl -vhdl2008 ../rtl/acquisition_stream.vhd read_vhdl -vhdl2008 ../rtl/acquisition_stream.vhd
read_vhdl -vhdl2008 ../rtl/adc_capture.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/adc_sample_stream.vhd
read_vhdl -vhdl2008 ../rtl/dma_axi_master.vhd read_vhdl -vhdl2008 ../rtl/dma_axi_master.vhd
read_vhdl -vhdl2008 ../rtl/dma_write_channel.vhd read_vhdl -vhdl2008 ../rtl/dma_write_channel.vhd

View File

@ -51,6 +51,13 @@
#define REG_TRIGGER_MODE 0x0240 #define REG_TRIGGER_MODE 0x0240
#define REG_TRIGGER_DELAY 0x0244 #define REG_TRIGGER_DELAY 0x0244
#define REG_TRIGGER_STATUS 0x0248 #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 { struct puzzlefw_context {
@ -601,6 +608,19 @@ static void show_status(struct puzzlefw_context *ctx)
v = puzzlefw_read_reg(ctx, REG_AVERAGING_EN); v = puzzlefw_read_reg(ctx, REG_AVERAGING_EN);
printf(" averaging_en = 0x%08x\n", v); 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 set_shift = 0, shift_steps = 0;
int avgon = 0, avgoff = 0; int avgon = 0, avgoff = 0;
int simon = 0, simoff = 0; int simon = 0, simoff = 0;
int rangeclear = 0;
if (argc == 2 && strcmp(argv[1], "show") == 0) { if (argc == 2 && strcmp(argv[1], "show") == 0) {
show = 1; show = 1;
@ -1084,6 +1105,8 @@ int main(int argc, char **argv)
simon = 1; simon = 1;
} else if (argc == 2 && strcmp(argv[1], "simoff") == 0) { } else if (argc == 2 && strcmp(argv[1], "simoff") == 0) {
simoff = 1; simoff = 1;
} else if (argc == 2 && strcmp(argv[1], "rangeclear") == 0) {
rangeclear = 1;
} else if (argc == 2 && strcmp(argv[1], "server") == 0) { } else if (argc == 2 && strcmp(argv[1], "server") == 0) {
server = 1; server = 1;
} else { } else {
@ -1152,6 +1175,9 @@ int main(int argc, char **argv)
" testje simoff\n" " testje simoff\n"
" Use real ADC data.\n" " Use real ADC data.\n"
"\n" "\n"
" testje rangeclear\n"
" Clear min/max ADC sample monitor.\n"
"\n"
" testje server\n" " testje server\n"
" Open TCP port 5001 to stream DMA data.\n" " Open TCP port 5001 to stream DMA data.\n"
"\n"); "\n");
@ -1251,6 +1277,10 @@ int main(int argc, char **argv)
puzzlefw_write_reg(&ctx, REG_SIMULATE_ADC, 0); puzzlefw_write_reg(&ctx, REG_SIMULATE_ADC, 0);
} }
if (rangeclear) {
puzzlefw_write_reg(&ctx, REG_ADC_RANGE_CLEAR, 1);
}
if (server) { if (server) {
run_server(&ctx); run_server(&ctx);
} }