-- altera vhdl_input_version vhdl_2008 -- XXX: QSYS Fix (https://www.intel.com/content/www/us/en/support/programmable/articles/000079458.html) library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.math_pkg.all; use work.rtps_package.all; use work.user_config.all; use work.rtps_config_package.all; entity rtps_out is generic ( RTPS_OUT_WIDTH : natural := NUM_ENDPOINTS+1; -- Max Serialized Payload Size in a UDP Stream (Bytes) [MAX_PAYLOAD(65536) - IPv4_HEADER(20) - UDP_HEADER(8) + FORMAT_HEADER(16)] MAX_BUFFER_SIZE : natural := 65524/(WORD_WIDTH/BYTE_WIDTH) ); port ( -- SYSTEM clk : in std_logic; reset : in std_logic; -- INPUT empty : in std_logic_vector(0 to RTPS_OUT_WIDTH-1); rd : out std_logic_vector(0 to RTPS_OUT_WIDTH-1); data_in : in WORD_ARRAY_TYPE(0 to RTPS_OUT_WIDTH-1); last_word_in: in std_logic_vector(0 to RTPS_OUT_WIDTH-1); -- OUTPUT full : in std_logic; wr : out std_logic; data_out : out std_logic_vector(WORD_WIDTH-1 downto 0) ); end entity; architecture arch of rtps_out is -- *CONSTANT DECLARATION* constant BUFFER_ADDR_WIDTH : natural := log2c(MAX_BUFFER_SIZE); -- *TYPE DECLARATION* type BUFFER_TYPE is array (0 to MAX_BUFFER_SIZE-1) of std_logic_vector(WORD_WIDTH-1 downto 0); type INPUT_STAGE_TYPE is (IDLE, SRC_ADDR_HEADER, DEST_ADDR_HEADER, PORT_HEADER, READ, SKIP); type OUTPUT_STAGE_TYPE is (IDLE, SRC_ADDR_HEADER, DEST_ADDR_HEADER, PORT_HEADER, PACKET_LENGTH, WRITE_PRIMER, WRITE, FINALIZE_WRITE); -- *SIGNAL DECLARATION* signal selector, selector_next : natural range 0 to RTPS_OUT_WIDTH-1; signal buff, buff_next : BUFFER_TYPE; signal in_pntr, in_pntr_next : unsigned(BUFFER_ADDR_WIDTH downto 0); signal out_pntr, out_pntr_next : unsigned(BUFFER_ADDR_WIDTH downto 0); signal length, length_next : unsigned(WORD_WIDTH-1 downto 0); signal packet_end, packet_end_next : unsigned(WORD_WIDTH-1 downto 0); signal input_stage, input_stage_next : INPUT_STAGE_TYPE; signal output_stage, output_stage_next : OUTPUT_STAGE_TYPE; signal filled ,reset_filled, set_filled: std_logic; signal src_addr, src_addr_next : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0); signal dest_addr, dest_addr_next : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0); signal ports, ports_next : std_logic_vector((UDP_PORT_WIDTH*2)-1 downto 0); signal rvalid_in, rready_in, rvalid_out, rready_out, wready_in, wvalid_in : std_logic; signal rdata_out, wdata_in : std_logic_vector(WORD_WIDTH-1 downto 0); begin buffer_inst : entity work.dp_mem_ctrl(arch) generic map ( ADDR_WIDTH => BUFFER_ADDR_WIDTH, DATA_WIDTH => WORD_WIDTH, MEMORY_DEPTH => MAX_BUFFER_SIZE, MAX_BURST_LENGTH => 2 ) port map ( -- SYSTEM clk => clk, reset => reset, -- READ PORT raddr => std_logic_vector(out_pntr(BUFFER_ADDR_WIDTH-1 downto 0)), rvalid_in => rvalid_in, rready_in => rready_in, rvalid_out => rvalid_out, rready_out => rready_out, rdata_out => rdata_out, -- WRITE PORT waddr => std_logic_vector(in_pntr(BUFFER_ADDR_WIDTH-1 downto 0)), wvalid_in => wvalid_in, wready_in => wready_in, wdata_in => wdata_in ); in_prc : process (all) begin -- DEFAULT input_stage_next <= input_stage; selector_next <= selector; in_pntr_next <= in_pntr; src_addr_next <= src_addr; dest_addr_next <= dest_addr; ports_next <= ports; buff_next <= buff; length_next <= length; -- DEFAULT Unregistered rd <= (others => '0'); set_filled <= '0'; wvalid_in <= '0'; wdata_in <= (others => '0'); case (input_stage) is when IDLE => -- Currently Selected Input FIFO is empty if (empty(selector) = '1') then -- Wrap from End to BEgining (Circular selection) if (selector = RTPS_OUT_WIDTH-1) then selector_next <= 0; else -- Select next input FIFO selector_next <= selector + 1; end if; else -- Wait until Output Pointer is beyond the Header if (filled = '0' and out_pntr /= 0) then -- Read from input FIFO input_stage_next <= SRC_ADDR_HEADER; in_pntr_next <= (others => '0'); end if; end if; when SRC_ADDR_HEADER => -- Input FIFO Guard if (empty(selector) = '0') then rd(selector) <= '1'; src_addr_next <= data_in(selector); input_stage_next<= DEST_ADDR_HEADER; -- SANITY CHECK: Skip Packet if last word in before actual packet if (last_word_in(selector) = '1') then -- Skip input_stage_next <= IDLE; in_pntr_next <= to_unsigned(MAX_BUFFER_SIZE, in_pntr'length); -- Select next input FIFO (Prevent lifelock) if (selector = RTPS_OUT_WIDTH-1) then selector_next <= 0; else selector_next <= selector + 1; end if; end if; end if; when DEST_ADDR_HEADER => -- Input FIFO Guard if (empty(selector) = '0') then rd(selector) <= '1'; dest_addr_next <= data_in(selector); input_stage_next<= PORT_HEADER; -- SANITY CHECK: Skip Packet if last word in before actual packet if (last_word_in(selector) = '1') then -- Skip input_stage_next <= IDLE; in_pntr_next <= to_unsigned(MAX_BUFFER_SIZE, in_pntr'length); -- Select next input FIFO (Prevent lifelock) if (selector = RTPS_OUT_WIDTH-1) then selector_next <= 0; else selector_next <= selector + 1; end if; end if; end if; when PORT_HEADER => -- Input FIFO Guard if (empty(selector) = '0') then rd(selector) <= '1'; ports_next <= data_in(selector); input_stage_next <= READ; -- SANITY CHECK: Skip Packet if last word in before actual packet if (last_word_in(selector) = '1') then -- Skip input_stage_next <= IDLE; in_pntr_next <= to_unsigned(MAX_BUFFER_SIZE, in_pntr'length); -- Select next input FIFO (Prevent lifelock) if (selector = RTPS_OUT_WIDTH-1) then selector_next <= 0; else selector_next <= selector + 1; end if; end if; end if; when READ => -- Output pointer past our point (We can safely read into the Buffer) if (out_pntr > in_pntr) then -- Input FIFO Guard if (empty(selector) = '0') then wdata_in <= data_in(selector); wvalid_in <= '1'; -- Memory Guard if (wready_in = '1') then rd(selector) <= '1'; in_pntr_next <= in_pntr + 1; -- Last Input Word if (last_word_in(selector) = '1') then assert (in_pntr'length <= WORD_WIDTH) severity FAILURE; -- Set Length length_next <= resize(in_pntr, WORD_WIDTH); -- Mark Buffer Ready for Output set_filled <= '1'; -- DONE input_stage_next <= IDLE; in_pntr_next <= to_unsigned(MAX_BUFFER_SIZE, in_pntr'length); -- Select next input FIFO (Prevent lifelock) if (selector = RTPS_OUT_WIDTH-1) then selector_next <= 0; else selector_next <= selector + 1; end if; -- Overflow (Packet larger than buffer) elsif (in_pntr = MAX_BUFFER_SIZE-1) then -- SKIP PACKET input_stage_next <= SKIP; end if; end if; end if; end if; when SKIP => -- Input FIFO Guard if (empty(selector) = '0') then rd(selector) <= '1'; -- Last Input Word if (last_word_in(selector) = '1') then -- DONE input_stage_next <= IDLE; in_pntr_next <= to_unsigned(MAX_BUFFER_SIZE, in_pntr'length); -- Select next input FIFO (Prevent lifelock) if (selector = RTPS_OUT_WIDTH-1) then selector_next <= 0; else selector_next <= selector + 1; end if; end if; end if; end case; end process; out_prc : process (all) begin -- DEFAULT output_stage_next <= output_stage; out_pntr_next <= out_pntr; wr <= '0'; data_out <= (others => '0'); packet_end_next <= packet_end; -- DEFAULT Unregistered reset_filled <= '0'; rvalid_in <= '0'; rready_out <= '0'; case (output_stage) is when IDLE => -- Wait until Buffer is Ready if (filled = '1') then output_stage_next <= SRC_ADDR_HEADER; out_pntr_next <= (others => '0'); -- Mark Buffer as being processed reset_filled <= '1'; end if; when SRC_ADDR_HEADER => -- Output FIFO Guard if (full = '0') then wr <= '1'; data_out <= src_addr; output_stage_next <= DEST_ADDR_HEADER; end if; when DEST_ADDR_HEADER => -- Output FIFO Guard if (full = '0') then wr <= '1'; data_out <= dest_addr; output_stage_next <= PORT_HEADER; end if; when PORT_HEADER => -- Output FIFO Guard if (full = '0') then wr <= '1'; data_out <= ports; output_stage_next <= PACKET_LENGTH; end if; when PACKET_LENGTH => -- Output FIFO Guard if (full = '0') then wr <= '1'; data_out <= std_logic_vector(length + 1); packet_end_next <= length; output_stage_next <= WRITE_PRIMER; end if; when WRITE_PRIMER => -- Prime the Memory Controller with Read Requests -- XXX: Optimized for Read Latency = 1 rvalid_in <= '1'; -- if (rready_in = '1') then if (out_pntr = packet_end) then -- DONE Reading output_stage_next <= FINALIZE_WRITE; out_pntr_next <= to_unsigned(MAX_BUFFER_SIZE, out_pntr'length); else output_stage_next <= WRITE; out_pntr_next <= out_pntr + 1; end if; end if; when WRITE => -- Output FIFO Guard if (full = '0' and rvalid_out = '1') then data_out <= rdata_out; -- Memory Guard if (rready_in = '1') then rvalid_in <= '1'; rready_out <= '1'; wr <= '1'; out_pntr_next <= out_pntr + 1; if (out_pntr = packet_end) then -- DONE Reading output_stage_next <= FINALIZE_WRITE; out_pntr_next <= to_unsigned(MAX_BUFFER_SIZE, out_pntr'length); end if; end if; end if; when FINALIZE_WRITE => -- Output FIFO Guard if (full = '0' and rvalid_out = '1') then data_out <= rdata_out; rready_out <= '1'; wr <= '1'; -- DONE output_stage_next <= IDLE; end if; end case; end process; filled_prc : process (all) begin if rising_edge(clk) then if (reset = '1') then filled <= '0'; else -- NOTE: This condition should not be possible due to the additional bound checks of the input and output processes. assert(not (reset_filled = '1' and set_filled = '1')) report "Both set and reset Flag set on same clock cycle" severity FAILURE; if (reset_filled = '1') then filled <= '0'; elsif (set_filled = '1') then filled <= '1'; end if; end if; end if; end process; sync_prc : process(all) begin if rising_edge(clk) then if (reset = '1') then input_stage <= IDLE; output_stage <= IDLE; selector <= 0; in_pntr <= to_unsigned(MAX_BUFFER_SIZE, in_pntr'length); out_pntr <= to_unsigned(MAX_BUFFER_SIZE, out_pntr'length); src_addr <= IPv4_ADDRESS_INVALID; dest_addr <= IPv4_ADDRESS_INVALID; ports <= UDP_PORT_INVALID & UDP_PORT_INVALID; length <= (others => '0'); packet_end <= (others => '0'); buff <= (others => (others => '0')); else input_stage <= input_stage_next; output_stage <= output_stage_next; selector <= selector_next; in_pntr <= in_pntr_next; out_pntr <= out_pntr_next; src_addr <= src_addr_next; dest_addr <= dest_addr_next; ports <= ports_next; length <= length_next; packet_end <= packet_end_next; buff <= buff_next; end if; end if; end process; end architecture;