]> jspc29.x-matter.uni-frankfurt.de Git - cri.git/commitdiff
hub_test: Add TRB parser
authorThomas Gessler <Thomas.Gessler@exp2.physik.uni-giessen.de>
Fri, 4 Sep 2020 14:22:06 +0000 (16:22 +0200)
committerThomas Gessler <Thomas.Gessler@exp2.physik.uni-giessen.de>
Fri, 4 Sep 2020 14:57:52 +0000 (16:57 +0200)
hub_test/constrs/debug.xdc
hub_test/hub_test.xpr
hub_test/src/hub_test.vhd
hub_test/src/trb_parser.vhd [new file with mode: 0644]

index 5eea18ea14d16208cd07ae5680ab5eb4debc45bb..fe30e4d54cec1b75c9f6840fca714cccbcd0a36e 100644 (file)
@@ -32,6 +32,26 @@ create_debug_port u_ila_0 probe
 set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe5]
 set_property port_width 72 [get_debug_ports u_ila_0/probe5]
 connect_debug_port u_ila_0/probe5 [get_nets [list {hub_data_seqnmbr[0]} {hub_data_seqnmbr[1]} {hub_data_seqnmbr[2]} {hub_data_seqnmbr[3]} {hub_data_seqnmbr[4]} {hub_data_seqnmbr[5]} {hub_data_seqnmbr[6]} {hub_data_seqnmbr[7]} {hub_data_seqnmbr[8]} {hub_data_seqnmbr[9]} {hub_data_seqnmbr[10]} {hub_data_seqnmbr[11]} {hub_data_seqnmbr[12]} {hub_data_seqnmbr[13]} {hub_data_seqnmbr[14]} {hub_data_seqnmbr[15]} {hub_data_seqnmbr[16]} {hub_data_seqnmbr[17]} {hub_data_seqnmbr[18]} {hub_data_seqnmbr[19]} {hub_data_seqnmbr[20]} {hub_data_seqnmbr[21]} {hub_data_seqnmbr[22]} {hub_data_seqnmbr[23]} {hub_data_seqnmbr[24]} {hub_data_seqnmbr[25]} {hub_data_seqnmbr[26]} {hub_data_seqnmbr[27]} {hub_data_seqnmbr[28]} {hub_data_seqnmbr[29]} {hub_data_seqnmbr[30]} {hub_data_seqnmbr[31]} {hub_data_seqnmbr[32]} {hub_data_seqnmbr[33]} {hub_data_seqnmbr[34]} {hub_data_seqnmbr[35]} {hub_data_seqnmbr[36]} {hub_data_seqnmbr[37]} {hub_data_seqnmbr[38]} {hub_data_seqnmbr[39]} {hub_data_seqnmbr[40]} {hub_data_seqnmbr[41]} {hub_data_seqnmbr[42]} {hub_data_seqnmbr[43]} {hub_data_seqnmbr[44]} {hub_data_seqnmbr[45]} {hub_data_seqnmbr[46]} {hub_data_seqnmbr[47]} {hub_data_seqnmbr[48]} {hub_data_seqnmbr[49]} {hub_data_seqnmbr[50]} {hub_data_seqnmbr[51]} {hub_data_seqnmbr[52]} {hub_data_seqnmbr[53]} {hub_data_seqnmbr[54]} {hub_data_seqnmbr[55]} {hub_data_seqnmbr[56]} {hub_data_seqnmbr[57]} {hub_data_seqnmbr[58]} {hub_data_seqnmbr[59]} {hub_data_seqnmbr[60]} {hub_data_seqnmbr[61]} {hub_data_seqnmbr[62]} {hub_data_seqnmbr[63]} {hub_data_seqnmbr[64]} {hub_data_seqnmbr[65]} {hub_data_seqnmbr[66]} {hub_data_seqnmbr[67]} {hub_data_seqnmbr[68]} {hub_data_seqnmbr[69]} {hub_data_seqnmbr[70]} {hub_data_seqnmbr[71]}]]
