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