rtps-fpga/src/rtps_out.vhd
2022-04-05 17:20:32 +02:00

390 lines
16 KiB
VHDL

-- 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;