+create_debug_port u_ila_0 probe
+set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe6]
+set_property port_width 288 [get_debug_ports u_ila_0/probe6]
+connect_debug_port u_ila_0/probe6 [get_nets [list {trb_data_tdata[0]} {trb_data_tdata[1]} {trb_data_tdata[2]} {trb_data_tdata[3]} {trb_data_tdata[4]} {trb_data_tdata[5]} {trb_data_tdata[6]} {trb_data_tdata[7]} {trb_data_tdata[8]} {trb_data_tdata[9]} {trb_data_tdata[10]} {trb_data_tdata[11]} {trb_data_tdata[12]} {trb_data_tdata[13]} {trb_data_tdata[14]} {trb_data_tdata[15]} {trb_data_tdata[16]} {trb_data_tdata[17]} {trb_data_tdata[18]} {trb_data_tdata[19]} {trb_data_tdata[20]} {trb_data_tdata[21]} {trb_data_tdata[22]} {trb_data_tdata[23]} {trb_data_tdata[24]} {trb_data_tdata[25]} {trb_data_tdata[26]} {trb_data_tdata[27]} {trb_data_tdata[28]} {trb_data_tdata[29]} {trb_data_tdata[30]} {trb_data_tdata[31]} {trb_data_tdata[32]} {trb_data_tdata[33]} {trb_data_tdata[34]} {trb_data_tdata[35]} {trb_data_tdata[36]} {trb_data_tdata[37]} {trb_data_tdata[38]} {trb_data_tdata[39]} {trb_data_tdata[40]} {trb_data_tdata[41]} {trb_data_tdata[42]} {trb_data_tdata[43]} {trb_data_tdata[44]} {trb_data_tdata[45]} {trb_data_tdata[46]} {trb_data_tdata[47]} {trb_data_tdata[48]} {trb_data_tdata[49]} {trb_data_tdata[50]} {trb_data_tdata[51]} {trb_data_tdata[52]} {trb_data_tdata[53]} {trb_data_tdata[54]} {trb_data_tdata[55]} {trb_data_tdata[56]} {trb_data_tdata[57]} {trb_data_tdata[58]} {trb_data_tdata[59]} {trb_data_tdata[60]} {trb_data_tdata[61]} {trb_data_tdata[62]} {trb_data_tdata[63]} {trb_data_tdata[64]} {trb_data_tdata[65]} {trb_data_tdata[66]} {trb_data_tdata[67]} {trb_data_tdata[68]} {trb_data_tdata[69]} {trb_data_tdata[70]} {trb_data_tdata[71]} {trb_data_tdata[72]} {trb_data_tdata[73]} {trb_data_tdata[74]} {trb_data_tdata[75]} {trb_data_tdata[76]} {trb_data_tdata[77]} {trb_data_tdata[78]} {trb_data_tdata[79]} {trb_data_tdata[80]} {trb_data_tdata[81]} {trb_data_tdata[82]} {trb_data_tdata[83]} {trb_data_tdata[84]} {trb_data_tdata[85]} {trb_data_tdata[86]} {trb_data_tdata[87]} {trb_data_tdata[88]} {trb_data_tdata[89]} {trb_data_tdata[90]} {trb_data_tdata[91]} {trb_data_tdata[92]} {trb_data_tdata[93]} {trb_data_tdata[94]} {trb_data_tdata[95]} {trb_data_tdata[96]} {trb_data_tdata[97]} {trb_data_tdata[98]} {trb_data_tdata[99]} {trb_data_tdata[100]} {trb_data_tdata[101]} {trb_data_tdata[102]} {trb_data_tdata[103]} {trb_data_tdata[104]} {trb_data_tdata[105]} {trb_data_tdata[106]} {trb_data_tdata[107]} {trb_data_tdata[108]} {trb_data_tdata[109]} {trb_data_tdata[110]} {trb_data_tdata[111]} {trb_data_tdata[112]} {trb_data_tdata[113]} {trb_data_tdata[114]} {trb_data_tdata[115]} {trb_data_tdata[116]} {trb_data_tdata[117]} {trb_data_tdata[118]} {trb_data_tdata[119]} {trb_data_tdata[120]} {trb_data_tdata[121]} {trb_data_tdata[122]} {trb_data_tdata[123]} {trb_data_tdata[124]} {trb_data_tdata[125]} {trb_data_tdata[126]} {trb_data_tdata[127]} {trb_data_tdata[128]} {trb_data_tdata[129]} {trb_data_tdata[130]} {trb_data_tdata[131]} {trb_data_tdata[132]} {trb_data_tdata[133]} {trb_data_tdata[134]} {trb_data_tdata[135]} {trb_data_tdata[136]} {trb_data_tdata[137]} {trb_data_tdata[138]} {trb_data_tdata[139]} {trb_data_tdata[140]} {trb_data_tdata[141]} {trb_data_tdata[142]} {trb_data_tdata[143]} {trb_data_tdata[144]} {trb_data_tdata[145]} {trb_data_tdata[146]} {trb_data_tdata[147]} {trb_data_tdata[148]} {trb_data_tdata[149]} {trb_data_tdata[150]} {trb_data_tdata[151]} {trb_data_tdata[152]} {trb_data_tdata[153]} {trb_data_tdata[154]} {trb_data_tdata[155]} {trb_data_tdata[156]} {trb_data_tdata[157]} {trb_data_tdata[158]} {trb_data_tdata[159]} {trb_data_tdata[160]} {trb_data_tdata[161]} {trb_data_tdata[162]} {trb_data_tdata[163]} {trb_data_tdata[164]} {trb_data_tdata[165]} {trb_data_tdata[166]} {trb_data_tdata[167]} {trb_data_tdata[168]} {trb_data_tdata[169]} {trb_data_tdata[170]} {trb_data_tdata[171]} {trb_data_tdata[172]} {trb_data_tdata[173]} {trb_data_tdata[174]} {trb_data_tdata[175]} {trb_data_tdata[176]} {trb_data_tdata[177]} {trb_data_tdata[178]} {trb_data_tdata[179]} {trb_data_tdata[180]} {trb_data_tdata[181]} {trb_data_tdata[182]} {trb_data_tdata[183]} {trb_data_tdata[184]} {trb_data_tdata[185]} {trb_data_tdata[186]} {trb_data_tdata[187]} {trb_data_tdata[188]} {trb_data_tdata[189]} {trb_data_tdata[190]} {trb_data_tdata[191]} {trb_data_tdata[192]} {trb_data_tdata[193]} {trb_data_tdata[194]} {trb_data_tdata[195]} {trb_data_tdata[196]} {trb_data_tdata[197]} {trb_data_tdata[198]} {trb_data_tdata[199]} {trb_data_tdata[200]} {trb_data_tdata[201]} {trb_data_tdata[202]} {trb_data_tdata[203]} {trb_data_tdata[204]} {trb_data_tdata[205]} {trb_data_tdata[206]} {trb_data_tdata[207]} {trb_data_tdata[208]} {trb_data_tdata[209]} {trb_data_tdata[210]} {trb_data_tdata[211]} {trb_data_tdata[212]} {trb_data_tdata[213]} {trb_data_tdata[214]} {trb_data_tdata[215]} {trb_data_tdata[216]} {trb_data_tdata[217]} {trb_data_tdata[218]} {trb_data_tdata[219]} {trb_data_tdata[220]} {trb_data_tdata[221]} {trb_data_tdata[222]} {trb_data_tdata[223]} {trb_data_tdata[224]} {trb_data_tdata[225]} {trb_data_tdata[226]} {trb_data_tdata[227]} {trb_data_tdata[228]} {trb_data_tdata[229]} {trb_data_tdata[230]} {trb_data_tdata[231]} {trb_data_tdata[232]} {trb_data_tdata[233]} {trb_data_tdata[234]} {trb_data_tdata[235]} {trb_data_tdata[236]} {trb_data_tdata[237]} {trb_data_tdata[238]} {trb_data_tdata[239]} {trb_data_tdata[240]} {trb_data_tdata[241]} {trb_data_tdata[242]} {trb_data_tdata[243]} {trb_data_tdata[244]} {trb_data_tdata[245]} {trb_data_tdata[246]} {trb_data_tdata[247]} {trb_data_tdata[248]} {trb_data_tdata[249]} {trb_data_tdata[250]} {trb_data_tdata[251]} {trb_data_tdata[252]} {trb_data_tdata[253]} {trb_data_tdata[254]} {trb_data_tdata[255]} {trb_data_tdata[256]} {trb_data_tdata[257]} {trb_data_tdata[258]} {trb_data_tdata[259]} {trb_data_tdata[260]} {trb_data_tdata[261]} {trb_data_tdata[262]} {trb_data_tdata[263]} {trb_data_tdata[264]} {trb_data_tdata[265]} {trb_data_tdata[266]} {trb_data_tdata[267]} {trb_data_tdata[268]} {trb_data_tdata[269]} {trb_data_tdata[270]} {trb_data_tdata[271]} {trb_data_tdata[272]} {trb_data_tdata[273]} {trb_data_tdata[274]} {trb_data_tdata[275]} {trb_data_tdata[276]} {trb_data_tdata[277]} {trb_data_tdata[278]} {trb_data_tdata[279]} {trb_data_tdata[280]} {trb_data_tdata[281]} {trb_data_tdata[282]} {trb_data_tdata[283]} {trb_data_tdata[284]} {trb_data_tdata[285]} {trb_data_tdata[286]} {trb_data_tdata[287]}]]
+create_debug_port u_ila_0 probe
+set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe7]
+set_property port_width 36 [get_debug_ports u_ila_0/probe7]
+connect_debug_port u_ila_0/probe7 [get_nets [list {trb_data_tkeep[0]} {trb_data_tkeep[1]} {trb_data_tkeep[2]} {trb_data_tkeep[3]} {trb_data_tkeep[4]} {trb_data_tkeep[5]} {trb_data_tkeep[6]} {trb_data_tkeep[7]} {trb_data_tkeep[8]} {trb_data_tkeep[9]} {trb_data_tkeep[10]} {trb_data_tkeep[11]} {trb_data_tkeep[12]} {trb_data_tkeep[13]} {trb_data_tkeep[14]} {trb_data_tkeep[15]} {trb_data_tkeep[16]} {trb_data_tkeep[17]} {trb_data_tkeep[18]} {trb_data_tkeep[19]} {trb_data_tkeep[20]} {trb_data_tkeep[21]} {trb_data_tkeep[22]} {trb_data_tkeep[23]} {trb_data_tkeep[24]} {trb_data_tkeep[25]} {trb_data_tkeep[26]} {trb_data_tkeep[27]} {trb_data_tkeep[28]} {trb_data_tkeep[29]} {trb_data_tkeep[30]} {trb_data_tkeep[31]} {trb_data_tkeep[32]} {trb_data_tkeep[33]} {trb_data_tkeep[34]} {trb_data_tkeep[35]}]]
+create_debug_port u_ila_0 probe
+set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe8]
+set_property port_width 9 [get_debug_ports u_ila_0/probe8]
+connect_debug_port u_ila_0/probe8 [get_nets [list {trb_data_tlast[0]} {trb_data_tlast[1]} {trb_data_tlast[2]} {trb_data_tlast[3]} {trb_data_tlast[4]} {trb_data_tlast[5]} {trb_data_tlast[6]} {trb_data_tlast[7]} {trb_data_tlast[8]}]]
+create_debug_port u_ila_0 probe
+set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe9]
+set_property port_width 9 [get_debug_ports u_ila_0/probe9]
+connect_debug_port u_ila_0/probe9 [get_nets [list {trb_data_tready[0]} {trb_data_tready[1]} {trb_data_tready[2]} {trb_data_tready[3]} {trb_data_tready[4]} {trb_data_tready[5]} {trb_data_tready[6]} {trb_data_tready[7]} {trb_data_tready[8]}]]
+create_debug_port u_ila_0 probe
+set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe10]
+set_property port_width 9 [get_debug_ports u_ila_0/probe10]
+connect_debug_port u_ila_0/probe10 [get_nets [list {trb_data_tvalid[0]} {trb_data_tvalid[1]} {trb_data_tvalid[2]} {trb_data_tvalid[3]} {trb_data_tvalid[4]} {trb_data_tvalid[5]} {trb_data_tvalid[6]} {trb_data_tvalid[7]} {trb_data_tvalid[8]}]]
 set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]
 set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]
 set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]
