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