redpitaya-puzzlefw/fpga/rtl/acquisition_stream.vhd

203 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 := ch4_mode;
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';
v.out_valid := '0';
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;