index 01bb521faa9a20c485d9378e531435cfe02a3f04..a219765e2b63deba2d7672f820013a69eeb37138 100644 (file)
           <Attr Name="UsedIn" Val="simulation"/>
         </FileInfo>
       </File>
+      <File Path="$PPRDIR/src/trb_parser.vhd">
+        <FileInfo>
+          <Attr Name="UsedIn" Val="synthesis"/>
+          <Attr Name="UsedIn" Val="simulation"/>
+        </FileInfo>
+      </File>
       <File Path="$PPRDIR/../../trbnet/xilinx/xcku/read_dna_address.vhd">
         <FileInfo>
           <Attr Name="UsedIn" Val="synthesis"/>
index 48c12ea2377b4253d1bfea83954078635dcef7b3..2b4f3558c9ed084595106591b9ceaaa4747145e9 100644 (file)
@@ -146,6 +146,12 @@ architecture behavioral of hub_test is
     signal hub_data_seqnmbr : std_logic_vector(((INTERFACE_NUM - 1) * 8) - 1 downto 0);
     signal hub_data_length : std_logic_vector(((INTERFACE_NUM - 1) * c_DATA_WIDTH) - 1 downto 0);
 
+    signal trb_data_tvalid : std_logic_vector(INTERFACE_NUM - 2 downto 0);
+    signal trb_data_tready : std_logic_vector(INTERFACE_NUM - 2 downto 0) := (others => '1');
+    signal trb_data_tdata : std_logic_vector((INTERFACE_NUM - 1) * 32 - 1 downto 0);
+    signal trb_data_tkeep : std_logic_vector((INTERFACE_NUM - 1) * 4 - 1 downto 0);
+    signal trb_data_tlast : std_logic_vector(INTERFACE_NUM - 2 downto 0);
+
     attribute MARK_DEBUG : string;
     attribute KEEP : string;
 
