390 lines
16 KiB
VHDL
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;
|