From: Tobias Weber Date: Wed, 10 Jan 2018 10:41:23 +0000 (+0100) Subject: components related to mupix readout. Still Missing Serdes and trigger logic. X-Git-Url: https://jspc29.x-matter.uni-frankfurt.de/git/?a=commitdiff_plain;h=24cc29ff8e5ca62b1c97048c0b721c661e256d50;p=trb3.git components related to mupix readout. Still Missing Serdes and trigger logic. --- diff --git a/mupix/Mupix8/sources/CircularMemory.vhd b/mupix/Mupix8/sources/CircularMemory.vhd index 4544389..eada2cc 100644 --- a/mupix/Mupix8/sources/CircularMemory.vhd +++ b/mupix/Mupix8/sources/CircularMemory.vhd @@ -13,7 +13,8 @@ entity CircularMemory is generic( g_datawidth : integer := 32; -- width of data words g_addresswidth : integer := 10; -- width of address pointers, number of words is 2**g_addresswidth - g_clockspeed : integer := 1e8 -- FPGA clock speed in Hz + g_clockspeed : integer := 1e8; -- FPGA clock speed in Hz + g_boundedbuf : boolean := false -- stop writing when buffer is full (FIFO mode) ); port( clk : in std_logic; -- clock input @@ -21,10 +22,14 @@ entity CircularMemory is wr_en : in std_logic; -- write enable data_in : in std_logic_vector(g_datawidth - 1 downto 0); -- input word rd_en : in std_logic; --read enable + offset_en : in std_logic; -- set offset between write and read pointer + offset : in std_logic_vector(g_addresswidth - 1 downto 0); -- read - write pointer offset data_out : out std_logic_vector(g_datawidth - 1 downto 0); -- output word empty : out std_logic; --empty flag full : out std_logic; --full flag - fillcnt : out std_logic_vector(g_addresswidth - 1 downto 0); -- number of words in circular buffer + almost_empty : out std_logic; --one more word remaining in buffer + almost_full : out std_logic; --one more word to write before buffer overflow + fillcnt : out std_logic_vector(g_addresswidth downto 0); -- number of words in circular buffer inword_freq : out std_logic_vector(31 downto 0); -- number of input words per second outword_freq : out std_logic_vector(31 downto 0) -- number of output words per second ); @@ -48,21 +53,29 @@ architecture RTL of CircularMemory is end component BlockMemory; --counters for write/read frequency - signal ticks_counter : unsigned(f_log2(g_clockspeed) - 1 downto 0) := (others => '0'); - signal inword_counter, outword_counter : unsigned(31 downto 0) := (others => '0'); - + signal ticks_counter : unsigned(f_log2(g_clockspeed) - 1 downto 0) := (others => '0'); + signal inword_counter, outword_counter : unsigned(31 downto 0) := (others => '0'); + --read and write pointers - constant c_high_address : integer := 2**g_addresswidth; - signal readpointer : integer range 0 to c_high_address - 1 := 0; - signal writepointer : integer range 0 to c_high_address - 1 := 0; - signal words_in_buffer : integer range 0 to c_high_address := 0; - + constant c_high_address : integer := 2**g_addresswidth; + signal readpointer : integer range 0 to c_high_address - 1 := 0; + signal writepointer : integer range 0 to c_high_address - 1 := 0; + signal words_in_buffer : integer range 0 to c_high_address := 0; + signal read_write_pointer_offset : integer range -(c_high_address - 1) to c_high_address - 1 := 0; + --wires to block memory - signal WrEn_mem : std_logic; + signal WrEn_mem : std_logic; signal WrAddr_mem : std_logic_vector(g_addresswidth - 1 downto 0); - signal Din_mem : std_logic_vector(g_datawidth - 1 downto 0); + signal Din_mem : std_logic_vector(g_datawidth - 1 downto 0); signal ReAddr_mem : std_logic_vector(g_addresswidth - 1 downto 0); - signal Dout_mem : std_logic_vector(g_datawidth - 1 downto 0); + signal Dout_mem : std_logic_vector(g_datawidth - 1 downto 0); + + signal empty_i : std_logic; + signal full_i : std_logic; + signal rd_en_i : std_logic; + signal wr_en_i : std_logic; + signal increment_wd_cnt : std_logic := '0'; + signal decrement_wd_cnt : std_logic := '0'; begin @@ -84,62 +97,79 @@ begin begin if rising_edge(clk) then if rst = '1' then - writepointer <= 0; + writepointer <= 0; + increment_wd_cnt <= '0'; else - if wr_en = '1' then - WrAddr_mem <= std_logic_vector(to_unsigned(writepointer, g_addresswidth)); - Din_mem <= data_in; - WrEn_mem <= '1'; + if wr_en_i = '1' then + increment_wd_cnt <= '1'; if writepointer = c_high_address - 1 then writepointer <= 0; else writepointer <= writepointer + 1; end if; else - writepointer <= writepointer; - WrAddr_mem <= (others => '0'); - Din_mem <= (others => '0'); - WrEn_mem <= '0'; + writepointer <= writepointer; + increment_wd_cnt <= '0'; end if; end if; end if; end process write_proc; - read_proc : process (clk) is + WrAddr_mem <= std_logic_vector(to_unsigned(writepointer, g_addresswidth)); + Din_mem <= data_in; + WrEn_mem <= wr_en_i; + + read_write_offset_proc : process(offset, writepointer) is + begin + read_write_pointer_offset <= writepointer - to_integer(unsigned(offset)) after 2 ns; + end process read_write_offset_proc; + + read_proc : process(clk) is begin if rising_edge(clk) then if rst = '1' then - readpointer <= 0; + readpointer <= 0; + decrement_wd_cnt <= '0'; else - if rd_en = '1' then - ReAddr_mem <= std_logic_vector(to_unsigned(readpointer, g_addresswidth)); + if offset_en = '1' then + if read_write_pointer_offset >= 0 then + readpointer <= read_write_pointer_offset; + else + readpointer <= c_high_address + read_write_pointer_offset; + end if; + elsif rd_en_i = '1' then + decrement_wd_cnt <= '1'; if readpointer = c_high_address - 1 then readpointer <= 0; else readpointer <= readpointer + 1; end if; else - readpointer <= readpointer; - ReAddr_mem <= (others => '0'); + decrement_wd_cnt <= '0'; + readpointer <= readpointer; end if; end if; end if; end process read_proc; - word_counter_proc : process (clk) is + ReAddr_mem <= std_logic_vector(to_unsigned(readpointer, g_addresswidth)); + + word_counter_proc : process(clk) is begin if rising_edge(clk) then if rst = '1' then words_in_buffer <= 0; + elsif offset_en = '1' then + words_in_buffer <= to_integer(unsigned(offset)); else words_in_buffer <= words_in_buffer; - if wr_en = '1' and rd_en = '0' then + if increment_wd_cnt = '1' and decrement_wd_cnt = '0' then if words_in_buffer < c_high_address then words_in_buffer <= words_in_buffer + 1; else words_in_buffer <= words_in_buffer; end if; - elsif wr_en = '0' and rd_en = '1' then + elsif increment_wd_cnt = '0' and decrement_wd_cnt = '1' then if words_in_buffer > 0 then words_in_buffer <= words_in_buffer - 1; else @@ -166,10 +196,10 @@ begin end if; if ticks_counter = g_clockspeed - 1 then ticks_counter <= (others => '0'); - inword_freq <= std_logic_vector(inword_counter); - outword_freq <= std_logic_vector(outword_counter); inword_counter <= (others => '0'); outword_counter <= (others => '0'); + inword_freq <= std_logic_vector(inword_counter); + outword_freq <= std_logic_vector(outword_counter); else ticks_counter <= ticks_counter + 1; end if; @@ -177,11 +207,16 @@ begin end if; end process throughput_proc; + empty_i <= '1' when words_in_buffer = 0 else '0'; + full_i <= '1' when words_in_buffer = c_high_address else '0'; + rd_en_i <= not empty_i and rd_en; + wr_en_i <= (wr_en and not full_i) when g_boundedbuf = true else wr_en; - - empty <= '1' after 1 ns when words_in_buffer = 0 else '0' after 1 ns; - full <= '1' after 1 ns when words_in_buffer = c_high_address else '0' after 1 ns; - fillcnt <= std_logic_vector(to_unsigned(words_in_buffer, g_addresswidth)); - data_out <= Dout_mem; + empty <= empty_i; + almost_empty <= '1' when (words_in_buffer < 3) else '0'; + full <= full_i; + almost_full <= '1' when words_in_buffer >= c_high_address - 1 else '0'; + fillcnt <= std_logic_vector(to_unsigned(words_in_buffer, g_addresswidth + 1)); + data_out <= Dout_mem; end architecture RTL; diff --git a/mupix/Mupix8/sources/MupixTRBReadout.vhd b/mupix/Mupix8/sources/MupixTRBReadout.vhd new file mode 100644 index 0000000..910bac4 --- /dev/null +++ b/mupix/Mupix8/sources/MupixTRBReadout.vhd @@ -0,0 +1,366 @@ +----------------------------------------------------------------------------------- +-- Entities for Mupix Readout with slow control access to TRB slow control +-- Tobias Weber +-- Ruhr Unversitaet Bochum +------------------------------------------------------------------------------------ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity MupixTRBReadout is + generic(g_mupix_links : natural := 4; -- number of input data channels from mupix + g_cyc_mem_address_width : integer := 13; -- memory depth of circular buffer + g_datawidth : integer := 32 -- width of data words + ); + port( + clk : in std_logic; -- clock input + rst : in std_logic; -- reset input + -- input fifo signals (maybe we need word width conversion somewhere ?) + fifo_empty : in std_logic_vector(g_mupix_links - 1 downto 0); + fifo_full : in std_logic_vector(g_mupix_links - 1 downto 0); + fifo_datain : in std_logic_vector(g_mupix_links*g_datawidth - 1 downto 0); + fifo_rden : out std_logic_vector(g_mupix_links - 1 downto 0); + -- readout and trigger + trb_trigger : in std_logic; -- signal from trb to start readout + dataout : out std_logic_vector(g_datawidth - 1 downto 0); -- data to TRB data channel + data_valid : out std_logic; -- output data is valid + busy : out std_logic; -- readout controller is busy + -- TRB slow Control channel + SLV_READ_IN : in std_logic; + SLV_WRITE_IN : in std_logic; + SLV_DATA_OUT : out std_logic_vector(31 downto 0); + SLV_DATA_IN : in std_logic_vector(31 downto 0); + SLV_ADDR_IN : in std_logic_vector(15 downto 0); + SLV_ACK_OUT : out std_logic; + SLV_NO_MORE_DATA_OUT : out std_logic; + SLV_UNKNOWN_ADDR_OUT : out std_logic); +end entity MupixTRBReadout; + + +architecture rtl of MupixTRBReadout is + + component CircularMemory + generic( + g_datawidth : integer := 32; + g_addresswidth : integer := 10; + g_clockspeed : integer := 1e8; + g_boundedbuf : boolean := false + ); + port( + clk : in std_logic; + rst : in std_logic; + wr_en : in std_logic; + data_in : in std_logic_vector(g_datawidth - 1 downto 0); + rd_en : in std_logic; + offset_en : in std_logic; + offset : in std_logic_vector(g_addresswidth - 1 downto 0); + data_out : out std_logic_vector(g_datawidth - 1 downto 0); + empty : out std_logic; + full : out std_logic; + almost_empty : out std_logic; + almost_full : out std_logic; + fillcnt : out std_logic_vector(g_addresswidth downto 0); + inword_freq : out std_logic_vector(31 downto 0); + outword_freq : out std_logic_vector(31 downto 0) + ); + end component CircularMemory; + + component FiFoDataMux + generic( + g_datawidth : integer := 32; + g_inputs : integer := 4; + g_clockspeed : integer := 1e8 + ); + port( + clk : in std_logic; + rst : in std_logic; + fifo_empty : in std_logic_vector(g_inputs - 1 downto 0); + fifo_full : in std_logic_vector(g_inputs - 1 downto 0); + fifo_datain : in std_logic_vector(g_inputs*g_datawidth - 1 downto 0); + fifo_mask : in std_logic_vector(g_inputs - 1 downto 0); + fifo_rden : out std_logic_vector(g_inputs - 1 downto 0); + buff_wren : out std_logic; + dataout : out std_logic_vector(g_datawidth - 1 downto 0); + wordin_freq : out std_logic_vector(32*g_inputs - 1 downto 0); + fifo_full_o : out std_logic + ); + end component FiFoDataMux; + + component ReadoutController + generic( + g_datawidth : integer := 32; + g_addresswidth : integer := 10 + ); + port( + clk : in std_logic; + rst : in std_logic; + start : in std_logic; + mode : in std_logic_vector(1 downto 0); + writes_after_trig : in std_logic_vector(g_addresswidth - 1 downto 0); + max_words : in std_logic_vector(g_addresswidth - 1 downto 0); + data_in : in std_logic_vector(g_datawidth - 1 downto 0); + empty : in std_logic; + almost_empty : in std_logic; + sensor_id : in std_logic_vector(g_datawidth - 1 downto 0); + offset_en : out std_logic; + rd_en : out std_logic; + busy : out std_logic; + data_valid : out std_logic; + data_out : out std_logic_vector(g_datawidth - 1 downto 0) + ); + end component ReadoutController; + + signal readout_mode_i : std_logic_vector(1 downto 0) := "00"; + signal readout_writes_aft_trig : std_logic_vector(g_cyc_mem_address_width - 1 downto 0) := (others => '0'); + signal readout_words : std_logic_vector(g_cyc_mem_address_width - 1 downto 0) := (others => '0'); + signal sensor_id : std_logic_vector(31 downto 0) := (others => '0'); + signal multiplexer_mask : std_logic_vector(g_mupix_links - 1 downto 0) := (others => '0'); + signal multiplexer_channel_sel : integer range 0 to g_mupix_links - 1 := 0; + signal wordin_freq : std_logic_vector(32*g_mupix_links - 1 downto 0) := (others => '0'); + signal fifo_full_o : std_logic; + + signal cycl_fillcnt : std_logic_vector(g_cyc_mem_address_width downto 0); + signal cycl_inword_freq : std_logic_vector(31 downto 0); + signal cycl_outword_freq : std_logic_vector(31 downto 0); + signal cycl_empty : std_logic; + signal cycl_full : std_logic; + signal cycl_almost_empty : std_logic; + + signal fifo_mux_wren : std_logic; + signal fifo_mux_data_out : std_logic_vector(g_datawidth - 1 downto 0); + + signal start_readout_slow_to_buffer : std_logic := '0'; + signal start_readout : std_logic := '0'; + signal readout_controller_data_in : std_logic_vector(g_datawidth - 1 downto 0); + signal readout_controller_data_out : std_logic_vector(g_datawidth - 1 downto 0); + signal readout_controller_busy : std_logic; + signal readout_controller_offset_en : std_logic; + signal readout_controller_rd_en : std_logic; + signal readout_controller_data_valid : std_logic; + + type slow_readout_fsm_type is (idle, waitstate); + signal slow_readout_fsm : slow_readout_fsm_type := idle; + signal slow_data : std_logic_vector(g_datawidth - 1 downto 0) := (others => '0'); + signal start_slow_read : std_logic := '0'; + signal slow_read_busy : std_logic := '0'; + signal slow_read_done : std_logic := '0'; + +begin + + start_readout <= start_readout_slow_to_buffer or trb_trigger; + + data_mux_1 : entity work.FiFoDataMux + generic map( + g_datawidth => g_datawidth, + g_inputs => g_mupix_links, + g_clockspeed => 1e8 + ) + port map( + clk => clk, + rst => rst, + fifo_empty => fifo_empty, + fifo_full => fifo_full, + fifo_datain => fifo_datain, + fifo_mask => multiplexer_mask, + fifo_rden => fifo_rden, + buff_wren => fifo_mux_wren, + dataout => fifo_mux_data_out, + wordin_freq => wordin_freq, + fifo_full_o => fifo_full_o + ); + + cycl_buffer_1 : entity work.CircularMemory + generic map( + g_datawidth => g_datawidth, + g_addresswidth => g_cyc_mem_address_width, + g_clockspeed => 1e8, + g_boundedbuf => false + ) + port map( + clk => clk, + rst => rst, + wr_en => fifo_mux_wren, + data_in => fifo_mux_data_out, + rd_en => readout_controller_rd_en, + offset_en => readout_controller_offset_en, + offset => readout_words, + data_out => readout_controller_data_in, + empty => cycl_empty, + full => cycl_full, + almost_empty => cycl_almost_empty, + almost_full => open, + fillcnt => cycl_fillcnt, + inword_freq => cycl_inword_freq, + outword_freq => cycl_outword_freq + ); + + readout_controller_1 : entity work.ReadoutController + generic map( + g_datawidth => g_datawidth, + g_addresswidth => g_cyc_mem_address_width + ) + port map( + clk => clk, + rst => rst, + start => start_readout, + mode => readout_mode_i, + writes_after_trig => readout_writes_aft_trig, + max_words => readout_words, + data_in => readout_controller_data_in, + empty => cycl_empty, + almost_empty => cycl_almost_empty, + sensor_id => sensor_id, + offset_en => readout_controller_offset_en, + rd_en => readout_controller_rd_en, + busy => readout_controller_busy, + data_valid => readout_controller_data_valid, + data_out => readout_controller_data_out + ); + + data_read_slow : process (clk) is + begin + if rising_edge(clk) then + if rst = '1' then + slow_readout_fsm <= idle; + slow_read_done <= '0'; + slow_read_busy <= '0'; + start_readout_slow_to_buffer <= '0'; + else + start_readout_slow_to_buffer <= '0'; + slow_read_done <= '0'; + case slow_readout_fsm is + when idle => + slow_readout_fsm <= idle; + slow_read_busy <= '0'; + if start_slow_read = '1' then + start_readout_slow_to_buffer <= '1'; + slow_read_busy <= '1'; + slow_data <= (others => '0'); + slow_readout_fsm <= waitstate; + end if; + when waitstate => + slow_read_busy <= '1'; + if readout_controller_data_valid = '1' then + slow_read_done <= '1'; + slow_data <= readout_controller_data_out; + slow_readout_fsm <= idle; + end if; + end case; + end if; + end if; + end process data_read_slow; + + + ------------------------------------------------------------------------------------ + --Slow Control Handling + ------------------------------------------------------------------------------------ + --0x100: readout controller mode + --0x101: readout controller writes after trigger + --0x102: readout controller numbers of words to readout + --0x103: readout controller sensor id + --0x104: readout controller start slow readout (read only) + --0x105: fifo multiplexer mask + --0x106: fifo multiplexer input frequency (write select channel, read frequency) + --0x107: fifo multiplexer full (read-only) + --0x108: circular memory empty/full flags, fill cnt (read-only) + --0x109: circular memory input word frequency (read-only) + --0x10a: circular memory output word frequency (read-only) + ----------------------------------------------------------------------------------- + slv_bus_handler : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + SLV_DATA_OUT <= (others => '0'); + SLV_ACK_OUT <= '0'; + SLV_NO_MORE_DATA_OUT <= '0'; + SLV_UNKNOWN_ADDR_OUT <= '0'; + start_slow_read <= '0'; + else + SLV_DATA_OUT <= (others => '0'); + SLV_ACK_OUT <= '0'; + SLV_NO_MORE_DATA_OUT <= '0'; + SLV_UNKNOWN_ADDR_OUT <= '0'; + start_slow_read <= '0'; + + if slow_read_busy = '1' then + if slow_read_done = '1' then + SLV_DATA_OUT <= slow_data; + SLV_ACK_OUT <= '1'; + end if; + + elsif SLV_WRITE_IN = '1' then + case SLV_ADDR_IN is + when x"0100" => + readout_mode_i <= SLV_DATA_IN(1 downto 0); + SLV_ACK_OUT <= '1'; + when x"0101" => + readout_writes_aft_trig <= SLV_DATA_IN(g_cyc_mem_address_width - 1 downto 0); + SLV_ACK_OUT <= '1'; + when x"0102" => + readout_words <= SLV_DATA_IN(g_cyc_mem_address_width - 1 downto 0); + SLV_ACK_OUT <= '1'; + when x"0103" => + sensor_id <= SLV_DATA_IN; + SLV_ACK_OUT <= '1'; + when x"0105" => + multiplexer_mask <= SLV_DATA_IN(g_mupix_links - 1 downto 0); + SLV_ACK_OUT <= '1'; + when x"0106" => + multiplexer_channel_sel <= to_integer(unsigned(SLV_DATA_IN)); + SLV_ACK_OUT <= '1'; + when others => + slv_unknown_addr_out <= '1'; + end case; + elsif SLV_READ_IN = '1' then + case SLV_ADDR_IN is + when x"0100" => + SLV_DATA_OUT(1 downto 0) <= readout_mode_i; + SLV_ACK_OUT <= '1'; + when x"0101" => + SLV_DATA_OUT(g_cyc_mem_address_width - 1 downto 0) <= readout_writes_aft_trig; + SLV_ACK_OUT <= '1'; + when x"0102" => + SLV_DATA_OUT(g_cyc_mem_address_width - 1 downto 0) <= readout_words; + SLV_ACK_OUT <= '1'; + when x"0103" => + SLV_DATA_OUT <= sensor_id; + SLV_ACK_OUT <= '1'; + when x"0104" => + if cycl_empty = '0' and readout_mode_i = "00" then + start_slow_read <= '1'; + else + SLV_NO_MORE_DATA_OUT <= '1'; + end if; + when x"0105" => + SLV_DATA_OUT(g_mupix_links - 1 downto 0) <= multiplexer_mask; + SLV_ACK_OUT <= '1'; + when x"0106" => + SLV_DATA_OUT <= wordin_freq((multiplexer_channel_sel + 1)*32 - 1 downto multiplexer_channel_sel*32); + SLV_ACK_OUT <= '1'; + when x"0107" => + SLV_DATA_OUT(0) <= fifo_full_o; + SLV_ACK_OUT <= '1'; + when x"0108" => + SLV_DATA_OUT(g_cyc_mem_address_width downto 0) <= cycl_fillcnt; + SLV_DATA_OUT(g_cyc_mem_address_width + 1) <= cycl_empty; + SLV_DATA_OUT(g_cyc_mem_address_width + 2) <= cycl_full; + SLV_ACK_OUT <= '1'; + when x"0109" => + SLV_DATA_OUT <= cycl_inword_freq; + SLV_ACK_OUT <= '1'; + when x"010a" => + SLV_DATA_OUT <= cycl_outword_freq; + SLV_ACK_OUT <= '1'; + when others => + slv_unknown_addr_out <= '1'; + end case; + end if; + end if; + end if; + end process slv_bus_handler; + + busy <= readout_controller_busy; + dataout <= readout_controller_data_out; + data_valid <= readout_controller_data_valid; + +end architecture; diff --git a/mupix/Mupix8/sources/ReadoutController.vhd b/mupix/Mupix8/sources/ReadoutController.vhd new file mode 100644 index 0000000..c1e2016 --- /dev/null +++ b/mupix/Mupix8/sources/ReadoutController.vhd @@ -0,0 +1,212 @@ +----------------------------------------------------------------------------------- +-- Readout Controller for Circular Memory +-- Tobias Weber +-- Ruhr Unversitaet Bochum +------------------------------------------------------------------------------------ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +----------------------------------------------------------------------------- +-- mode = 0: read single word from buffer, return 0x0 if empty +-- the frame header and trailer are missing in this mode +-- mode = 1: read until buffer empty or maximum of words to read +-- mode = 2: wait for words after trigger, set read offset and start reading +-- note that full/empty flags are reset in this mode +----------------------------------------------------------------------------- + +entity ReadoutController is + generic( + g_datawidth : integer := 32; -- width of data words + g_addresswidth : integer := 10 -- width of address pointers, number of words is 2**g_addresswidth + ); + port( + clk : in std_logic; -- clock input + rst : in std_logic; -- reset input + start : in std_logic; -- start reading + mode : in std_logic_vector(1 downto 0); -- readout mode + writes_after_trig : in std_logic_vector(g_addresswidth - 1 downto 0); -- clock cycles after trigger before starting readout (needs to be at least two) + max_words : in std_logic_vector(g_addresswidth - 1 downto 0); -- maximum number of words to read + data_in : in std_logic_vector(g_datawidth - 1 downto 0); -- data from buffer + empty : in std_logic; --empty flag from circular buffer + almost_empty : in std_logic; --empty flag from circular buffer + sensor_id : in std_logic_vector(g_datawidth - 1 downto 0); + offset_en : out std_logic; -- set offset between read and write pointer (offset value is set directly in circular buffer) + rd_en : out std_logic; -- read enable to circular buffer + busy : out std_logic; -- readout controller busy + data_valid : out std_logic; -- data from readout controller valid + data_out : out std_logic_vector(g_datawidth - 1 downto 0)); -- data from readout controller +end entity ReadoutController; + +architecture RTL of ReadoutController is + + constant c_frame_start : std_logic_vector(g_datawidth - 1 downto 0) := x"FABEABBA"; + constant c_frame_end : std_logic_vector(g_datawidth - 1 downto 0) := x"BEEFBEEF"; + + --readout state type, in mode 1, 2 a header and trailer will be put + --at beginning and end of readout frame + type t_readout_state_type is (idle, header, read_single, read, wait_memory, wait_trigger, trailer); + signal readout_fsm : t_readout_state_type := idle; + + signal wordcounter : unsigned(g_addresswidth - 1 downto 0) := (others => '0'); + signal framecounter : unsigned(31 downto 0) := (others => '0'); + signal headercounter : integer range 0 to 2 := 0; + signal trailercounter : integer range 0 to 1 := 0; + signal triggerwaitcounter : unsigned(g_addresswidth - 1 downto 0) := (others => '0'); + signal almost_empty_edge : std_logic_vector(1 downto 0) := (others => '0'); + +begin + + edge_detection : process (clk) is + begin + if rising_edge(clk) then + if rst = '1' then + almost_empty_edge <= (others => '0'); + else + almost_empty_edge <= almost_empty_edge(0) & almost_empty; + end if; + end if; + end process edge_detection; + + + readout_proc : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + readout_fsm <= idle; + rd_en <= '0'; + busy <= '0'; + data_valid <= '0'; + data_out <= (others => '0'); + framecounter <= (others => '0'); + headercounter <= 0; + else + busy <= '0'; + data_valid <= '0'; + data_out <= (others => '0'); + rd_en <= '0'; + offset_en <= '0'; + case readout_fsm is + when idle => + trailercounter <= 0; + wordcounter <= (others => '0'); + headercounter <= 0; + triggerwaitcounter <= (others => '0'); + headercounter <= 0; + if start = '1' then + if mode = "00" then + if empty = '0' then + readout_fsm <= read_single; + else + data_out <= (others => '0'); + data_valid <= '1'; + readout_fsm <= idle; + end if; + busy <= '1'; + elsif mode = "01" then + readout_fsm <= header; + busy <= '1'; + elsif mode = "10" then + readout_fsm <= header; + busy <= '1'; + else + readout_fsm <= idle; + end if; + else + readout_fsm <= idle; + end if; + when header => + busy <= '1'; + data_valid <= '1'; + headercounter <= headercounter + 1; + case headercounter is + when 0 => + data_out <= c_frame_start; + when 1 => + data_out <= sensor_id; + when 2 => + data_out <= std_logic_vector(framecounter); + framecounter <= framecounter + 1; + if mode = "01" then + if empty = '0' then + readout_fsm <= wait_memory; + rd_en <= '1'; + else + readout_fsm <= trailer; + end if; + else + readout_fsm <= wait_trigger; + end if; + end case; + when read_single => + data_valid <= '1'; + busy <= '1'; + rd_en <= '1'; + data_out <= data_in; + readout_fsm <= idle; + when wait_memory => + busy <= '1'; + if empty = '0' then + readout_fsm <= read; + rd_en <= '1'; + else + readout_fsm <= trailer; + rd_en <= '0'; + end if; + when read => + busy <= '1'; + data_valid <= '1'; + data_out <= data_in; + wordcounter <= wordcounter + 1; + readout_fsm <= read; + if mode = "01" then + if wordcounter < unsigned(max_words) - 2 and almost_empty = '0' then + rd_en <= '1'; + else + rd_en <= '0'; + end if; + if almost_empty_edge = "01" or wordcounter = unsigned(max_words) - 1 then + readout_fsm <= trailer; + end if; + else + if wordcounter < unsigned(max_words) - 2 and almost_empty = '0' then + rd_en <= '1'; + else + rd_en <= '0'; + end if; + if almost_empty_edge = "01" or wordcounter = unsigned(max_words) - 1 then + readout_fsm <= trailer; + end if; + end if; + when wait_trigger => + busy <= '1'; + offset_en <= '0'; + triggerwaitcounter <= triggerwaitcounter + 1; + if triggerwaitcounter = unsigned(writes_after_trig) - 1 then + if empty = '0' then + readout_fsm <= wait_memory; + rd_en <= '1'; + else + readout_fsm <= trailer; + end if; + end if; + if triggerwaitcounter = unsigned(writes_after_trig) - 2 then + offset_en <= '1'; + end if; + when trailer => + busy <= '1'; + data_valid <= '1'; + readout_fsm <= trailer; + trailercounter <= trailercounter + 1; + if trailercounter = 0 then + data_out(g_addresswidth - 1 downto 0) <= std_logic_vector(wordcounter); + else + data_out <= c_frame_end; + readout_fsm <= idle; + end if; + end case; + end if; + end if; + end process readout_proc; + +end architecture RTL; diff --git a/mupix/Mupix8/tb/CircularMemoryTest.vhd b/mupix/Mupix8/tb/CircularMemoryTest.vhd index 2388f99..1d2268f 100644 --- a/mupix/Mupix8/tb/CircularMemoryTest.vhd +++ b/mupix/Mupix8/tb/CircularMemoryTest.vhd @@ -18,9 +18,13 @@ architecture sim of CircularMemoryTest is wr_en : in std_logic; data_in : in std_logic_vector(g_datawidth - 1 downto 0); rd_en : in std_logic; + offset_en : in std_logic; + offset : in std_logic_vector(g_addresswidth - 1 downto 0); data_out : out std_logic_vector(g_datawidth - 1 downto 0); empty : out std_logic; + almost_empty : out std_logic; full : out std_logic; + almost_full : out std_logic; fillcnt : out std_logic_vector(g_addresswidth - 1 downto 0); inword_freq : out std_logic_vector(31 downto 0); outword_freq : out std_logic_vector(31 downto 0) @@ -36,10 +40,14 @@ architecture sim of CircularMemoryTest is signal wr_en : std_logic := '0'; signal data_in : std_logic_vector(c_data_width - 1 downto 0) := (others => '0'); signal rd_en : std_logic := '0'; + signal offset_en : std_logic := '0'; + signal offset : std_logic_vector(c_address_width - 1 downto 0) := (others => '0'); signal data_out : std_logic_vector(c_data_width - 1 downto 0); signal empty : std_logic; + signal almost_empty : std_logic; signal full : std_logic; - signal fillcnt : std_logic_vector(c_address_width - 1 downto 0); + signal almost_full : std_logic; + signal fillcnt : std_logic_vector(c_address_width downto 0); signal inword_freq : std_logic_vector(31 downto 0); signal outword_freq : std_logic_vector(31 downto 0); @@ -57,9 +65,13 @@ begin wr_en => wr_en, data_in => data_in, rd_en => rd_en, + offset_en => offset_en, + offset => offset, data_out => data_out, empty => empty, + almost_empty => almost_empty, full => full, + almost_full => almost_full, fillcnt => fillcnt, inword_freq => inword_freq, outword_freq => outword_freq @@ -76,18 +88,35 @@ begin stimulus : process is begin wait for 100 ns; - for i in 1 to 7 loop + --general read/write test + for i in 1 to 15 loop wr_en <= '1'; data_in <= std_logic_vector(to_unsigned(i, c_data_width)); wait for c_clk_period; end loop; wr_en <= '0'; wait for 50 ns; - for i in 1 to 7 loop + for i in 1 to 9 loop rd_en <= '1'; wait for c_clk_period; end loop; rd_en <= '0'; + --test offset for read pointer + wait for 100 ns; + offset <= std_logic_vector(to_unsigned(4, c_address_width)); + wait for c_clk_period; + offset_en <= '1'; + wait for c_clk_period; + offset_en <= '0'; + for i in 1 to 3 loop + wr_en <= '1'; + data_in <= std_logic_vector(to_unsigned(i, c_data_width)); + wait for c_clk_period; + end loop; + wr_en <= '0'; + offset_en <= '1'; + wait for c_clk_period; + offset_en <= '0'; wait; end process stimulus; diff --git a/mupix/Mupix8/tb/MupixTRBReadoutTest.vhd b/mupix/Mupix8/tb/MupixTRBReadoutTest.vhd new file mode 100644 index 0000000..67610f9 --- /dev/null +++ b/mupix/Mupix8/tb/MupixTRBReadoutTest.vhd @@ -0,0 +1,178 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.TRBSimulationPkg.all; + +entity MupixTRBReadoutTest is +end entity MupixTRBReadoutTest; + +architecture sim of MupixTRBReadoutTest is + + component MupixTRBReadout + generic( + g_mupix_links : natural := 4; + g_cyc_mem_address_width : integer := 13; + g_datawidth : integer := 32 + ); + port( + clk : in std_logic; + rst : in std_logic; + fifo_empty : in std_logic_vector(g_mupix_links - 1 downto 0); + fifo_full : in std_logic_vector(g_mupix_links - 1 downto 0); + fifo_datain : in std_logic_vector(g_mupix_links*g_datawidth - 1 downto 0); + fifo_rden : out std_logic_vector(g_mupix_links - 1 downto 0); + trb_trigger : in std_logic; + dataout : out std_logic_vector(g_datawidth - 1 downto 0); + data_valid : out std_logic; + busy : out std_logic; + SLV_READ_IN : in std_logic; + SLV_WRITE_IN : in std_logic; + SLV_DATA_OUT : out std_logic_vector(31 downto 0); + SLV_DATA_IN : in std_logic_vector(31 downto 0); + SLV_ADDR_IN : in std_logic_vector(15 downto 0); + SLV_ACK_OUT : out std_logic; + SLV_NO_MORE_DATA_OUT : out std_logic; + SLV_UNKNOWN_ADDR_OUT : out std_logic + ); + end component MupixTRBReadout; + + component STD_FIFO + generic( + DATA_WIDTH : positive := 8; + FIFO_DEPTH : positive := 256 + ); + port( + CLK : in std_logic; + RST : in std_logic; + WriteEn : in std_logic; + DataIn : in std_logic_vector(DATA_WIDTH - 1 downto 0); + ReadEn : in std_logic; + DataOut : out std_logic_vector(DATA_WIDTH - 1 downto 0); + Empty : out std_logic; + Full : out std_logic + ); + end component STD_FIFO; + + constant c_clk_period : time := 10 ns; + constant c_mupix_links : integer := 4; + constant c_cyc_mem_address_width : integer := 6; + constant c_datawidth : integer := 32; + + signal clk : std_logic; + signal rst : std_logic := '0'; + signal fifo_empty : std_logic_vector(c_mupix_links - 1 downto 0) := (others => '1'); + signal fifo_full : std_logic_vector(c_mupix_links - 1 downto 0) := (others => '0'); + signal fifo_data_to_mux : std_logic_vector(c_mupix_links*c_datawidth - 1 downto 0) := (others => '0'); + signal fifo_rden : std_logic_vector(c_mupix_links - 1 downto 0) := (others => '0'); + signal trb_trigger : std_logic := '0'; + signal dataout : std_logic_vector(c_datawidth - 1 downto 0); + signal data_valid : std_logic; + signal busy : std_logic; + signal SLV_READ_IN : std_logic := '0'; + signal SLV_WRITE_IN : std_logic := '0'; + signal SLV_DATA_OUT : std_logic_vector(31 downto 0); + signal SLV_DATA_IN : std_logic_vector(31 downto 0) := (others => '0'); + signal SLV_ADDR_IN : std_logic_vector(15 downto 0) := (others => '0'); + signal SLV_ACK_OUT : std_logic; + signal SLV_NO_MORE_DATA_OUT : std_logic; + signal SLV_UNKNOWN_ADDR_OUT : std_logic; + + signal fifo_write_en : std_logic_vector(c_mupix_links - 1 downto 0) := (others => '0'); + signal fifo_datain : std_logic_vector(c_mupix_links*c_datawidth - 1 downto 0) := (others => '0'); + +begin + + + dut : entity work.MupixTRBReadout + generic map( + g_mupix_links => c_mupix_links, + g_cyc_mem_address_width => c_cyc_mem_address_width, + g_datawidth => c_datawidth + ) + port map( + clk => clk, + rst => rst, + fifo_empty => fifo_empty, + fifo_full => fifo_full, + fifo_datain => fifo_data_to_mux, + fifo_rden => fifo_rden, + trb_trigger => trb_trigger, + dataout => dataout, + data_valid => data_valid, + busy => busy, + SLV_READ_IN => SLV_READ_IN, + SLV_WRITE_IN => SLV_WRITE_IN, + SLV_DATA_OUT => SLV_DATA_OUT, + SLV_DATA_IN => SLV_DATA_IN, + SLV_ADDR_IN => SLV_ADDR_IN, + SLV_ACK_OUT => SLV_ACK_OUT, + SLV_NO_MORE_DATA_OUT => SLV_NO_MORE_DATA_OUT, + SLV_UNKNOWN_ADDR_OUT => SLV_UNKNOWN_ADDR_OUT + ); + + gen_input_fifo : for i in 0 to 3 generate + input_fifo : entity work.STD_FIFO + generic map( + DATA_WIDTH => c_datawidth, + FIFO_DEPTH => 16 + ) + port map( + CLK => CLK, + RST => RST, + WriteEn => fifo_write_en(i), + DataIn => fifo_datain((i + 1)*c_datawidth - 1 downto i*c_datawidth), + ReadEn => fifo_rden(i), + DataOut => fifo_data_to_mux((i + 1)*c_datawidth - 1 downto i*c_datawidth), + Empty => fifo_empty(i), + Full => fifo_full(i) + ); + end generate gen_input_fifo; + + + clock_gen : process is + begin + clk <= '1'; + wait for c_clk_period/2; + clk <= '0'; + wait for c_clk_period/2; + end process clock_gen; + + stimulus : process is + begin + wait for 100 ns; + TRBRegisterWrite(SLV_WRITE_IN, SLV_DATA_IN, SLV_ADDR_IN, x"0000000A", x"0101"); + TRBRegisterWrite(SLV_WRITE_IN, SLV_DATA_IN, SLV_ADDR_IN, x"0000000B", x"0102"); + TRBRegisterWrite(SLV_WRITE_IN, SLV_DATA_IN, SLV_ADDR_IN, x"ABBA0001", x"0103"); + TRBRegisterWrite(SLV_WRITE_IN, SLV_DATA_IN, SLV_ADDR_IN, x"0000000F", x"0105"); + TRBRegisterWrite(SLV_WRITE_IN, SLV_DATA_IN, SLV_ADDR_IN, x"00000000", x"0106"); + wait for 5*c_clk_period; + for i in 1 to 10 loop + fifo_write_en <= (others => '1'); + fifo_datain(4*32 - 1 downto 3*32) <= std_logic_vector(to_unsigned(i, c_datawidth)); + fifo_datain(3*32 - 1 downto 2*32) <= std_logic_vector(to_unsigned(i + 10, c_datawidth)); + fifo_datain(2*32 - 1 downto 1*32) <= std_logic_vector(to_unsigned(i + 20, c_datawidth)); + fifo_datain(1*32 - 1 downto 0*32) <= std_logic_vector(to_unsigned(i + 30, c_datawidth)); + wait for c_clk_period; + end loop; + fifo_write_en <= (others => '0'); + fifo_datain(4*32 - 1 downto 3*32) <= (others => '0'); + wait for 500 ns; + TRBRegisterWrite(SLV_WRITE_IN, SLV_DATA_IN, SLV_ADDR_IN, x"00000000", x"0100"); + TRBRegisterRead(SLV_READ_IN, SLV_DATA_IN, SLV_ADDR_IN, x"00000000", x"0104"); + if SLV_ACK_OUT = '0' then + wait until slv_ack_out = '1'; + wait for c_clk_period; + end if; + TRBRegisterRead(SLV_READ_IN, SLV_DATA_IN, SLV_ADDR_IN, x"00000000", x"0104"); + if SLV_ACK_OUT = '0' then + wait until slv_ack_out = '1'; + wait for c_clk_period; + end if; + TRBRegisterWrite(SLV_WRITE_IN, SLV_DATA_IN, SLV_ADDR_IN, x"00000001", x"0100"); + TRBRegisterRead(SLV_READ_IN, SLV_DATA_IN, SLV_ADDR_IN, x"00000000", x"0104"); + wait; + end process stimulus; + + +end architecture; \ No newline at end of file diff --git a/mupix/Mupix8/tb/ReadoutControllerTest.vhd b/mupix/Mupix8/tb/ReadoutControllerTest.vhd new file mode 100644 index 0000000..a512454 --- /dev/null +++ b/mupix/Mupix8/tb/ReadoutControllerTest.vhd @@ -0,0 +1,229 @@ +---------------------------------------------------------- +-- Test bench for Circular Memory Readout Controller +-- Tobias Weber +-- Ruhr Universtitaet Bochum +---------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity ReadoutControllerTest is +end entity ReadoutControllerTest; + +architecture sim of ReadoutControllerTest is + +constant c_datawidth : integer := 32; +constant c_addresswidth : integer := 6; +constant c_clockspeed : integer := 1e8; +constant c_clk_period : time := 10 ns; + +component CircularMemory + generic( + g_datawidth : integer := 32; + g_addresswidth : integer := 10; + g_clockspeed : integer := 1e8 + ); + port( + clk : in std_logic; + rst : in std_logic; + wr_en : in std_logic; + data_in : in std_logic_vector(g_datawidth - 1 downto 0); + rd_en : in std_logic; + offset_en : in std_logic; + offset : in std_logic_vector(g_addresswidth - 1 downto 0); + data_out : out std_logic_vector(g_datawidth - 1 downto 0); + empty : out std_logic; + almost_empty : out std_logic; + full : out std_logic; + almost_full : out std_logic; + fillcnt : out std_logic_vector(g_addresswidth downto 0); + inword_freq : out std_logic_vector(31 downto 0); + outword_freq : out std_logic_vector(31 downto 0) + ); + end component CircularMemory; + +component ReadoutController + generic( + g_datawidth : integer := 32; + g_addresswidth : integer := 10 + ); + port( + clk : in std_logic; + rst : in std_logic; + start : in std_logic; + mode : in std_logic_vector(1 downto 0); + writes_after_trig : in std_logic_vector(g_addresswidth - 1 downto 0); + max_words : in std_logic_vector(g_addresswidth - 1 downto 0); + data_in : in std_logic_vector(g_datawidth - 1 downto 0); + empty : in std_logic; + almost_empty : in std_logic; + sensor_id : in std_logic_vector(g_datawidth - 1 downto 0); + offset_en : out std_logic; + rd_en : out std_logic; + busy : out std_logic; + data_valid : out std_logic; + data_out : out std_logic_vector(g_datawidth - 1 downto 0) + ); + end component ReadoutController; + + signal clk : std_logic; + signal rst : std_logic := '0'; + signal wr_en : std_logic := '0'; + signal data_in : std_logic_vector(c_datawidth - 1 downto 0) := (others => '0'); + signal rd_en : std_logic; + signal offset_en : std_logic; + signal offset : std_logic_vector(c_addresswidth - 1 downto 0); + signal data_out : std_logic_vector(c_datawidth - 1 downto 0); + signal empty : std_logic; + signal almost_empty : std_logic; + signal full : std_logic; + signal fillcnt : std_logic_vector(c_addresswidth downto 0); + signal inword_freq : std_logic_vector(31 downto 0); + signal outword_freq : std_logic_vector(31 downto 0); + signal start : std_logic := '0'; + signal mode : std_logic_vector(1 downto 0) := (others => '0'); + signal writes_after_trig : std_logic_vector(c_addresswidth - 1 downto 0) := (others => '0'); + signal max_words : std_logic_vector(c_addresswidth - 1 downto 0) := (others => '0'); + signal sensor_id : std_logic_vector(c_datawidth - 1 downto 0) := (others => '0'); + signal busy : std_logic; + signal data_valid : std_logic; + signal data_out_ctrl : std_logic_vector(c_datawidth - 1 downto 0); + +begin + + memory : entity work.CircularMemory + generic map( + g_datawidth => c_datawidth, + g_addresswidth => c_addresswidth, + g_clockspeed => c_clockspeed + ) + port map( + clk => clk, + rst => rst, + wr_en => wr_en, + data_in => data_in, + rd_en => rd_en, + offset_en => offset_en, + offset => offset, + data_out => data_out, + empty => empty, + almost_empty => almost_empty, + full => full, + almost_full => open, + fillcnt => fillcnt, + inword_freq => inword_freq, + outword_freq => outword_freq + ); + + controller : entity work.ReadoutController + generic map( + g_datawidth => c_datawidth, + g_addresswidth => c_addresswidth + ) + port map( + clk => clk, + rst => rst, + start => start, + mode => mode, + writes_after_trig => writes_after_trig, + max_words => max_words, + data_in => data_out, + empty => empty, + almost_empty => almost_empty, + sensor_id => sensor_id, + offset_en => offset_en, + rd_en => rd_en, + busy => busy, + data_valid => data_valid, + data_out => data_out_ctrl + ); + + clk_gen : process is + begin + clk <= '1'; + wait for c_clk_period/2; + clk <= '0'; + wait for c_clk_period/2; + end process clk_gen; + + stimulus : process is + begin + wait for 100 ns; + sensor_id <= x"ABBA0001"; + max_words <= std_logic_vector(to_unsigned(10, c_addresswidth)); + offset <= std_logic_vector(to_unsigned(10, c_addresswidth)); + writes_after_trig <= std_logic_vector(to_unsigned(4, c_addresswidth)); + wait for c_clk_period/2; + + wait for c_clk_period; + --try reading from empty circular memory + start <= '1'; + wait for c_clk_period; + start <= '0'; + wait for 5*c_clk_period; + start <= '1'; + mode <= "01"; + wait for c_clk_period; + start <= '0'; + wait for 10*c_clk_period; + --fill the circular memory + for i in 1 to 22 loop + wr_en <= '1'; + data_in <= std_logic_vector(to_unsigned(i + 10, c_datawidth)); + wait for c_clk_period; + end loop; + wr_en <= '0'; + --try different read modes again + --read two single words + wait for c_clk_period; + start <= '1'; + mode <= "00"; + wait for c_clk_period; + start <= '0'; + wait for 5*c_clk_period; + start <= '1'; + wait for c_clk_period; + start <= '0'; + --read given number of words + wait for 5*c_clk_period; + start <= '1'; + mode <= "01"; + wait for c_clk_period; + start <= '0'; + if busy = '1' then + wait until busy = '0'; + wait for 5*c_clk_period; + end if; + --read single word + mode <= "00"; + start <= '1'; + wait for c_clk_period; + start <= '0'; + wait for 5*c_clk_period; + --and read given number of words again + mode <= "01"; + start <= '1'; + wait for c_clk_period; + start <= '0'; + if busy = '1' then + wait until busy = '0'; + wait for 5*c_clk_period; + end if; + wait for 50 ns; + -- trigger window test + for i in 1 to 78 loop + wr_en <= '1'; + data_in <= std_logic_vector(to_unsigned(i + 10, c_datawidth)); + wait for c_clk_period; + end loop; + wr_en <= '0'; + wait for 5*c_clk_period; + mode <= "10"; + start <= '1'; + wait for c_clk_period; + start <= '0'; + wait; + end process stimulus; + + +end architecture sim; \ No newline at end of file