@@ -155,6 +161,11 @@ architecture behavioral of hub_test is
     attribute MARK_DEBUG of hub_data_address_sender : signal is "true";
     attribute MARK_DEBUG of hub_data_seqnmbr : signal is "true";
     attribute MARK_DEBUG of hub_data_length : signal is "true";
+    attribute MARK_DEBUG of trb_data_tvalid : signal is "true";
+    attribute MARK_DEBUG of trb_data_tready : signal is "true";
+    attribute MARK_DEBUG of trb_data_tdata : signal is "true";
+    attribute MARK_DEBUG of trb_data_tkeep : signal is "true";
+    attribute MARK_DEBUG of trb_data_tlast : signal is "true";
 
     attribute KEEP of hub_data_active : signal is "true";
     attribute KEEP of hub_data_out : signal is "true";
@@ -162,6 +173,11 @@ architecture behavioral of hub_test is
     attribute KEEP of hub_data_address_sender : signal is "true";
     attribute KEEP of hub_data_seqnmbr : signal is "true";
     attribute KEEP of hub_data_length : signal is "true";
+    attribute KEEP of trb_data_tvalid : signal is "true";
+    attribute KEEP of trb_data_tready : signal is "true";
+    attribute KEEP of trb_data_tdata : signal is "true";
+    attribute KEEP of trb_data_tkeep : signal is "true";
+    attribute KEEP of trb_data_tlast : signal is "true";
 
     signal usrclk : std_logic;
 
@@ -470,6 +486,28 @@ begin
     );
 
 
+    generate_parsers:
+    for i in 0 to INTERFACE_NUM - 2 generate
+    begin
+        trb_parser_i : entity work.trb_parser
+        port map (
+            CLK                 => sysclk_100,
+            RESET               => reset,
+            DATA_ACTIVE         => hub_data_active(i),
+            DATA_OUT            => hub_data_out(32 * i + 31 downto 32 * i),
+            DATA_READY          => hub_data_ready(i),
+            DATA_ADDRESS_SENDER => hub_data_address_sender(16 * i + 15 downto 16 * i),
+            DATA_SEQNMBR        => hub_data_seqnmbr(8 * i + 7 downto 8 * i),
+            DATA_LENGTH         => hub_data_length(16 * i + 15 downto 16 * i),
+            M_AXIS_TVALID       => trb_data_tvalid(i),
+            M_AXIS_TREADY       => trb_data_tready(i),
+            M_AXIS_TDATA        => trb_data_tdata(32 * i + 31 downto 32 * i),
+            M_AXIS_TKEEP        => trb_data_tkeep(4 * i + 3 downto 4 * i),
+            M_AXIS_TLAST        => trb_data_tlast(i)
+        );
+    end generate generate_parsers;
+
+
     gen_media_record : for i in 0 to INTERFACE_NUM - 1 generate
     begin
         med_data_in(i * 16 + 15 downto i * 16)    <= med2int_i(i).data;
