202 lines
5.7 KiB
VHDL
202 lines
5.7 KiB
VHDL
--
|
|
-- 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;
|