diff --git a/hub_test/src/trb_parser.vhd b/hub_test/src/trb_parser.vhd
new file mode 100644 (file)
index 0000000..8289557
--- /dev/null
@@ -0,0 +1,580 @@
+-------------------------------------------------------------------------------
+--! @file
+--! @brief TRB-data parser
+--!
+--! Receives TRB data from a hub data interface (DATA_* ports) and parses the
+--! content. Outputs only valid TRB data to a 4-byte AXI-Stream interface.
+--! Discards the complete input packet in case of any format error or buffer
+--! overflow.
+--!
+--! Prepends a 32-bit word to every output packet:
+--!
+--! [31:24]: reserved
+--! [23:0]:  MBS trigger-number
+--!
+--! Assumptions about DATA_* ports:
+--!  - DATA_ACTIVE goes high for a new input packet and remains high until the
+--!    packet is finished.
+--!  - DATA_ACTIVE goes low for at least one clock cycle between packets.
+--!  - DATA_READY signifies that the word on DATA_OUT is valid.
+-------------------------------------------------------------------------------
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library xpm;
+use xpm.vcomponents.all;
+
+entity trb_parser is
+    port (
+        CLK                 : in  std_logic;
+        RESET               : in  std_logic;
+
+        DATA_ACTIVE         : in  std_logic;
+        DATA_OUT            : in  std_logic_vector(31 downto 0);
+        DATA_READY          : in  std_logic;
+        DATA_ADDRESS_SENDER : in  std_logic_vector(15 downto 0);
+        DATA_SEQNMBR        : in  std_logic_vector(7 downto 0);
+        DATA_LENGTH         : in  std_logic_vector(15 downto 0);
+
+        M_AXIS_TVALID       : out std_logic;
+        M_AXIS_TREADY       : in  std_logic;
+        M_AXIS_TDATA        : out std_logic_vector(31 downto 0);
+        M_AXIS_TKEEP        : out std_logic_vector(3 downto 0);
+        M_AXIS_TLAST        : out std_logic
+    );
+end entity trb_parser;
+
+architecture behavioral of trb_parser is
+    type in_state is (
+        in_reset,
+        discard_input_packet,
+        trg_type_inactive,
+        trg_type,
+        combiner_header,
+        subsub_header,
+        dirich_data,
+        cts_data,
+        trailer_0,
+        trailer_1,
+        aaaa_or_end_of_packet,
+        end_of_packet,
+        terminate_input
+    );
+
+    type out_state is (
+        out_reset,
+        check_meta_fifo,
+        output_mbs_trg_num,
+        output_forward,
+        output_discard
+    );
+
+    signal in_state_cur : in_state := in_reset;
+    signal in_state_next : in_state;
+
+    signal out_state_cur : out_state := out_reset;
+    signal out_state_next : out_state;
+
+    type ERR_CODE is (
+        ERR_INIT_DATA_LOST,   -- 0
+        ERR_READY_NOT_ACTIVE, -- 1
+        ERR_META_FIFO_FULL,   -- 2
+        ERR_DATA_FIFO_FULL,   -- 3
+        ERR_UNEXPECTED_EOP,   -- 4
+        ERR_DATA_FIFO_AFULL,  -- 5
+        ERR_COMBINER_SIZE,    -- 6
+        ERR_TRAILER,          -- 7
+        ERR_META_FIFO_FATAL,  -- 8
+        ERR_DATA_FIFO_FATAL,  -- 9
+        ERR_PADDING,          -- A
+        ERR_TRAILING_DATA     -- B
+    );
+
+    signal err_mask : std_logic_vector(ERR_CODE'pos(ERR_CODE'high) downto 0);
+
+    signal data_fifo_almost_full : std_logic;
+    signal data_fifo_dout : std_logic_vector(32 downto 0);
+    signal data_fifo_empty : std_logic;
+    signal data_fifo_full : std_logic;
+    signal data_fifo_rd_rst_busy : std_logic;
+    signal data_fifo_wr_rst_busy : std_logic;
+    signal data_fifo_din : std_logic_vector(32 downto 0);
+    signal data_fifo_rd_en : std_logic;
+    signal data_fifo_wr_en : std_logic;
+
+    signal meta_fifo_dout : std_logic_vector(24 downto 0);
+    signal meta_fifo_empty : std_logic;
+    signal meta_fifo_full : std_logic;
+    signal meta_fifo_rd_rst_busy : std_logic;
+    signal meta_fifo_wr_rst_busy : std_logic;
+    signal meta_fifo_din : std_logic_vector(24 downto 0);
+    signal meta_fifo_rd_en : std_logic;
+    signal meta_fifo_wr_en : std_logic;
+
+    signal comb_rem_words : unsigned(15 downto 0) := x"0000";
+    signal comb_rem_words_next : unsigned(15 downto 0);
+    signal subsub_rem_words : unsigned(15 downto 0) := x"0000";
+    signal subsub_rem_words_next : unsigned(15 downto 0);
+    signal mbs_trg_num : std_logic_vector(23 downto 0) := x"000000";
+    signal mbs_trg_num_next : std_logic_vector(23 downto 0);
+    signal comb_addr : std_logic_vector(15 downto 0) := x"0000";
+    signal comb_addr_next : std_logic_vector(15 downto 0);
+
+    signal fifo_resets_busy : std_logic;
+begin
+    data_fifo : xpm_fifo_sync
+    generic map (
+        DOUT_RESET_VALUE    => "0",
+        ECC_MODE            => "no_ecc",
+        FIFO_MEMORY_TYPE    => "auto",
+        FIFO_READ_LATENCY   => 0,
+        FIFO_WRITE_DEPTH    => 16384, -- 16 to 4194304 (power of 2)
+        FULL_RESET_VALUE    => 0,
+        PROG_EMPTY_THRESH   => 10,
+        PROG_FULL_THRESH    => 10,
+        RD_DATA_COUNT_WIDTH => 1,
+        READ_DATA_WIDTH     => 33,
+        READ_MODE           => "fwft",
+        SIM_ASSERT_CHK      => 0,
+        USE_ADV_FEATURES    => "0008", -- almost_full
+        WAKEUP_TIME         => 0,
+        WRITE_DATA_WIDTH    => 33,
+        WR_DATA_COUNT_WIDTH => 1
+    )
+    port map (
+        almost_empty  => open,
+        almost_full   => data_fifo_almost_full,
+        data_valid    => open,
+        dbiterr       => open,
+        dout          => data_fifo_dout,
+        empty         => data_fifo_empty,
+        full          => data_fifo_full,
+        overflow      => open,
+        prog_empty    => open,
+        prog_full     => open,
+        rd_data_count => open,
+        rd_rst_busy   => data_fifo_rd_rst_busy,
+        sbiterr       => open,
+        underflow     => open,
+        wr_ack        => open,
+        wr_data_count => open,
+        wr_rst_busy   => data_fifo_wr_rst_busy,
+        din           => data_fifo_din,
+        injectdbiterr => '0',
+        injectsbiterr => '0',
+        rd_en         => data_fifo_rd_en,
+        rst           => RESET,
+        sleep         => '0',
+        wr_clk        => CLK,
+        wr_en         => data_fifo_wr_en
+    );
+
+    meta_fifo : xpm_fifo_sync
+    generic map (
+        DOUT_RESET_VALUE    => "0",
+        ECC_MODE            => "no_ecc",
+        FIFO_MEMORY_TYPE    => "auto",
+        FIFO_READ_LATENCY   => 0,
+        FIFO_WRITE_DEPTH    => 16, -- 16 to 4194304 (power of 2)
+        FULL_RESET_VALUE    => 0,
+        PROG_EMPTY_THRESH   => 10,
+        PROG_FULL_THRESH    => 10,
+        RD_DATA_COUNT_WIDTH => 1,
+        READ_DATA_WIDTH     => 25,
+        READ_MODE           => "fwft",
+        SIM_ASSERT_CHK      => 0,
+        USE_ADV_FEATURES    => "0000",
+        WAKEUP_TIME         => 0,
+        WRITE_DATA_WIDTH    => 25,
+        WR_DATA_COUNT_WIDTH => 1
+    )
+    port map (
+        almost_empty  => open,
+        almost_full   => open,
+        data_valid    => open,
+        dbiterr       => open,
+        dout          => meta_fifo_dout,
+        empty         => meta_fifo_empty,
+        full          => meta_fifo_full,
+        overflow      => open,
+        prog_empty    => open,
+        prog_full     => open,
+        rd_data_count => open,
+        rd_rst_busy   => meta_fifo_rd_rst_busy,
+        sbiterr       => open,
+        underflow     => open,
+        wr_ack        => open,
+        wr_data_count => open,
+        wr_rst_busy   => meta_fifo_wr_rst_busy,
+        din           => meta_fifo_din,
+        injectdbiterr => '0',
+        injectsbiterr => '0',
+        rd_en         => meta_fifo_rd_en,
+        rst           => RESET,
+        sleep         => '0',
+        wr_clk        => CLK,
+        wr_en         => meta_fifo_wr_en
+    );
+
+    fifo_resets_busy <= data_fifo_rd_rst_busy or data_fifo_wr_rst_busy
+                        or meta_fifo_rd_rst_busy or meta_fifo_wr_rst_busy;
+
+    in_sm_sync:
+    process (CLK) is
+    begin
+        if rising_edge(CLK) then
+            if RESET = '1' then
+                in_state_cur <= in_reset;
+                comb_rem_words <= x"0000";
+                subsub_rem_words <= x"0000";
+                mbs_trg_num <= x"000000";
+                comb_addr <= x"0000";
+            else
+                in_state_cur <= in_state_next;
+                comb_rem_words <= comb_rem_words_next;
+                subsub_rem_words <= subsub_rem_words_next;
+                mbs_trg_num <= mbs_trg_num_next;
+                comb_addr <= comb_addr_next;
+            end if;
+        end if;
+    end process in_sm_sync;
+
+    in_sm_comb:
+    process (
+        in_state_cur,
+        comb_rem_words,
+        subsub_rem_words,
+        mbs_trg_num,
+        comb_addr,
+        DATA_ACTIVE,
+        DATA_OUT,
+        DATA_READY,
+        fifo_resets_busy,
+        meta_fifo_full,
+        data_fifo_full,
+        data_fifo_almost_full
+    ) is
+    begin
+        in_state_next <= in_state_cur;
+
+        comb_rem_words_next <= comb_rem_words;
+        subsub_rem_words_next <= subsub_rem_words;
+        mbs_trg_num_next <= mbs_trg_num;
+        comb_addr_next <= comb_addr;
+
+        err_mask <= (others => '0');
+        err_mask(ERR_CODE'pos(ERR_READY_NOT_ACTIVE)) <= DATA_READY
+                                                        and not DATA_ACTIVE;
+        err_mask(ERR_CODE'pos(ERR_DATA_FIFO_FATAL)) <= data_fifo_full
+                                                       and data_fifo_wr_en;
+        err_mask(ERR_CODE'pos(ERR_META_FIFO_FATAL)) <= meta_fifo_full
+                                                       and meta_fifo_wr_en;
+
+        data_fifo_din(31 downto 0) <= DATA_OUT;
+        data_fifo_din(32) <= '0';
+        data_fifo_wr_en <= '0';
+
+        meta_fifo_din(23 downto 0) <= mbs_trg_num;
+        meta_fifo_din(24) <= '0';
+        meta_fifo_wr_en <= '0';
+
+        case in_state_cur is
+        when in_reset =>
+            if fifo_resets_busy = '0' then
+                in_state_next <= discard_input_packet;
+                if DATA_ACTIVE = '0' then
+                    in_state_next <= trg_type_inactive;
+                else
+                    err_mask(ERR_CODE'pos(ERR_INIT_DATA_LOST)) <= '1';
+                    in_state_next <= discard_input_packet;
+                end if;
+            end if;
+        when discard_input_packet =>
+            if DATA_ACTIVE = '0' then
+                in_state_next <= trg_type_inactive;
+            end if;
+        when trg_type_inactive =>
+            if DATA_ACTIVE = '1' then
+                if meta_fifo_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_META_FIFO_FULL)) <= '1';
+                end if;
+                if data_fifo_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_FULL)) <= '1';
+                end if;
+                if meta_fifo_full = '1' or data_fifo_full = '1' then
+                    -- Before accepting a new packet, we ensure that at least
+                    -- one word can be written to both FIFOs. This condition is
+                    -- kept up until the packet ends and the last data word and
+                    -- meta word are written.
+                    in_state_next <= discard_input_packet;
+                elsif DATA_READY = '1' then
+                    if data_fifo_almost_full = '1' then
+                        err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                        in_state_next <= terminate_input;
+                    else
+                        data_fifo_wr_en <= '1';
+                        in_state_next <= combiner_header;
+                    end if;
+                else
+                    in_state_next <= trg_type;
+                end if;
+            end if;
+        when trg_type =>
+            if DATA_ACTIVE = '0' then
+                err_mask(ERR_CODE'pos(ERR_UNEXPECTED_EOP)) <= '1';
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_wr_en <= '1';
+                in_state_next <= trg_type_inactive;
+            elsif DATA_READY = '1' then
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    in_state_next <= combiner_header;
+                end if;
+            end if;
+        when combiner_header =>
+            comb_rem_words_next <= unsigned(DATA_OUT(31 downto 16));
+            comb_addr_next <= DATA_OUT(15 downto 0);
+            if DATA_ACTIVE = '0' then
+                err_mask(ERR_CODE'pos(ERR_UNEXPECTED_EOP)) <= '1';
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_wr_en <= '1';
+                in_state_next <= trg_type_inactive;
+            elsif DATA_READY = '1' then
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    in_state_next <= subsub_header;
+                end if;
+            end if;
+        when subsub_header =>
+            subsub_rem_words_next <= unsigned(DATA_OUT(31 downto 16));
+            if DATA_ACTIVE = '0' then
+                err_mask(ERR_CODE'pos(ERR_UNEXPECTED_EOP)) <= '1';
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_wr_en <= '1';
+                in_state_next <= trg_type_inactive;
+            elsif DATA_READY = '1' then
+                comb_rem_words_next <= comb_rem_words - 1;
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    if DATA_OUT(15 downto 0) = comb_addr then
+                        in_state_next <= cts_data;
+                    else
+                        in_state_next <= dirich_data;
+                    end if;
+                end if;
+            end if;
+        when dirich_data =>
+            if DATA_ACTIVE = '0' then
+                err_mask(ERR_CODE'pos(ERR_UNEXPECTED_EOP)) <= '1';
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_wr_en <= '1';
+                in_state_next <= trg_type_inactive;
+            elsif DATA_READY = '1' then
+                comb_rem_words_next <= comb_rem_words - 1;
+                subsub_rem_words_next <= subsub_rem_words - 1;
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    if subsub_rem_words = 1 then
+                        in_state_next <= subsub_header;
+                    end if;
+                end if;
+            end if;
+        when cts_data =>
+            mbs_trg_num_next <= DATA_OUT(23 downto 0);
+            if DATA_ACTIVE = '0' then
+                err_mask(ERR_CODE'pos(ERR_UNEXPECTED_EOP)) <= '1';
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_wr_en <= '1';
+                in_state_next <= trg_type_inactive;
+            elsif DATA_READY = '1' then
+                comb_rem_words_next <= comb_rem_words - 1;
+                subsub_rem_words_next <= subsub_rem_words - 1;
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    if subsub_rem_words = 1 then
+                        if comb_rem_words = 1 then
+                            in_state_next <= trailer_0;
+                        else
+                            err_mask(ERR_CODE'pos(ERR_COMBINER_SIZE)) <= '1';
+                            in_state_next <= terminate_input;
+                        end if;
+                    end if;
+                end if;
+            end if;
+        when trailer_0 =>
+            if DATA_ACTIVE = '0' then
+                err_mask(ERR_CODE'pos(ERR_UNEXPECTED_EOP)) <= '1';
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_wr_en <= '1';
+                in_state_next <= trg_type_inactive;
+            elsif DATA_READY = '1' then
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    if DATA_OUT = x"00015555" then
+                        in_state_next <= trailer_1;
+                    else
+                        err_mask(ERR_CODE'pos(ERR_TRAILER)) <= '1';
+                        in_state_next <= terminate_input;
+                    end if;
+                end if;
+            end if;
+        when trailer_1 =>
+            if DATA_ACTIVE = '0' then
+                err_mask(ERR_CODE'pos(ERR_UNEXPECTED_EOP)) <= '1';
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_wr_en <= '1';
+                in_state_next <= trg_type_inactive;
+            elsif DATA_READY = '1' then
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    in_state_next <= aaaa_or_end_of_packet;
+                end if;
+            end if;
+        when aaaa_or_end_of_packet =>
+            if DATA_ACTIVE = '0' then
+                in_state_next <= trg_type_inactive;
+                data_fifo_din(31 downto 0) <= x"600D_DA7A";
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_din(24) <= '1';
+                meta_fifo_wr_en <= '1';
+            elsif DATA_READY = '1' then
+                if data_fifo_almost_full = '1' then
+                    err_mask(ERR_CODE'pos(ERR_DATA_FIFO_AFULL)) <= '1';
+                    in_state_next <= terminate_input;
+                else
+                    data_fifo_wr_en <= '1';
+                    if DATA_OUT = x"AAAAAAAA" then
+                        in_state_next <= end_of_packet;
+                    else
+                        err_mask(ERR_CODE'pos(ERR_PADDING)) <= '1';
+                        in_state_next <= terminate_input;
+                    end if;
+                end if;
+            end if;
+        when end_of_packet =>
+            if DATA_ACTIVE = '0' then
+                in_state_next <= trg_type_inactive;
+                data_fifo_din(31 downto 0) <= x"600D_DA7A";
+                data_fifo_din(32) <= '1';
+                data_fifo_wr_en <= '1';
+                meta_fifo_din(24) <= '1';
+                meta_fifo_wr_en <= '1';
+            elsif DATA_READY = '1' then
+                err_mask(ERR_CODE'pos(ERR_TRAILING_DATA)) <= '1';
+                in_state_next <= terminate_input;
+            end if;
+        when terminate_input =>
+            data_fifo_din(32) <= '1';
+            data_fifo_wr_en <= '1';
+            meta_fifo_wr_en <= '1';
+            if DATA_ACTIVE = '1' then
+                in_state_next <= discard_input_packet;
+            else
+                in_state_next <= trg_type_inactive;
+            end if;
+        end case;
+    end process in_sm_comb;
+
+
+    out_sm_sync:
+    process (CLK) is
+    begin
+        if rising_edge(CLK) then
+            if RESET = '1' then
+                out_state_cur <= out_reset;
+            else
+                out_state_cur <= out_state_next;
+            end if;
+        end if;
+    end process out_sm_sync;
+
+    out_sm_comb:
+    process (
+        out_state_cur,
+        fifo_resets_busy,
+        meta_fifo_empty,
+        meta_fifo_dout,
+        data_fifo_empty,
+        data_fifo_dout,
+        M_AXIS_TREADY
+    ) is
+    begin
+        out_state_next <= out_state_cur;
+
+        M_AXIS_TVALID <= '0';
+        M_AXIS_TLAST <= data_fifo_dout(32);
+        M_AXIS_TDATA <= data_fifo_dout(31 downto 0);
+        M_AXIS_TKEEP <= "1111";
+
+        meta_fifo_rd_en <= '0';
+        data_fifo_rd_en <= '0';
+
+        case out_state_cur is
+        when out_reset =>
+            if fifo_resets_busy = '0' then
+                out_state_next <= check_meta_fifo;
+            end if;
+        when check_meta_fifo =>
+            if meta_fifo_empty = '0' then
+                if meta_fifo_dout(24) = '1' then
+                    out_state_next <= output_mbs_trg_num;
+                else
+                    meta_fifo_rd_en <= '1';
+                    out_state_next <= output_discard;
+                end if;
+            end if;
+        when output_mbs_trg_num =>
+            M_AXIS_TVALID <= '1';
+            M_AXIS_TLAST <= '0';
+            M_AXIS_TDATA(31 downto 24) <= x"00";
+            M_AXIS_TDATA(23 downto 0) <= meta_fifo_dout(23 downto 0);
+            if M_AXIS_TREADY = '1' then
+                meta_fifo_rd_en <= '1';
+                out_state_next <= output_forward;
+            end if;
+        when output_forward =>
+            M_AXIS_TVALID <= not data_fifo_empty;
+            data_fifo_rd_en <= M_AXIS_TREADY;
+            if M_AXIS_TREADY = '1' and data_fifo_empty = '0'
+                    and data_fifo_dout(32) = '1'
+            then
+                out_state_next <= check_meta_fifo;
+            end if;
+        when output_discard =>
+            data_fifo_rd_en <= '1';
+            if data_fifo_empty = '0' and data_fifo_dout(32) = '1' then
+                out_state_next <= check_meta_fifo;
+            end if;
+        end case;
+    end process out_sm_comb;
+end architecture behavioral;