diff --git a/rtl/noc/bypassable_queue.vhd b/rtl/noc/bypassable_queue.vhd deleted file mode 100644 index 89bf50eaa2..0000000000 --- a/rtl/noc/bypassable_queue.vhd +++ /dev/null @@ -1,146 +0,0 @@ --- Copyright (c) 2011-2023 Columbia University, System Level Design Group --- SPDX-License-Identifier: Apache-2.0 - ---/* --- * Module: bypassable_queue --- * Description: Bypassable FIFO. --- * A FIFO can be bypassed when the incoming flit can be forwarded directly --- * to an output without need to be stored. --- * Author: Michele Petracca --- * $ID$ --- * --- */ - -library ieee; -use ieee.std_logic_1164.all; -use ieee.std_logic_unsigned.all; -use ieee.std_logic_arith.all; - -entity bypassable_queue is - generic( - depth : integer; - width : integer --- localx : std_logic_vector(2 downto 0); --- localy : std_logic_vector(2 downto 0) - ); - - port( - clk : in std_logic := '0'; - rst : in std_logic; - - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0); - routing_out : out std_logic_vector(4 downto 0)); -end bypassable_queue; - - -architecture behavior of bypassable_queue is - -component fifo0 - generic( - depth : integer; - width : integer); - port( - clk : in std_logic; - rst : in std_logic; - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0)); -end component; - -component fifo1 - generic( - width : integer); - port( - clk : in std_logic; - rst : in std_logic; - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0)); -end component; - - -signal fifo_data_out, data_out_i : std_logic_vector(width-1 downto 0); -signal wr_internal_fifo, rd_internal_fifo : std_logic; -signal empty_i : std_logic; -signal routing_out_i : std_logic_vector(4 downto 0); - ---for others: fifo use entity testchip_v2.fifo(behavior); ---for others: fifo1 use entity testchip_v2.fifo1(behavior); - -begin - -wr_internal_fifo <= '1' when (wrreq = '1' and empty_i = '1' and rdreq = '0') or (wrreq = '1' and empty_i = '0') else '0'; -rd_internal_fifo <= '1' when (rdreq = '1' and empty_i = '0') else '0'; - -QUEUE_INST1: if (depth = 1) generate -queue: fifo1 - generic map( - width => width) - port map( - clk => clk, - rst => rst, - - rdreq => rd_internal_fifo, - wrreq => wr_internal_fifo, - data_in => data_in, - - --request registers - empty => empty_i, - full => full, - data_out => fifo_data_out); -end generate; - -QUEUE_INST: if (depth > 1) generate -queue: fifo0 - generic map( - depth => depth, - width => width) - port map( - clk => clk, - rst => rst, - - rdreq => rd_internal_fifo, - wrreq => wr_internal_fifo, - data_in => data_in, - - --request registers - empty => empty_i, - full => full, - data_out => fifo_data_out); -end generate; - - -empty <= empty_i; -routing_out <= data_out_i(4 downto 0); -data_out <= data_out_i; - -process(data_in, fifo_data_out, empty_i) -begin - if empty_i = '1' then - data_out_i <= data_in; - else - data_out_i <= fifo_data_out; - end if; -end process; - -end behavior; diff --git a/rtl/noc/nobypassable_queue.vhd b/rtl/noc/nobypassable_queue.vhd deleted file mode 100644 index dfdb24a6ff..0000000000 --- a/rtl/noc/nobypassable_queue.vhd +++ /dev/null @@ -1,150 +0,0 @@ --- Copyright (c) 2011-2023 Columbia University, System Level Design Group --- SPDX-License-Identifier: Apache-2.0 - ---/* --- * Module: bypassable_queue --- * Description: Bypassable FIFO. --- * A FIFO can be bypassed when the incoming flit can be forwarded directly --- * to an output without need to be stored. --- * Author: Michele Petracca --- * $ID$ --- * --- */ - -library IEEE; -use IEEE.STD_LOGIC_1164.all; -use ieee.std_logic_unsigned.all; -use ieee.std_logic_arith.all; - -entity nobypassable_queue is - generic( - depth : integer; - width : integer --- localx : std_logic_vector(2 downto 0); --- localy : std_logic_vector(2 downto 0) - ); - port( - clk : in std_logic := '0'; - rst : in std_logic; - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0); - routing_out : out std_logic_vector(4 downto 0)); -end nobypassable_queue; - - -architecture behavior of nobypassable_queue is - -component fifo0 - generic( - depth : integer; - width : integer); - port( - clk : in std_logic; - rst : in std_logic; - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0)); -end component; - -component fifo1 - generic( - width : integer); - port( - clk : in std_logic; - rst : in std_logic; - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0)); -end component; - - -signal fifo_data_out, data_out_i : std_logic_vector(width-1 downto 0); -signal wr_internal_fifo, rd_internal_fifo : std_logic; -signal empty_i : std_logic; -signal routing_out_i : std_logic_vector(4 downto 0); - ---for others: fifo use entity testchip_v2.fifo(behavior); ---for others: fifo1 use entity testchip_v2.fifo1(behavior); - -begin ---bypassable ---wr_internal_fifo <= '1' when (wrreq = '1' and empty_i = '1' and rdreq = '0') or (wrreq = '1' and empty_i = '0') else '0'; ---no bypassable -wr_internal_fifo <= wrreq; -rd_internal_fifo <= '1' when (rdreq = '1' and empty_i = '0') else '0'; - -QUEUE_INST1: if (depth = 1) generate -queue: fifo1 - generic map( - width => width) - port map( - clk => clk, - rst => rst, - - rdreq => rd_internal_fifo, - wrreq => wr_internal_fifo, - data_in => data_in, - - --request registers - empty => empty_i, - full => full, - data_out => fifo_data_out); -end generate; - -QUEUE_INST: if (depth > 1) generate -queue: fifo0 - generic map( - depth => depth, - width => width) - port map( - clk => clk, - rst => rst, - - rdreq => rd_internal_fifo, - wrreq => wr_internal_fifo, - data_in => data_in, - - --request registers - empty => empty_i, - full => full, - data_out => fifo_data_out); -end generate; - - -empty <= empty_i; -routing_out <= data_out_i(4 downto 0); -data_out <= data_out_i; - ---bypassable ---process(data_in, fifo_data_out, empty_i) ---begin --- if empty_i = '1' then --- data_out_i <= data_in; --- else --- data_out_i <= fifo_data_out; --- end if; ---end process; - ---no bypassable -data_out_i <= fifo_data_out; - -end behavior; diff --git a/rtl/noc/router.vhd b/rtl/noc/router.vhd index dc9361e44a..7339a674c1 100644 --- a/rtl/noc/router.vhd +++ b/rtl/noc/router.vhd @@ -31,976 +31,102 @@ use IEEE.STD_LOGIC_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; -entity router is - generic( - flow_control : integer := 0; --0 = AN; 1 = CB - width : integer := 34; - depth : integer := 4; - ports : std_logic_vector(4 downto 0) := "11111" --- localx : std_logic_vector(2 downto 0); --- localy : std_logic_vector(2 downto 0) - - ); - port( - clk : in std_logic; - rst : in std_logic; - --- CONST_ports : in std_logic_vector(4 downto 0); - CONST_localx : in std_logic_vector(2 downto 0); - CONST_localy : in std_logic_vector(2 downto 0); - - data_n_in : in std_logic_vector(width-1 downto 0); - data_s_in : in std_logic_vector(width-1 downto 0); - data_w_in : in std_logic_vector(width-1 downto 0); - data_e_in : in std_logic_vector(width-1 downto 0); - data_p_in : in std_logic_vector(width-1 downto 0); - - data_void_in : in std_logic_vector(4 downto 0); - stop_in : in std_logic_vector(4 downto 0); +use work.stdlib.all; - data_n_out : out std_logic_vector(width-1 downto 0); - data_s_out : out std_logic_vector(width-1 downto 0); - data_w_out : out std_logic_vector(width-1 downto 0); - data_e_out : out std_logic_vector(width-1 downto 0); - data_p_out : out std_logic_vector(width-1 downto 0); - - data_void_out : out std_logic_vector(4 downto 0); - stop_out : out std_logic_vector(4 downto 0)); +entity router is + generic( + flow_control : integer := 0; --0 = AN; 1 = CB + width : integer := 34; + depth : integer := 4; + ports : std_logic_vector(4 downto 0) := "11111" + ); + port( + clk : in std_logic; + rst : in std_logic; + + CONST_localx : in std_logic_vector(2 downto 0); + CONST_localy : in std_logic_vector(2 downto 0); + + data_n_in : in std_logic_vector(width-1 downto 0); + data_s_in : in std_logic_vector(width-1 downto 0); + data_w_in : in std_logic_vector(width-1 downto 0); + data_e_in : in std_logic_vector(width-1 downto 0); + data_p_in : in std_logic_vector(width-1 downto 0); + + data_void_in : in std_logic_vector(4 downto 0); + stop_in : in std_logic_vector(4 downto 0); + + data_n_out : out std_logic_vector(width-1 downto 0); + data_s_out : out std_logic_vector(width-1 downto 0); + data_w_out : out std_logic_vector(width-1 downto 0); + data_e_out : out std_logic_vector(width-1 downto 0); + data_p_out : out std_logic_vector(width-1 downto 0); + + data_void_out : out std_logic_vector(4 downto 0); + stop_out : out std_logic_vector(4 downto 0)); end router; architecture behavior of router is -component bypassable_queue - generic( - depth : integer; - width : integer --- localx : std_logic_vector(2 downto 0); --- localy : std_logic_vector(2 downto 0) - ); - - port( - clk : in std_logic; - rst : in std_logic; - --- CONST_localx : in std_logic_vector(2 downto 0); --- CONST_localy : in std_logic_vector(2 downto 0); - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0); - routing_out : out std_logic_vector(4 downto 0)); -end component; - -component nobypassable_queue - generic( - depth : integer; - width : integer --- localx : std_logic_vector(2 downto 0); --- localy : std_logic_vector(2 downto 0) - ); - - port( - clk : in std_logic; - rst : in std_logic; - --- CONST_localx : in std_logic_vector(2 downto 0); --- CONST_localy : in std_logic_vector(2 downto 0); - - rdreq : in std_logic; - wrreq : in std_logic; - data_in : in std_logic_vector(width-1 downto 0); - - --request registers - empty : out std_logic; - full : out std_logic; - data_out : out std_logic_vector(width-1 downto 0); - routing_out : out std_logic_vector(4 downto 0)); -end component; - -component routing_engine - generic( - loc_port : integer --- localx : std_logic_vector(2 downto 0); --- localy : std_logic_vector(2 downto 0) - ); - - port( - clk : in std_logic; - rst : in std_logic; - - localx : in std_logic_vector(2 downto 0); - localy : in std_logic_vector(2 downto 0); - - --current hop routing; one-hot encoding - destination_port : in std_logic_vector(4 downto 0); - destx : in std_logic_vector(2 downto 0); - desty : in std_logic_vector(2 downto 0); - - --next hop routing; one-hot encoded - next_routing : out std_logic_vector(4 downto 0)); -end component; - -component rtr_arbitration_engine - port( - clk : in std_logic; - rst : in std_logic; - - --requests registers; one-hot encoded like the parameter - requests : in std_logic_vector(3 downto 0); - - shift_priority : in std_logic; - update_priority : in std_logic; - lock_priority : in std_logic; - - valid_no_collision : out std_logic; - valid_collision : out std_logic; - --grant registers; one-hot encoded like the parameter - grant_no_collision : out std_logic_vector(3 downto 0); - grant_collision : out std_logic_vector(3 downto 0)); -end component; - -type data_t is array (0 to 4) of std_logic_vector(width-1 downto 0); -signal data_in, data_out, data_out_crossbar, fifo_head, last_flit : data_t; - -type routing_matching_t is array (0 to 4) of std_logic_vector(4 downto 0); -signal saved_routing_request, final_routing_request, routing_request, next_hop_routing, enhanc_routing_configuration : routing_matching_t; - -type transp_routing_matching_t is array (0 to 4) of std_logic_vector(3 downto 0); -signal transp_final_routing_request, grant_no_collision, grant_collision, saved_grant_collision, saved_grant_no_collision, routing_configuration, saved_routing_configuration : transp_routing_matching_t; - -type routing_clr_t is array (0 to 4) of std_logic_vector(4 downto 0); -signal rd_fifo, routing_clr : routing_clr_t; -signal rd_fifo_output : std_logic_vector(4 downto 0); - - ---type state_t is (idle, read_ctrl_msg, read); -type state_t_a is array (0 to 4) of std_logic_vector(1 downto 0); -signal state, new_state : state_t_a; - -signal in_unvalid_flit, out_unvalid_flit : std_logic_vector(4 downto 0); - -signal rd_fifo_or : std_logic_vector(4 downto 0); - -signal full, empty, wr_fifo : std_logic_vector(4 downto 0); -signal shift_priority, update_priority, lock_priority, data_void_in_d : std_logic_vector(4 downto 0); - -signal stop_out_i, data_void_out_i, valid_no_collision, valid_collision, last_flit_tail : std_logic_vector(4 downto 0); - ---constant depth : integer := 2; ---constant width : integer := 18; - -constant idle : std_logic_vector(1 downto 0) := "00"; -constant read_ctrl_msg : std_logic_vector(1 downto 0) := "01"; -constant read : std_logic_vector(1 downto 0) := "10"; -constant read_tail : std_logic_vector(1 downto 0) := "11"; - -constant max_credits : integer := 6; -subtype credits_tt is integer range 0 to depth; -type credits_t is array (0 to 4) of credits_tt; -signal credits : credits_t; - -signal forwarded_tail, forwarding_tail, forwarding_head, forwarded_head, forwarding_under_progress : std_logic_vector(4 downto 0); - -signal insert_lookahead_routing : std_logic_vector(4 downto 0); - -attribute keep : string; -attribute keep of data_n_in : signal is "true"; -attribute keep of data_s_in : signal is "true"; -attribute keep of data_w_in : signal is "true"; -attribute keep of data_e_in : signal is "true"; -attribute keep of data_p_in : signal is "true"; -attribute keep of data_void_in : signal is "true"; -attribute keep of stop_in : signal is "true"; -attribute keep of data_n_out : signal is "true"; -attribute keep of data_s_out : signal is "true"; -attribute keep of data_w_out : signal is "true"; -attribute keep of data_e_out : signal is "true"; -attribute keep of data_p_out : signal is "true"; -attribute keep of data_void_out : signal is "true"; -attribute keep of stop_out : signal is "true"; -attribute keep of data_in : signal is "true"; -attribute keep of data_out : signal is "true"; -attribute keep of data_out_crossbar : signal is "true"; -attribute keep of fifo_head : signal is "true"; -attribute keep of last_flit : signal is "true"; -attribute keep of saved_routing_request : signal is "true"; -attribute keep of final_routing_request : signal is "true"; -attribute keep of routing_request : signal is "true"; -attribute keep of next_hop_routing : signal is "true"; -attribute keep of enhanc_routing_configuration : signal is "true"; -attribute keep of transp_final_routing_request : signal is "true"; -attribute keep of grant_no_collision : signal is "true"; -attribute keep of grant_collision : signal is "true"; -attribute keep of saved_grant_collision : signal is "true"; -attribute keep of saved_grant_no_collision : signal is "true"; -attribute keep of routing_configuration : signal is "true"; -attribute keep of saved_routing_configuration : signal is "true"; -attribute keep of rd_fifo : signal is "true"; -attribute keep of routing_clr : signal is "true"; -attribute keep of rd_fifo_output : signal is "true"; -attribute keep of state : signal is "true"; -attribute keep of new_state : signal is "true"; -attribute keep of in_unvalid_flit : signal is "true"; -attribute keep of out_unvalid_flit : signal is "true"; -attribute keep of rd_fifo_or : signal is "true"; -attribute keep of full : signal is "true"; -attribute keep of empty : signal is "true"; -attribute keep of wr_fifo : signal is "true"; -attribute keep of shift_priority : signal is "true"; -attribute keep of update_priority : signal is "true"; -attribute keep of lock_priority : signal is "true"; -attribute keep of data_void_in_d : signal is "true"; -attribute keep of stop_out_i : signal is "true"; -attribute keep of data_void_out_i : signal is "true"; -attribute keep of valid_no_collision : signal is "true"; -attribute keep of valid_collision : signal is "true"; -attribute keep of last_flit_tail : signal is "true"; -attribute keep of credits : signal is "true"; -attribute keep of forwarded_tail : signal is "true"; -attribute keep of forwarding_tail : signal is "true"; -attribute keep of forwarding_head : signal is "true"; -attribute keep of forwarded_head : signal is "true"; -attribute keep of forwarding_under_progress : signal is "true"; -attribute keep of insert_lookahead_routing : signal is "true"; + component lookahead_router_wrapper + generic( + FlowControl : std_logic; + Width : integer; + Ports : std_logic_vector(4 downto 0) + ); + port( + clk : in std_logic; + rst : in std_logic; + + CONST_localx : in std_logic_vector(2 downto 0); + CONST_localy : in std_logic_vector(2 downto 0); + + data_n_in : in std_logic_vector(width-1 downto 0); + data_s_in : in std_logic_vector(width-1 downto 0); + data_w_in : in std_logic_vector(width-1 downto 0); + data_e_in : in std_logic_vector(width-1 downto 0); + data_p_in : in std_logic_vector(width-1 downto 0); + + data_void_in : in std_logic_vector(4 downto 0); + stop_in : in std_logic_vector(4 downto 0); + + data_n_out : out std_logic_vector(width-1 downto 0); + data_s_out : out std_logic_vector(width-1 downto 0); + data_w_out : out std_logic_vector(width-1 downto 0); + data_e_out : out std_logic_vector(width-1 downto 0); + data_p_out : out std_logic_vector(width-1 downto 0); + + data_void_out : out std_logic_vector(4 downto 0); + stop_out : out std_logic_vector(4 downto 0)); + end component; begin -data_in(0) <= data_n_in; -data_in(1) <= data_s_in; -data_in(2) <= data_w_in; -data_in(3) <= data_e_in; -data_in(4) <= data_p_in; - -data_n_out <= data_out(0); -data_s_out <= data_out(1); -data_w_out <= data_out(2); -data_e_out <= data_out(3); -data_p_out <= data_out(4); - -stop_out <= stop_out_i; - - -INPUT_FIFO : for i in 0 to 4 generate - INPUT_FIFO_SEL : if ports(i) = '1' generate - - VOID_IN_DELAY_AN: if flow_control = 0 generate - --ACK/NACK - process(clk, rst) - begin - if rst = '0' then - data_void_in_d(i) <= '1'; - elsif clk'event and clk = '1' then - if stop_out_i(i) = '0' then - data_void_in_d(i) <= data_void_in(i); - end if; - end if; - end process; - end generate; - - VOID_IN_DELAY_CB: if flow_control = 1 generate - --With CB stop_out and void_in are not correlated - data_void_in_d(i) <= data_void_in(i); - end generate; - - --read from the queue if any of the output requests data - rd_fifo_or(i) <= rd_fifo(0)(i) or rd_fifo(1)(i) or rd_fifo(2)(i) or rd_fifo(3)(i) or rd_fifo(4)(i); - --write in the fifo just valid data if the fifo is not backpressuring the channel - wr_fifo(i) <= (not data_void_in(i)) and (not full(i)); --TO CHECK: maybe the control on the full can be avoided - - INPUT_QUEUE_AN: if flow_control = 0 generate - INPUT_FIFO_i: bypassable_queue --ACKNACK or CB w/ bypassable queue - generic map( - depth => depth, - width => width --- localx => localx, --- localy => localy - ) - port map( - clk => clk, - rst => rst, - --- CONST_localx => CONST_localx, --- CONST_localy => CONST_localy, - - rdreq => rd_fifo_or(i), - wrreq => wr_fifo(i), - data_in => data_in(i), - - --request registers - empty => empty(i), - full => full(i), - data_out => fifo_head(i), - routing_out => routing_request(i)); - - in_unvalid_flit(i) <= empty(i) and data_void_in(i); - end generate; - - INPUT_QUEUE_CB: if flow_control = 1 generate - INPUT_FIFO_i: nobypassable_queue --CB w/ non-bypassable queue - generic map( - depth => depth, - width => width --- localx => localx, --- localy => localy - ) - port map( - clk => clk, - rst => rst, - --- CONST_localx => CONST_localx, --- CONST_localy => CONST_localy, - - rdreq => rd_fifo_or(i), - wrreq => wr_fifo(i), - data_in => data_in(i), - - --request registers - empty => empty(i), - full => full(i), - data_out => fifo_head(i), - routing_out => routing_request(i)); - - in_unvalid_flit(i) <= empty(i); - end generate; - - process(rst,clk) - begin - if rst = '0' then - last_flit_tail(i) <= '0'; - elsif clk'event and clk = '1' then - last_flit_tail(i) <= fifo_head(i)(width - 2); - end if; - end process; - - - ROUTING_REQUEST_LATCH_CB: if flow_control = 1 generate - --latching of the routing for the current worm - process(clk, rst) - begin - if rst = '0' then - saved_routing_request(i) <= (others => '0'); - elsif clk'event and clk = '1' then - if fifo_head(i)(width-2) = '1' then - saved_routing_request(i) <= (others => '0'); - --CB: why AN does not need to know if the FIFO is empty? Just because of the bypassable queue? - --Or because I never checked with FIFO_depth = packet_size? - --Response (12-27-09): with the bypassable queue you need to add the condition and you need an extra - --condition to take care of the bypassability => in_unvalid_flit is the right flac to use in both cases (TODO) - elsif fifo_head(i)(width-1) = '1' and empty(i) = '0' then - saved_routing_request(i) <= routing_request(i); - end if; - end if; - end process; - end generate; - - ROUTING_REQUEST_LATCH_AN: if flow_control = 0 generate - --latching of the routing for the current worm - process(clk, rst) - begin - if rst = '0' then - saved_routing_request(i) <= (others => '0'); - elsif clk'event and clk = '1' then - if fifo_head(i)(width-2) = '1' then - saved_routing_request(i) <= (others => '0'); - --ACKNACK: condition changed on Dec-27th-09 by MP to solve a reset problem - --in_unvalid_flit tells you if a new routing needs to be saved, o/w you keep the old one - --NOTE: matches with the condition above for the CB - elsif fifo_head(i)(width-1) = '1' and in_unvalid_flit(i) = '0' then - saved_routing_request(i) <= routing_request(i); - end if; - end if; - end process; - end generate; - - - FINAL_ROUTING_REQUEST_MUX_AN: if flow_control = 0 generate - process(fifo_head(i), routing_request(i), saved_routing_request(i), data_void_in(i), empty(i)) - begin - --ACKNACK or CB w/ bypassable queue - if fifo_head(i)(width-1) = '1' and ((data_void_in(i) = '0' and empty(i) = '1') or (empty(i) = '0')) then - final_routing_request(i) <= routing_request(i); - else - final_routing_request(i) <= saved_routing_request(i); - end if; - end process; - end generate; - - FINAL_ROUTING_REQUEST_MUX_CB: if flow_control = 1 generate - process(fifo_head(i), routing_request(i), saved_routing_request(i), data_void_in(i), empty(i)) - begin - --CB w/ nonbypassable queue - if fifo_head(i)(width-1) = '1' and empty(i) = '0' then - final_routing_request(i) <= routing_request(i); - else - final_routing_request(i) <= saved_routing_request(i); - end if; - end process; - end generate; - - - STOP_OUT_AN: if flow_control = 0 generate - --ACK/NACK - stop_out_i(i) <= full(i); - end generate; - - STOP_OUT_CB: if flow_control = 1 generate - --CB: I give a credit back every time a flit is read from the bypassable queue : w/ bypassable queue - --process(clk, rst) - --begin - -- if rst = '0' then - -- stop_out_i(i) <= '1'; - -- elsif clk'event and clk = '1' then - -- stop_out_i(i) <= not (rd_fifo_or(i) and (not in_unvalid_flit(i))); - -- end if; - --end process; - - --CB: I give a credit back every time a flit is read from the bypassable queue : w/ no bypassable queue - stop_out_i(i) <= not (rd_fifo_or(i) and (not in_unvalid_flit(i))); - end generate; - - - ROUTING_INPUT_i: routing_engine - generic map ( - loc_port => i --- localx => localx, --- localy => localy - ) - port map( - clk => clk, - rst => rst, - - localx => CONST_localx, - localy => CONST_localy, - - destination_port => fifo_head(i)(4 downto 0), - --move the destination address at the begining of the head flit - there were 2 bits for command - --destx => fifo_head(i)(width-6 downto width-8), - --desty => fifo_head(i)(width-3 downto width-5), - destx => fifo_head(i)(width-12 downto width-14), -- 14 - 12 - desty => fifo_head(i)(width-9 downto width-11), -- 17 - 15 - - --response registers; one-hot encoded like the parameter - next_routing => next_hop_routing(i)); - - end generate INPUT_FIFO_SEL; - - INPUT_FIFO_SEL_BAR : if ports(i) = '0' generate - stop_out_i(i) <= '1'; - final_routing_request(i) <= (others => '0'); - saved_routing_request(i) <= (others => '0'); - in_unvalid_flit(i) <= '1'; - fifo_head(i) <= (others => '0'); - routing_request(i) <= (others => '0'); - empty(i) <= '1'; - next_hop_routing(i) <= (others => '0'); - end generate INPUT_FIFO_SEL_BAR; - -end generate INPUT_FIFO; - -transp_final_routing_request(0)(0) <= final_routing_request(1)(0); -transp_final_routing_request(0)(1) <= final_routing_request(2)(0); -transp_final_routing_request(0)(2) <= final_routing_request(3)(0); -transp_final_routing_request(0)(3) <= final_routing_request(4)(0); - -transp_final_routing_request(1)(0) <= final_routing_request(0)(1); -transp_final_routing_request(1)(1) <= final_routing_request(2)(1); -transp_final_routing_request(1)(2) <= final_routing_request(3)(1); -transp_final_routing_request(1)(3) <= final_routing_request(4)(1); - -transp_final_routing_request(2)(0) <= final_routing_request(0)(2); -transp_final_routing_request(2)(1) <= final_routing_request(1)(2); -transp_final_routing_request(2)(2) <= final_routing_request(3)(2); -transp_final_routing_request(2)(3) <= final_routing_request(4)(2); - -transp_final_routing_request(3)(0) <= final_routing_request(0)(3); -transp_final_routing_request(3)(1) <= final_routing_request(1)(3); -transp_final_routing_request(3)(2) <= final_routing_request(2)(3); -transp_final_routing_request(3)(3) <= final_routing_request(4)(3); - -transp_final_routing_request(4)(0) <= final_routing_request(0)(4); -transp_final_routing_request(4)(1) <= final_routing_request(1)(4); -transp_final_routing_request(4)(2) <= final_routing_request(2)(4); -transp_final_routing_request(4)(3) <= final_routing_request(3)(4); - - -enhanc_routing_configuration(0)(0) <= '0'; -enhanc_routing_configuration(0)(1) <= routing_configuration(0)(0); -enhanc_routing_configuration(0)(2) <= routing_configuration(0)(1); -enhanc_routing_configuration(0)(3) <= routing_configuration(0)(2); -enhanc_routing_configuration(0)(4) <= routing_configuration(0)(3); - -enhanc_routing_configuration(1)(0) <= routing_configuration(1)(0); -enhanc_routing_configuration(1)(1) <= '0'; -enhanc_routing_configuration(1)(2) <= routing_configuration(1)(1); -enhanc_routing_configuration(1)(3) <= routing_configuration(1)(2); -enhanc_routing_configuration(1)(4) <= routing_configuration(1)(3); - -enhanc_routing_configuration(2)(0) <= routing_configuration(2)(0); -enhanc_routing_configuration(2)(1) <= routing_configuration(2)(1); -enhanc_routing_configuration(2)(2) <= '0'; -enhanc_routing_configuration(2)(3) <= routing_configuration(2)(2); -enhanc_routing_configuration(2)(4) <= routing_configuration(2)(3); - -enhanc_routing_configuration(3)(0) <= routing_configuration(3)(0); -enhanc_routing_configuration(3)(1) <= routing_configuration(3)(1); -enhanc_routing_configuration(3)(2) <= routing_configuration(3)(2); -enhanc_routing_configuration(3)(3) <= '0'; -enhanc_routing_configuration(3)(4) <= routing_configuration(3)(3); - -enhanc_routing_configuration(4)(0) <= routing_configuration(4)(0); -enhanc_routing_configuration(4)(1) <= routing_configuration(4)(1); -enhanc_routing_configuration(4)(2) <= routing_configuration(4)(2); -enhanc_routing_configuration(4)(3) <= routing_configuration(4)(3); -enhanc_routing_configuration(4)(4) <= '0'; - - -OUTPUT_CONTROL : for i in 0 to 4 generate - OUTPUT_CONTROL_SEL : if ports(i) = '1' generate - update_priority(i) <= '0'; - ARBITRATION_OUTPUT_i: rtr_arbitration_engine - port map( - clk => clk, - rst => rst, - - --requests registers; one-hot encoded like the parameter - requests => transp_final_routing_request(i), - shift_priority => shift_priority(i), - update_priority => update_priority(i), - lock_priority => lock_priority(i), - - valid_no_collision => valid_no_collision(i), - valid_collision => valid_collision(i), - --grant registers; one-hot encoded like the parameter - grant_no_collision => grant_no_collision(i), - grant_collision => grant_collision(i)); - - --Uncommented if TH < 1 --- process(clk, rst) --- begin --- if rst = '0' then --- saved_grant_collision(i) <= (others => '0'); --- elsif clk'event and clk = '1' then --- if valid_collision(i) = '1' then --- saved_grant_collision(i) <= grant_collision(i); ----- elsif data_out(i)(width - 2) = '1' then --- elsif forwarded_tail(i) = '1' then --- saved_grant_collision(i) <= (others => '0'); --- end if; --- end if; --- end process; - - process(clk, rst) - begin - if rst = '0' then - saved_grant_no_collision(i) <= (others => '0'); - elsif clk'event and clk = '1' then - if valid_no_collision(i) = '1' then - saved_grant_no_collision(i) <= grant_collision(i); - elsif forwarded_tail(i) = '1' then - saved_grant_no_collision(i) <= (others => '0'); - end if; - end if; - end process; - - process(clk, rst) - begin - if rst = '0' then - saved_routing_configuration(i) <= (others => '0'); - elsif clk'event and clk = '1' then - if forwarding_under_progress(i) = '1' then - saved_routing_configuration(i) <= routing_configuration(i); - elsif forwarded_tail(i) = '1' then - saved_routing_configuration(i) <= (others => '0'); - end if; - end if; - end process; - - shift_priority(i) <= forwarding_tail(i); - process(rst,clk) - begin - if rst = '0' then - lock_priority(i) <= '0'; - elsif clk'event and clk = '1' then - if forwarding_tail(i) = '1' then - lock_priority(i) <= '0'; - elsif forwarding_head(i) = '1' then - lock_priority(i) <= '1'; - end if; - end if; - end process; - - --TH = 1 : The lookahead routing has to be inserted on the first flit of a packet - process(rst,clk) - begin - if rst = '0' then - insert_lookahead_routing(i) <= '1'; - elsif clk'event and clk = '1' then - if forwarding_tail(i) = '1' then --and forwarding_under_progress(i) = '1' then - insert_lookahead_routing(i) <= '1'; - elsif forwarding_head(i) = '1' then --and forwarding_under_progress(i) = '1' then - insert_lookahead_routing(i) <= '0'; - end if; - end if; - end process; - - --crossbar - process(enhanc_routing_configuration(i), rd_fifo_output(i), fifo_head, next_hop_routing, in_unvalid_flit, forwarding_head(i), forwarding_under_progress(i), state(i), insert_lookahead_routing(i)) - begin - --if state(i) = idle then --TH < 1: after every packet the FSM goes in idle and that allows to insert the lookahead routing in the head flit - if insert_lookahead_routing(i) = '1' then --for TH = 1, because the FSM does not always go through idle before sending a head flit--and forwarding_under_progress(i) = '1' then - if enhanc_routing_configuration(i)(0) = '1' then - data_out_crossbar(i) <= fifo_head(0)(width-1 downto 5) & next_hop_routing(0); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(0) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(0); - - elsif enhanc_routing_configuration(i)(1) = '1' then - data_out_crossbar(i) <= fifo_head(1)(width-1 downto 5) & next_hop_routing(1); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(1) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(1); - - elsif enhanc_routing_configuration(i)(2) = '1' then - data_out_crossbar(i) <= fifo_head(2)(width-1 downto 5) & next_hop_routing(2); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(2) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(2); - - elsif enhanc_routing_configuration(i)(3) = '1' then - data_out_crossbar(i) <= fifo_head(3)(width-1 downto 5) & next_hop_routing(3); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(3) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(3); - - elsif enhanc_routing_configuration(i)(4) = '1' then - data_out_crossbar(i) <= fifo_head(4)(width-1 downto 5) & next_hop_routing(4); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(4) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(4); - - else - data_out_crossbar(i) <= (others => '0'); - rd_fifo(i) <= (others => '0'); - out_unvalid_flit(i) <= '1'; - end if; - else - if enhanc_routing_configuration(i)(0) = '1' then - data_out_crossbar(i) <= fifo_head(0); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(0) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(0); - - elsif enhanc_routing_configuration(i)(1) = '1' then - data_out_crossbar(i) <= fifo_head(1); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(1) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(1); - - elsif enhanc_routing_configuration(i)(2) = '1' then - data_out_crossbar(i) <= fifo_head(2); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(2) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(2); - - elsif enhanc_routing_configuration(i)(3) = '1' then - data_out_crossbar(i) <= fifo_head(3); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(3) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(3); - - elsif enhanc_routing_configuration(i)(4) = '1' then - data_out_crossbar(i) <= fifo_head(4); - rd_fifo(i) <= (others => '0'); - rd_fifo(i)(4) <= rd_fifo_output(i); - out_unvalid_flit(i) <= in_unvalid_flit(4); - - else - data_out_crossbar(i) <= (others => '0'); - rd_fifo(i) <= (others => '0'); - out_unvalid_flit(i) <= '1'; - end if; - - end if; - end process; - - MISC_AN: if flow_control = 0 generate - --ACKNACK - rd_fifo_output(i) <= not stop_in(i); - forwarded_tail(i) <= data_out(i)(width - 2) and (not stop_in(i)); - forwarded_head(i) <= data_out(i)(width - 1) and (not stop_in(i)); - forwarding_tail(i) <= '1' when (data_out_crossbar(i)(width-2) = '1' and out_unvalid_flit(i) = '0' and stop_in(i) = '0') else '0'; - forwarding_head(i) <= '1' when (data_out_crossbar(i)(width-1) = '1' and out_unvalid_flit(i) = '0' and stop_in(i) = '0') else '0'; - end generate; - - MISC_CB: if flow_control = 1 generate - --CB - rd_fifo_output(i) <= '1' when credits(i) > 0 else '0'; - --w/ output FF - --forwarded_tail(i) <= '1' when (data_out(i)(width - 2) = '1' and data_void_out_i(i) = '0') else '0'; -- and credits(i) > 0) else '0'; - --forwarded_head(i) <= '1' when (data_out(i)(width - 1) = '1' and data_void_out_i(i) = '0') else '0'; -- and credits(i) > 0) else '0'; - --w/o output FF - forwarded_tail(i) <= '1' when (last_flit(i)(width - 2) = '1') else '0'; -- and credits(i) > 0) else '0'; - forwarded_head(i) <= '1' when (last_flit(i)(width - 1) = '1') else '0'; -- and credits(i) > 0) else '0'; - forwarding_tail(i) <= '1' when (data_out_crossbar(i)(width-2) = '1' and out_unvalid_flit(i) = '0' and credits(i) > 0) else '0'; - forwarding_head(i) <= '1' when (data_out_crossbar(i)(width-1) = '1' and out_unvalid_flit(i) = '0' and credits(i) > 0) else '0'; - end generate; - - - process(state(i), valid_collision(i), valid_no_collision(i), grant_no_collision(i), grant_collision(i), saved_routing_configuration(i), data_out_crossbar(i), data_out(i), stop_in(i), forwarded_tail(i), forwarding_tail(i), rd_fifo_output(i)) - begin - case state(i) is - - when idle => - --if a configuration is obtained forward the head flit - if valid_no_collision(i) = '1' and rd_fifo_output(i) = '1' then - routing_configuration(i) <= grant_no_collision(i); - if data_out_crossbar(i)(width - 2) = '1' then - forwarding_under_progress(i) <= '1'; - new_state(i) <= read_ctrl_msg; - else - forwarding_under_progress(i) <= '1'; - new_state(i) <= read; - end if; - --uncomment if TH < 1 - --elsif valid_collision(i) = '1' and rd_fifo_output(i) = '1' then - -- routing_configuration(i) <= grant_collision(i); - -- if data_out_crossbar(i)(width - 2) = '1' then - -- forwarding_under_progress(i) <= '0'; - -- new_state(i) <= read_ctrl_msg; - -- else - -- forwarding_under_progress(i) <= '1'; - -- new_state(i) <= read; - -- end if; - else - routing_configuration(i) <= (others => '0'); - forwarding_under_progress(i) <= '0'; - new_state(i) <= idle; - end if; - - when read_ctrl_msg => - if rd_fifo_output(i) = '1' then - --added for TH = 1 - if valid_no_collision(i) = '1' then - routing_configuration(i) <= grant_no_collision(i); - if data_out_crossbar(i)(width - 2) = '1' then - forwarding_under_progress(i) <= data_out_crossbar(i)(width - 1); -- considering single-flit packets! - new_state(i) <= read_ctrl_msg; - else - forwarding_under_progress(i) <= '1'; - new_state(i) <= read; - end if; - else - --until here added for TH = 1 - routing_configuration(i) <= (others => '0'); - forwarding_under_progress(i) <= '0'; - new_state(i) <= idle; - --added for TH = 1 - end if; - --until here added for TH = 1 - else - routing_configuration(i) <= (others => '0'); - forwarding_under_progress(i) <= '1'; - new_state(i) <= read_ctrl_msg; - end if; - - when read => - --New note: equal for both CB and ACKNACK - if forwarded_tail(i) = '1' then --ACK/NACK for TH < 1 - --TH < 1 - --routing_configuration(i) <= (others => '0'); - --forwarding_under_progress(i) <= '0'; - --new_state(i) <= idle; - --until here: TH < 1 - --added for TH = 1 - if valid_no_collision(i) = '1' then - routing_configuration(i) <= grant_no_collision(i); - if data_out_crossbar(i)(width - 2) = '1' then - forwarding_under_progress(i) <= data_out_crossbar(i)(width - 1); -- considering single-flit packets! - new_state(i) <= read_ctrl_msg; - else - forwarding_under_progress(i) <= '1'; - new_state(i) <= read; - end if; - else - routing_configuration(i) <= (others => '0'); - forwarding_under_progress(i) <= '0'; - new_state(i) <= idle; - end if; - --until here added for TH = 1 - else - routing_configuration(i) <= saved_routing_configuration(i); - forwarding_under_progress(i) <= '1'; - new_state(i) <= read; - end if; - - when others => - routing_configuration(i) <= (others => '0'); - forwarding_under_progress(i) <= '0'; - new_state(i) <= idle; - - end case; - end process; - - process(clk, rst) - begin - if rst = '0' then - state(i) <= idle; - elsif clk'event and clk = '1' then - state(i) <= new_state(i); - end if; - end process; - - DATA_OUT_AN: if flow_control = 0 generate - --ACK/NACK - process(rst, clk) - begin - if rst = '0' then - data_out(i) <= (others => '0'); - elsif clk'event and clk = '1' then - if stop_in(i) = '0' and forwarding_under_progress(i) = '1' and out_unvalid_flit(i) = '0' then - data_out(i) <= data_out_crossbar(i); - end if; - end if; - end process; - - process(rst, clk) - begin - if rst = '0' then - data_void_out_i(i) <= '1'; - elsif clk'event and clk = '1' then - --if new_state(i) = idle or (out_unvalid_flit(i) = '1' and stop_in(i) = '0') then - -- data_void_out_i(i) <= '1'; - --else - -- data_void_out_i(i) <= out_unvalid_flit(i); - --end if; - if new_state(i) = idle then - data_void_out_i(i) <= '1'; - elsif stop_in(i) = '0' then - data_void_out_i(i) <= out_unvalid_flit(i); - end if; - end if; - end process; - end generate; - - - DATA_OUT_CB: if flow_control = 1 generate - --CB: a flit is sent only if credits > 0 : with output FF - --process(rst, clk) - --begin - -- if rst = '0' then - -- data_out(i) <= (others => '0'); - -- elsif clk'event and clk = '1' then - -- --question: forwarding_under_progress instead of idle? - -- if credits(i) > 0 and new_state(i) /= idle and out_unvalid_flit(i) = '0' then - -- data_out(i) <= data_out_crossbar(i); - -- end if; - -- end if; - --end process; - - --CB: a flit is sent only if credits > 0 : w/o output FF - data_out(i) <= data_out_crossbar(i); - - --CB: a flit is sent only if credits > 0 : with output FF - --process(rst, clk) - --begin - -- if rst = '0' then - -- data_void_out_i(i) <= '1'; - -- credits(i) <= depth; - -- elsif clk'event and clk = '1' then - -- if credits(i) > 0 and new_state(i) /= idle and out_unvalid_flit(i) = '0' then - -- data_void_out_i(i) <= '0'; - -- if stop_in(i) = '1' then - -- credits(i) <= credits(i) - 1; - -- end if; - -- else - -- data_void_out_i(i) <= '1'; - -- if stop_in(i) = '0' then - -- credits(i) <= credits(i) + 1; - -- end if; - -- end if; - -- end if; - --end process; - - --CB: a flit is sent only if credits > 0 : w/o output FF - process(rst, clk) - begin - if rst = '0' then - credits(i) <= depth; - elsif clk'event and clk = '1' then - if data_void_out_i(i) ='0' then - if stop_in(i) = '1' then - credits(i) <= credits(i) - 1; - end if; - else - if stop_in(i) = '0' then - credits(i) <= credits(i) + 1; - end if; - end if; - end if; - end process; - data_void_out_i(i) <= '0' when (credits(i) > 0 and new_state(i) /= idle and out_unvalid_flit(i) = '0') else '1'; - end generate; - - - data_void_out(i) <= data_void_out_i(i); - - - process(rst, clk) - begin - if rst = '0' then - last_flit(i) <= (others => '0'); - elsif clk = '1' and clk'event then - if data_void_out_i(i) = '0' then - last_flit(i) <= data_out_crossbar(i); - end if; - end if; - end process; - - end generate OUTPUT_CONTROL_SEL; - - OUTPUT_CONTROL_SEL_BAR : if ports(i) = '0' generate - valid_no_collision(i) <= '0'; - valid_collision(i) <= '0'; - grant_no_collision(i) <= (others => '0'); - grant_collision(i) <= (others => '0'); - data_void_out(i) <= '1'; - data_out(i) <= (others => '0'); - routing_configuration(i) <= (others => '0'); - data_out_crossbar(i) <= (others => '0'); - rd_fifo(i) <= (others => '0'); - out_unvalid_flit(i) <= '1'; - shift_priority(i) <= '0'; - lock_priority(i) <= '0'; - saved_routing_configuration(i) <= (others => '0'); - saved_grant_no_collision(i) <= (others => '0'); - saved_grant_collision(i) <= (others => '0'); - rd_fifo_output(i) <= '0'; - rd_fifo_or(i) <= '0'; - wr_fifo(i) <= '0'; - update_priority(i) <= '0'; - data_void_in_d(i) <= '1'; - data_void_out_i(i) <= '1'; - last_flit_tail(i) <= '0'; - forwarded_tail(i) <= '0'; - forwarding_tail(i) <= '0'; - forwarding_head(i) <= '0'; - forwarded_head(i) <= '0'; - forwarding_under_progress(i) <= '0'; - insert_lookahead_routing(i) <= '0'; - full(i) <= '0'; - DISCONNECTED_PORT_CB: if flow_control = 1 generate - credits(i) <= 0; - end generate; - end generate OUTPUT_CONTROL_SEL_BAR; - -end generate OUTPUT_CONTROL; - + lookahead_router_wrapper_i: lookahead_router_wrapper + generic map ( + FlowControl => to_std_logic(flow_control), + Width => width, + Ports => Ports) + port map ( + clk => clk, + rst => rst, + CONST_localx => CONST_localx, + CONST_localy => CONST_localy, + data_n_in => data_n_in, + data_s_in => data_s_in, + data_w_in => data_w_in, + data_e_in => data_e_in, + data_p_in => data_p_in, + data_void_in => data_void_in, + stop_in => stop_in, + data_n_out => data_n_out, + data_s_out => data_s_out, + data_w_out => data_w_out, + data_e_out => data_e_out, + data_p_out => data_p_out, + data_void_out => data_void_out, + stop_out => stop_out); end behavior; diff --git a/rtl/noc/router/lookahead_router.sv b/rtl/noc/router/lookahead_router.sv new file mode 100644 index 0000000000..57239cb6b8 --- /dev/null +++ b/rtl/noc/router/lookahead_router.sv @@ -0,0 +1,518 @@ +// 2D mesh NoC router +// +// This module is a 5x5 router with 1 local port and 4 link ports (north, south, west, east) to +// route data. The routing algorithm is XY Dimension Order. The router uses a worm-hole flow-control +// at network level and an ACK/NACK or credit-based flow control at link level. Links can tolerate +// wire pipeline through the insertion of relay stations. The router implements routing look-ahead, +// performing routing for the following hop and carrying the routing result into the head flit of +// the worm. In case of incoming head flit directed to a free output without contention the flit is +// forwarded in a single clock cycle. In case of contention, the worm arriving from the port with +// the current highest priority is forwarded one cycle after the tail flit of the previous worm. +// +// This module has been implemented in SystemVerilog based on the original VHDL implementation from +// the Columbia University open-source project ESP: https://github.com/sld-columbia/esp +// +// The original copyright notice and author information are included below. +// +// Interface +// +// * Inputs +// - clk: all signals are synchronous to this clock signal. +// - rst: active high reset +// - position: static input that encodes the x,y coordinates of the router on the mesh. +// - data_X_in: input data for each port (North, South, West, East, Local). +// - data_void_in: each bit indicates if the corresponding data_X_in holds valid data. +// - stop_in: when using ACK/NACK flow control, stop_in[X] is 0 to indicate that the corresponding +// output port X is ready to accept a new flit; when using credit-based flow control, stop_in[X] +// is 0 to send credits back for the output port X. +// +// * Outputs +// - data_X_out: output data for each port (North, South, West, East, Local). +// - data_void_out: each bit indicates if the corresponding data_X_out holds valid data. +// - stop_out: when using ACK/NACK flow control, stop_out[X] is 0 to indicate that the corresponding +// input port X is ready to accept a new flit; when using credit-based flow control, stop_in[X] is +// 0 to send credits back for the input port X. +// +// * Parameters +// - FlowControl: either ACK/NACK (stop-void) or credit-based +// - DataWidth: width of the router port, except for the two preaamble bits indicating head and +// tail. DataWidth must be large enough to hold the header flit information for routing: +// DataWidth > $bits(noc::packet_info_t) + $bits(noc::direction_t). +// - PortWidth: DataWidth + 2. This parameter is used to define input ports. +// and should not be overwritten. +// - Ports: each bit is set to 1 to indicate that the corresponding input and output port is +// enabled. This parameter can be used to disable ports on the fringe of the NoC mesh. +// + +//////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2011-2022 Columbia University, System Level Design Group +// SPDX-License-Identifier: Apache-2.0 +// Author: Michele Petracca +//////////////////////////////////////////////////////////////////////////////// + +module lookahead_router + #( + parameter noc::noc_flow_control_t FlowControl = noc::kFlowControlAckNack, + parameter int unsigned DataWidth = 32, + parameter int unsigned PortWidth = DataWidth + $bits(noc::preamble_t), + parameter bit [4:0] Ports = noc::AllPorts + ) + ( + input logic clk, + input logic rst, + // Coordinates + input noc::xy_t position, + // Input ports + input logic [PortWidth-1:0] data_n_in, + input logic [PortWidth-1:0] data_s_in, + input logic [PortWidth-1:0] data_w_in, + input logic [PortWidth-1:0] data_e_in, + input logic [PortWidth-1:0] data_p_in, + input logic [4:0] data_void_in, + output logic [4:0] stop_out, + // Output ports + output logic [PortWidth-1:0] data_n_out, + output logic [PortWidth-1:0] data_s_out, + output logic [PortWidth-1:0] data_w_out, + output logic [PortWidth-1:0] data_e_out, + output logic [PortWidth-1:0] data_p_out, + output logic [4:0] data_void_out, + input logic [4:0] stop_in + ); + + localparam bit FifoBypassEnable = FlowControl == noc::kFlowControlAckNack; + + localparam int unsigned ReservedWidth = + DataWidth - $bits(noc::packet_info_t) - $bits(noc::direction_t); + + typedef struct packed { + noc::preamble_t preamble; + noc::packet_info_t info; + logic [ReservedWidth-1:0] reserved; + noc::direction_t routing; + } header_t; + + typedef logic [PortWidth-1:0] payload_t; + + typedef union packed { + header_t header; + payload_t flit; + } flit_t; + + typedef enum logic { + kHeadFlit = 1'b0, + kPayloadFlits = 1'b1 + } state_t; + + state_t [4:0] state; + state_t [4:0] new_state; + + flit_t [4:0] data_in; + flit_t [4:0] fifo_head; + flit_t [4:0] data_out_crossbar; + flit_t [4:0] last_flit; + + logic [4:0][4:0] saved_routing_request; + logic [4:0][4:0] final_routing_request; // ri lint_check_waive NOT_READ + logic [4:0][4:0] next_hop_routing; + + logic [4:0][3:0] transp_final_routing_request; + + logic [4:0][4:0] enhanc_routing_configuration; + + logic [4:0][3:0] routing_configuration; + logic [4:0][3:0] saved_routing_configuration; + logic [4:0][3:0] grant; + logic [4:0] grant_valid; + + logic [4:0][4:0] rd_fifo; + logic [4:0] no_backpressure; + logic [4:0] rd_fifo_or; + + logic [4:0] in_unvalid_flit; + logic [4:0] out_unvalid_flit; + logic [4:0] in_valid_head; + + logic [4:0] full; + logic [4:0] empty; + logic [4:0] wr_fifo; + + noc::credits_t credits; + + logic [4:0] forwarding_tail; + logic [4:0] forwarding_head; + logic [4:0] forwarding_in_progress; + logic [4:0] insert_lookahead_routing; + + assign data_in[noc::kNorthPort] = data_n_in; + assign data_in[noc::kSouthPort] = data_s_in; + assign data_in[noc::kWestPort] = data_w_in; + assign data_in[noc::kEastPort] = data_e_in; + assign data_in[noc::kLocalPort] = data_p_in; + + // This router has a single cycle delay. + // When using ready-valid protocol, the register is placed at the output; for credit-based, + // the register is the input FIFO (not bypassable) and the output of the crossbar is not + // registered. + assign data_n_out = FifoBypassEnable ? last_flit[noc::kNorthPort] : + data_out_crossbar[noc::kNorthPort]; + assign data_s_out = FifoBypassEnable ? last_flit[noc::kSouthPort] : + data_out_crossbar[noc::kSouthPort]; + assign data_w_out = FifoBypassEnable ? last_flit[noc::kWestPort] : + data_out_crossbar[noc::kWestPort]; + assign data_e_out = FifoBypassEnable ? last_flit[noc::kEastPort] : + data_out_crossbar[noc::kEastPort]; + assign data_p_out = FifoBypassEnable ? last_flit[noc::kLocalPort] : + data_out_crossbar[noc::kLocalPort]; + + genvar g_i; + + ////////////////////////////////////////////////////////////////////////////// + // Input FIFOs and look-ahead routing + ////////////////////////////////////////////////////////////////////////////// + for (g_i = 0; g_i < 5; g_i++) begin : gen_input_fifo + if (Ports[g_i]) begin : gen_input_port_enabled + + // Read FIFO if any of the output ports requests data. + // The FIFO won't update read pointer if empty + assign rd_fifo_or[g_i] = rd_fifo[0][g_i] | rd_fifo[1][g_i] | rd_fifo[2][g_i] | + rd_fifo[3][g_i] | rd_fifo[4][g_i]; + + // Write FIFO if data is valid. + // The FIFO won't accept the write if full. + assign wr_fifo[g_i] = ~data_void_in[g_i]; + + // Input FIFO + router_fifo + #( + .BypassEnable(FifoBypassEnable), + .Depth(noc::PortQueueDepth), + .Width(PortWidth) + ) + input_queue ( + .clk, + .rst, + .rdreq(rd_fifo_or[g_i]), + .wrreq(wr_fifo[g_i]), + .data_in(data_in[g_i]), + .empty(empty[g_i]), + .full(full[g_i]), + .data_out(fifo_head[g_i]) + ); + + assign in_unvalid_flit[g_i] = FifoBypassEnable ? empty[g_i] & data_void_in[g_i] : empty[g_i]; + assign in_valid_head[g_i] = fifo_head[g_i].header.preamble.head & ~in_unvalid_flit[g_i]; + + always_ff @(posedge clk) begin + if (rst) begin + saved_routing_request[g_i] <= '0; + end else begin + if (fifo_head[g_i].header.preamble.tail) begin + // Clear saved_routing_request if tail is next + saved_routing_request[g_i] <= '0; + end else if (in_valid_head[g_i]) begin + // Sample saved_routing_request if valid head flit + saved_routing_request[g_i] <= fifo_head[g_i].header.routing; + end + end + end + + assign final_routing_request[g_i] = in_valid_head[g_i] ? fifo_head[g_i].header.routing : + saved_routing_request[g_i]; + + // AckNack: stop data at input port if FIFO is full + // CreditBased: send credits when reading from the input FIFO + assign stop_out[g_i] = FifoBypassEnable ? full[g_i] : + ~(rd_fifo_or[g_i] & ~in_unvalid_flit[g_i]); + + lookahead_routing lookahead_routing_i + ( + .clk, + .position, + .destination(fifo_head[g_i].header.info.destination), + .current_routing(fifo_head[g_i].header.routing), + .next_routing(next_hop_routing[g_i]) + ); + + end else begin : gen_input_port_disabled + + assign stop_out[g_i] = 1'b1; + assign final_routing_request[g_i] = '0; + assign saved_routing_request[g_i] = '0; + assign in_unvalid_flit[g_i] = '1; + assign fifo_head[g_i] = '0; + assign empty[g_i] = 1'b1; + assign full[g_i] = '0; + assign next_hop_routing[g_i] = '0; + assign rd_fifo_or[g_i] = '0; + assign wr_fifo[g_i] = '0; + assign in_valid_head[g_i] = 1'b0; + + end // if (Ports[g_i]) + + end // for gen_input_fifo + + + ////////////////////////////////////////////////////////////////////////////// + // Output crossbar and arbitration + ////////////////////////////////////////////////////////////////////////////// + for (g_i = 0; g_i < 5; g_i++) begin : gen_output_control + if (Ports[g_i]) begin : gen_output_port_enabled + + genvar g_j; + for (g_j = 0; g_j < 5; g_j++) begin : gen_transpose_routing + // transpose current routing request for easier accesss, but + // allow routing only to output port different from input port + if (g_j < g_i) begin : gen_transpose_routin_j_lt_i + assign transp_final_routing_request[g_i][g_j] = final_routing_request[g_j][g_i]; + assign enhanc_routing_configuration[g_i][g_j] = routing_configuration[g_i][g_j]; + end else if (g_j > g_i) begin : gen_transpose_routin_j_gt_i + assign transp_final_routing_request[g_i][g_j-1] = final_routing_request[g_j][g_i]; + assign enhanc_routing_configuration[g_i][g_j] = routing_configuration[g_i][g_j-1]; + end else begin : gen_transpose_routin_j_eq_i + assign enhanc_routing_configuration[g_i][g_j] = 1'b0; + end + end // for gen_transpose_routing + + // Arbitration + router_arbiter arbiter_i ( + .clk(clk), + .rst(rst), + .request(transp_final_routing_request[g_i]), + .forwarding_head(forwarding_head[g_i]), + .forwarding_tail(forwarding_tail[g_i]), + .grant(grant[g_i]), + .grant_valid(grant_valid[g_i]) + ); + + // Sample current routing configuration + always_ff @(posedge clk) begin + if (forwarding_in_progress[g_i]) begin + saved_routing_configuration[g_i] <= routing_configuration[g_i]; + end + end + + // Set to overwrite routing info only on the head flit + always_ff @(posedge clk) begin + if (rst) begin + // First flit must be head + insert_lookahead_routing[g_i] <= 1'b1; + end else begin + if (forwarding_tail[g_i]) begin + // Next flit will be head (convers single-flit packet) + insert_lookahead_routing[g_i] <= 1'b1; + end else if (forwarding_head[g_i]) begin + // Next flit will not be head + insert_lookahead_routing[g_i] <= 1'b0; + end + end + end + + // Crossbar + always_comb begin + data_out_crossbar[g_i] = '0; + rd_fifo[g_i] = '0; + out_unvalid_flit[g_i] = 1'b1; + + unique case (enhanc_routing_configuration[g_i]) + noc::goNorth : begin + data_out_crossbar[g_i] = ~insert_lookahead_routing[g_i] ? fifo_head[noc::kNorthPort] : + {fifo_head[noc::kNorthPort].flit[PortWidth-1:5], next_hop_routing[noc::kNorthPort]}; + rd_fifo[g_i][noc::kNorthPort] = no_backpressure[g_i]; + out_unvalid_flit[g_i] = in_unvalid_flit[noc::kNorthPort]; + end + + noc::goSouth : begin + data_out_crossbar[g_i] = ~insert_lookahead_routing[g_i] ? fifo_head[noc::kSouthPort] : + {fifo_head[noc::kSouthPort].flit[PortWidth-1:5], next_hop_routing[noc::kSouthPort]}; + rd_fifo[g_i][noc::kSouthPort] = no_backpressure[g_i]; + out_unvalid_flit[g_i] = in_unvalid_flit[noc::kSouthPort]; + end + + noc::goWest : begin + data_out_crossbar[g_i] = ~insert_lookahead_routing[g_i] ? fifo_head[noc::kWestPort] : + {fifo_head[noc::kWestPort].flit[PortWidth-1:5], next_hop_routing[noc::kWestPort]}; + rd_fifo[g_i][noc::kWestPort] = no_backpressure[g_i]; + out_unvalid_flit[g_i] = in_unvalid_flit[noc::kWestPort]; + end + + noc::goEast : begin + data_out_crossbar[g_i] = ~insert_lookahead_routing[g_i] ? fifo_head[noc::kEastPort] : + {fifo_head[noc::kEastPort].flit[PortWidth-1:5], next_hop_routing[noc::kEastPort]}; + rd_fifo[g_i][noc::kEastPort] = no_backpressure[g_i]; + out_unvalid_flit[g_i] = in_unvalid_flit[noc::kEastPort]; + end + + noc::goLocal : begin + data_out_crossbar[g_i] = ~insert_lookahead_routing[g_i] ? fifo_head[noc::kLocalPort] : + {fifo_head[noc::kLocalPort].flit[PortWidth-1:5], next_hop_routing[noc::kLocalPort]}; + rd_fifo[g_i][noc::kLocalPort] = no_backpressure[g_i]; + out_unvalid_flit[g_i] = in_unvalid_flit[noc::kLocalPort]; + end + + default : begin + end + endcase + end + + + // Sample output + always_ff @(posedge clk) begin + if (rst) begin + last_flit[g_i] <= '0; + end else begin + if (FifoBypassEnable) begin + if (no_backpressure[g_i] & forwarding_in_progress[g_i] & ~out_unvalid_flit[g_i]) begin + last_flit[g_i] <= data_out_crossbar[g_i]; + end + end else begin + if (~data_void_out[g_i]) begin + last_flit[g_i] <= data_out_crossbar[g_i]; + end + end + end + end + + // Flow control + assign no_backpressure[g_i] = FifoBypassEnable ? ~stop_in[g_i] : credits[g_i] != '0; + assign forwarding_tail[g_i] = data_out_crossbar[g_i].header.preamble.tail & + ~out_unvalid_flit[g_i] & no_backpressure[g_i]; + assign forwarding_head[g_i] = data_out_crossbar[g_i].header.preamble.head & + ~out_unvalid_flit[g_i] & no_backpressure[g_i]; + + always_comb begin : flow_control_fsm + new_state[g_i] = state[g_i]; + routing_configuration[g_i] = '0; + forwarding_in_progress[g_i] = 1'b0; + + unique case (state[g_i]) + kHeadFlit : begin + if (grant_valid[g_i] & no_backpressure[g_i]) begin + // First flit of a new packet can be forwarded + routing_configuration[g_i] = grant[g_i]; + forwarding_in_progress[g_i] = 1'b1; + if (~data_out_crossbar[g_i].header.preamble.tail) begin + // Non-single-flit packet; expecting more payload flit + new_state[g_i] = kPayloadFlits; + end + end + end + + kPayloadFlits : begin + // Payload of a packet is being forwarded; do not change routing configuration + routing_configuration[g_i] = saved_routing_configuration[g_i]; + forwarding_in_progress[g_i] = 1'b1; + if (forwarding_tail[g_i]) begin + // Next flit must be head + new_state[g_i] = kHeadFlit; + end + end + + default : begin + end + endcase // unique case (state[g_i]) + end + + always_ff @(posedge clk) begin + if (rst) begin + state[g_i] <= kHeadFlit; + end else begin + state[g_i] <= new_state[g_i]; + end + end + + // Data void out and credits + if (FifoBypassEnable) begin : gen_data_void_out_acknack + always_ff @(posedge clk) begin + if (rst) begin + data_void_out[g_i] <= 1'b1; + end else begin + if (~forwarding_in_progress[g_i] && no_backpressure[g_i]) begin + data_void_out[g_i] <= 1'b1; + end else if (no_backpressure[g_i]) begin + data_void_out[g_i] <= out_unvalid_flit[g_i]; + end + end + end + assign credits[g_i] = '0; + end else begin : gen_data_void_out_creditbased + assign data_void_out[g_i] = forwarding_in_progress[g_i] & no_backpressure[g_i] ? + out_unvalid_flit[g_i] : 1'b1; + always_ff @(posedge clk) begin + if (rst) begin + credits[g_i] = noc::PortQueueDepth; + end else begin + if (~data_void_out[g_i]) begin + credits[g_i] = credits[g_i] - stop_in[g_i]; + end else begin + credits[g_i] = credits[g_i] + ~stop_in[g_i]; + end + end + end + end + + + + end else begin : gen_input_port_disabled + assign grant_valid[g_i] = '0; + assign grant[g_i] = '0; + assign data_void_out[g_i] = '1; + assign out_unvalid_flit[g_i] = '1; + assign data_out_crossbar[g_i] = '0; + assign last_flit[g_i] = '0; + assign routing_configuration[g_i] = '0; + assign saved_routing_configuration[g_i] = '0; + assign rd_fifo[g_i] = '0; + assign no_backpressure[g_i] = '1; + assign forwarding_tail[g_i] = '0; + assign forwarding_head[g_i] = '0; + assign forwarding_in_progress[g_i] = '0; + assign insert_lookahead_routing[g_i] = '0; + assign credits[g_i] = '0; + end // block: gen_output_port_enabled + + end // for gen_output_control + + ////////////////////////////////////////////////////////////////////////////// + // Assertions + ////////////////////////////////////////////////////////////////////////////// + +`ifndef SYNTHESIS +// pragma coverage off +//VCS coverage off + + if (DataWidth < $bits(noc::packet_info_t) + $bits(noc::direction_t)) begin : gen_a_data_width + $fatal(2'd2, "Fail: DataWidth insufficient to hold packet and routing information."); + end + + if ($bits(header_t) != DataWidth + $bits(noc::preamble_t)) begin : gen_a_header_width + $fatal(2'd2, "Fail: header_t width (%02d) must be DataWidth (%02d) + preamble_t width (%01d)", + $bits(header_t), DataWidth, $bits(noc::preamble_t)); + end + + if (PortWidth != $bits(header_t)) begin : gen_a_port_width + $fatal(2'd2, "Fail: PortWidth must match header_t width."); + end + + for (g_i = 0; g_i < 4; g_i++) begin : gen_assert_legal_routing_request + a_no_request_to_same_port: assert property (@(posedge clk) disable iff(rst) + final_routing_request[g_i][g_i] == 1'b0) + else $error("Fail: a_no_request_to_same_port"); + a_enhanc_routing_configuration_onehot: assert property (@(posedge clk) disable iff(rst) + $onehot0(enhanc_routing_configuration[g_i])) + else $error("Fail: a_enhanc_routing_configuration_onehot"); + a_expect_head_flit: assert property (@(posedge clk) disable iff(rst) + ~out_unvalid_flit[g_i] & state[g_i] == kHeadFlit + |-> + data_out_crossbar[g_i].header.preamble.head) + else $error("Fail: a_expect_head_flit"); + a_credits_in_range: assert property (@(posedge clk) disable iff(rst) + credits[g_i] <= noc::PortQueueDepth) + else $error("Fail: a_enhanc_routing_configuration_onehot"); + end + +// pragma coverage on +//VCS coverage on +`endif // ~SYNTHESIS + +endmodule diff --git a/rtl/noc/router/lookahead_router_wrapper.sv b/rtl/noc/router/lookahead_router_wrapper.sv new file mode 100644 index 0000000000..59d0c97f7d --- /dev/null +++ b/rtl/noc/router/lookahead_router_wrapper.sv @@ -0,0 +1,61 @@ +module lookahead_router_wrapper + #( + parameter bit FlowControl, + parameter int unsigned Width = 32, + parameter bit [4:0] Ports = noc::AllPorts + ) + ( + input logic clk, + input logic rst, + // Coordinates + input logic[2:0] CONST_localx, + input logic[2:0] CONST_localy, + // Input ports + input logic [Width-1:0] data_n_in, + input logic [Width-1:0] data_s_in, + input logic [Width-1:0] data_w_in, + input logic [Width-1:0] data_e_in, + input logic [Width-1:0] data_p_in, + input logic [4:0] data_void_in, + output logic [4:0] stop_out, + // Output ports + output logic [Width-1:0] data_n_out, + output logic [Width-1:0] data_s_out, + output logic [Width-1:0] data_w_out, + output logic [Width-1:0] data_e_out, + output logic [Width-1:0] data_p_out, + output logic [4:0] data_void_out, + input logic [4:0] stop_in + ); + + noc::xy_t position; + assign position.x = CONST_localx; + assign position.y = CONST_localy; + + lookahead_router + #( + .FlowControl(FlowControl), + .DataWidth(Width - $bits(noc::preamble_t)), + .Ports(Ports) + ) router_impl_i + ( + .clk, + .rst(~rst), + .position, + .data_n_in, + .data_s_in, + .data_w_in, + .data_e_in, + .data_p_in, + .data_void_in, + .stop_out, + .data_n_out, + .data_s_out, + .data_w_out, + .data_e_out, + .data_p_out, + .data_void_out, + .stop_in + ); + +endmodule diff --git a/rtl/noc/router/lookahead_routing.sv b/rtl/noc/router/lookahead_routing.sv new file mode 100644 index 0000000000..0f7c5c1ba6 --- /dev/null +++ b/rtl/noc/router/lookahead_routing.sv @@ -0,0 +1,88 @@ +// Compute next YX positional routing for a 2D mesh NoC +// +// This module determines the next routing direction (lookahead) for the current flit. +// First the coordinates of the next hop are determined based on the routing direction +// encoded in the header flit. Next, the routing direction is updated based on the +// coordinates of the destination router. +// Packets are routed first west or east (X axis), then north or south (Y axis). +// The YX positional routing is proven to be deadlock free. +// +// There is no delay from inputs destination and current_routing to output next_routing. +// Conversely, to improve timing, the local position input is sampled, thus there is a one-cycle +// delay from input position to output next_routing. Note, however, that position is supposed to be +// a static input after initialization, because it encodes the position of the router on the mesh. +// +// Interface +// +// * Inputs +// - clk: clock. +// - position: static input that encodes the x,y coordinates of the router on the mesh. +// - destination: x,y coordinates of the destination router. +// - current_routing: one-hot encoded routing direction for the current hop. +// +// * Outputs +// - next_routing: one-hot encoded routing direction for the next hop. +// + +module lookahead_routing + ( + input logic clk, + input noc::xy_t position, + input noc::xy_t destination, + input noc::direction_t current_routing, + output noc::direction_t next_routing + ); + + function automatic noc::direction_t routing( + input noc::xy_t next_position, + input noc::xy_t destination); + // Compute next routing: go East/West first, then North/South + noc::direction_t west, east, north, south; + west = next_position.x > destination.x ? + // 00100 : 11011 + noc::goWest : ~noc::goWest; + east = next_position.x < destination.x ? + // 01000 : 10111 + noc::goEast : ~noc::goEast; + north = next_position.y > destination.y ? + // 01101 : 11110 + noc::goNorth | noc::goWest | noc::goEast : ~noc::goNorth; + south = next_position.y < destination.y ? + // 01110 : 11101 + noc::goSouth | noc::goWest | noc::goEast : ~noc::goSouth; + // Result is go_local when none of the above is true + routing = west & east & north & south; + endfunction + + // Compute next position for every possible routing except local port + noc::xy_t [3:0] next_position_d, next_position_q; + // North + assign next_position_d[noc::kNorthPort].x = position.x; + assign next_position_d[noc::kNorthPort].y = position.y - 1'b1; + // South + assign next_position_d[noc::kSouthPort].x = position.x; + assign next_position_d[noc::kSouthPort].y = position.y + 1'b1; + // West + assign next_position_d[noc::kWestPort].x = position.x - 1'b1; + assign next_position_d[noc::kWestPort].y = position.y; + // East + assign next_position_d[noc::kEastPort].x = position.x + 1'b1; + assign next_position_d[noc::kEastPort].y = position.y; + + always_ff @(posedge clk) begin + next_position_q <= next_position_d; + end + + always_comb begin + // We don't need to consider the case in which current_routing is goLocal + unique case (current_routing) + noc::goNorth : next_routing = routing(next_position_q[noc::kNorthPort], destination); + noc::goSouth : next_routing = routing(next_position_q[noc::kSouthPort], destination); + noc::goWest : next_routing = routing(next_position_q[noc::kWestPort], destination); + noc::goEast : next_routing = routing(next_position_q[noc::kEastPort], destination); + // When current_routing is goLocal, we don't care about next_routing assignment + default : next_routing = current_routing; + endcase + end + +endmodule diff --git a/rtl/noc/router/noc_pkg.sv b/rtl/noc/router/noc_pkg.sv new file mode 100644 index 0000000000..d48c9f4f3b --- /dev/null +++ b/rtl/noc/router/noc_pkg.sv @@ -0,0 +1,103 @@ +// This package defines constants, data types and functions for the NoC + +package noc; + + // + // Configuration parameters + // + + // Queues + parameter int unsigned PortQueueDepth = 4; + + // Coordinates + parameter int unsigned xMax = 8; + parameter int unsigned yMax = 8; + + // Message Type + parameter int unsigned messageTypeWidth = 5; + + // + // Direction constants, types and functions + // + + // Router ports enable + parameter bit [4:0] AllPorts = 5'b11111; + parameter bit [4:0] TopLeftRouterPorts = 5'b11010; + parameter bit [4:0] TopRightRouterPorts = 5'b10110; + parameter bit [4:0] BottomLeftRouterPorts = 5'b11001; + parameter bit [4:0] BottomRightRouterPorts = 5'b10101; + + typedef enum logic [2:0] { + kNorthPort = 3'd0, + kSouthPort = 3'd1, + kWestPort = 3'd2, + kEastPort = 3'd3, + kLocalPort = 3'd4 + } noc_port_t; + + // one-hot encoding of the ports for routing + typedef struct packed { + logic go_local; + logic go_east; + logic go_west; + logic go_south; + logic go_north; + } direction_t; + + function automatic direction_t get_onehot_port ( + input noc_port_t port); + get_onehot_port.go_north = (port == kNorthPort); + get_onehot_port.go_south = (port == kSouthPort); + get_onehot_port.go_west = (port == kWestPort); + get_onehot_port.go_east = (port == kEastPort); + get_onehot_port.go_local = (port == kLocalPort); + endfunction // get_onehot_port + + parameter direction_t goNorth = get_onehot_port(kNorthPort); + parameter direction_t goSouth = get_onehot_port(kSouthPort); + parameter direction_t goWest = get_onehot_port(kWestPort); + parameter direction_t goEast = get_onehot_port(kEastPort); + parameter direction_t goLocal = get_onehot_port(kLocalPort); + + // + // Coordinates types + // + + parameter int unsigned xWidth = $clog2(xMax); + parameter int unsigned yWidth = $clog2(xMax); + + typedef struct packed { + logic [yWidth-1:0] y; + logic [xWidth-1:0] x; + } xy_t; + + + // + // Flow control types + // + typedef enum logic { + kFlowControlAckNack = 1'b0, + kFlowControlCreditBased = 1'b1 + } noc_flow_control_t; + + parameter int unsigned CreditsWidth = $clog2(PortQueueDepth + 1); + typedef logic [4:0][CreditsWidth-1:0] credits_t; + + // + // Packet info encoding + // + + typedef logic [messageTypeWidth-1:0] message_t; + + typedef struct packed { + logic head; + logic tail; + } preamble_t; + + typedef struct packed { + xy_t source; + xy_t destination; + message_t message; + } packet_info_t; + +endpackage diff --git a/rtl/noc/router/router_arbiter.sv b/rtl/noc/router/router_arbiter.sv new file mode 100644 index 0000000000..fe95436ad5 --- /dev/null +++ b/rtl/noc/router/router_arbiter.sv @@ -0,0 +1,141 @@ +// 4 inputs to 1 output router arbiter +// +// There is no delay from request to grant. +// The abriter assumes that the request remains stable while the entire +// packet is forwarded. Hence, priority is updated whenever a tail flit +// is forwarded. Grant is locked between a head flit and the corresponding +// tail flit. +// +// Interface +// +// * Inputs +// - clk: clock. +// - rst: active-high reset. +// - request: each bit should be set to 1 if there is a valid flit coming from the corresponding +// input port that needs to be routed to the output port arbitrated by this module. +// - forwarding_head: set to 1 to indicate the head flit of a new packet is being routed this cycle. +// The current grant gets locked until the tail flit is forwarded (wormhole routing) +// - forwarding_tail: set to 1 to indicate the tail flit of a packet is being routed this cycle. +// Priority is updated and grant is unlocked. +// +// * Outputs +// - grant: one-hot or zero. When grant[i] is set, request[i] is granted and the packet from the +// corresponding input port i can be routed. +// and the packet from the input +// - grant_valid: this flag indicates whether the current grant output is valid. When at least one +// request bit is set, the arbiter grants the next higher priority request with zero-cycles delay, +// unless grant is locked. +// + +module router_arbiter + ( + input logic clk, + input logic rst, + input logic [3:0] request, + input logic forwarding_head, + input logic forwarding_tail, + output logic [3:0] grant, + output logic grant_valid + ); + + logic grant_locked; + + // Lock current grant for flit between head and tail, tail included + always_ff @(posedge clk) begin + if (rst) begin + grant_locked <= 1'b0; + end else begin + if (forwarding_tail) begin + grant_locked <= 1'b0; + end else if (forwarding_head) begin + grant_locked <= 1'b1; + end + end + end + + assign grant_valid = |request & ~grant_locked; + + // Update priority + typedef logic [3:0][3:0] priority_t; + priority_t priority_mask, priority_mask_next; + priority_t grant_stage1; + logic [3:0][1:0] grant_stage2; + + // Higher priority is given to request[0] at reset + localparam priority_t InitialPriority = { 4'b0000, // request[3] + 4'b1000, // request[2] + 4'b1100, // request[1] + 4'b1110 }; // request[0] + + always_ff @(posedge clk) begin + if (rst) begin + priority_mask <= InitialPriority; + end else if (forwarding_tail) begin + priority_mask <= priority_mask_next; + end + end + + always_comb begin + priority_mask_next = priority_mask; + + unique case (grant) + 4'b0001 : begin + priority_mask_next[0] = '0; + priority_mask_next[1][0] = 1'b1; + priority_mask_next[2][0] = 1'b1; + priority_mask_next[3][0] = 1'b1; + end + 4'b0010 : begin + priority_mask_next[1] = '0; + priority_mask_next[0][1] = 1'b1; + priority_mask_next[2][1] = 1'b1; + priority_mask_next[3][1] = 1'b1; + end + 4'b0100 : begin + priority_mask_next[2] = '0; + priority_mask_next[0][2] = 1'b1; + priority_mask_next[1][2] = 1'b1; + priority_mask_next[3][2] = 1'b1; + end + 4'b1000 : begin + priority_mask_next[3] = '0; + priority_mask_next[0][3] = 1'b1; + priority_mask_next[1][3] = 1'b1; + priority_mask_next[2][3] = 1'b1; + end + default begin + end + endcase + end + + genvar g_i, g_j; + for (g_i = 0; g_i < 4; g_i++) begin : gen_grant + + for (g_j = 0; g_j < 4; g_j++) begin : gen_grant_stage1 + assign grant_stage1[g_i][g_j] = request[g_j] & priority_mask[g_j][g_i]; + end + + for (g_j = 0; g_j < 2; g_j++) begin : gen_grant_stage2 + assign grant_stage2[g_i][g_j] = ~(grant_stage1[g_i][2*g_j] | grant_stage1[g_i][2*g_j + 1]); + end + + assign grant[g_i] = &grant_stage2[g_i] & request[g_i]; + + end // gen_grant + + // + // Assertions + // + +`ifndef SYNTHESIS +// pragma coverage off +//VCS coverage off + + a_grant_onehot: assert property (@(posedge clk) disable iff(rst) $onehot0(grant)) + else $error("Fail: a_grant_onehot"); + +// pragma coverage on +//VCS coverage on +`endif // ~SYNTHESIS + +endmodule diff --git a/rtl/noc/router/router_fifo.sv b/rtl/noc/router/router_fifo.sv new file mode 100644 index 0000000000..10d347056f --- /dev/null +++ b/rtl/noc/router/router_fifo.sv @@ -0,0 +1,138 @@ +// FIFO for packet-switched router +// +// Interface +// +// * Inputs +// - clk: all signals are synchronous to this clock signal. +// - rst: active high reset +// - rdreq: set to 1 for one cycle to pop unless the FIFO is empty; if BypassEnable is set, rdreq +// and wrreq can be asserted in the same cycle to forward data_in to data_out. +// - wrreq: set to 1 for one cycle to push; wrreq has no effect if the FIFO is full. +// - data_in: data to be pushed; if BypassEnable is set and the FIFO is empty data_in is forwarded +// to data_out with zero-cycles delay. +// +// * Outputs +// - empty: when set to 1, the FIFO is empty; after reset, the FIFO is empty. +// - full: when set to 1, the FIFO is full. +// - data_out: data present at the head of the FIFO; this output is not valid when the FIFO is +// empty. If BypassEnable is set, data_out may be valid if data_in is valid; in this case the +// user should assert both rdreq and wrreq. +// +// Parameters +// - BypassEnable: set to 1 to enable zero-cycles delay between data_in and data_out when the FIFO +// is empty. +// - Depth: depth of the FIFO +// - Width: bit-width of data_in and data_out +// +module router_fifo + #( + parameter bit BypassEnable = 1'b1, + parameter int unsigned Depth = 4, + parameter int unsigned Width = 8 + ) + ( + input logic clk, + input logic rst, + input logic rdreq, + input logic wrreq, + input logic [Width-1:0] data_in, + output logic empty, + output logic full, + output logic [Width-1:0] data_out + ); + + logic [Depth-1:0][Width-1:0] mem; + + // Read pointer for FIFO head + logic [$clog2(Depth+1)-1:0] head; + logic [$clog2(Depth+1)-1:0] head_next; + + // One hot mask for FIFO tail and tilization + logic [Depth-1:0] tail; + logic [Depth:0] used; + + logic valid_read; + logic valid_write; + logic [Width-1:0] data_out_nobypass; + + genvar g_i; + + assign valid_read = rdreq & ~empty; + assign valid_write = BypassEnable & empty ? wrreq & ~rdreq : wrreq & ~full; + assign data_out = BypassEnable & empty ? data_in : data_out_nobypass; + + // Update head + assign head_next = head == (Depth - 1'b1) ? '0 : head + 1'b1; + always_ff @(posedge clk) begin + if (rst) begin + head <= '0; + end else if (valid_read) begin + head <= head_next; + end + end + + // Update tail + always_ff @(posedge clk) begin + if (rst) begin + tail[Depth-1:1] <= '0; + tail[0] <= 1'b1; + end else if (valid_write) begin + tail[Depth-1:1] <= tail[Depth-2:0]; + tail[0] <= tail[Depth-1]; + end + end + + // Update FIFO state + always_ff @(posedge clk) begin + if (rst) begin + used[Depth:1] <= '0; + used[0] <= 1'b1; + end else begin + if (valid_write & ~valid_read) begin + used[Depth:1] <= used[Depth-1:0]; + used[0] <= 1'b0; + end else if (~valid_write & valid_read) begin + used[Depth-1:0] <= used[Depth:1]; + used[Depth] <= 1'b0; + end + end + end + + assign empty = used[0]; + assign full = used[Depth]; + + // Write + for (g_i = 0; g_i < Depth; g_i++) begin : gen_fifo_write + always_ff @(posedge clk) begin + if (rst) begin + mem[g_i] <= '0; + end else if (valid_write & tail[g_i]) begin + mem[g_i] <= data_in; + end + end + end + + // Read + assign data_out_nobypass = mem[head]; // ri lint_check_waive VAR_INDEX_RANGE + + // + // Assertions + // + +`ifndef SYNTHESIS +// pragma coverage off +//VCS coverage off + + // FIFO is in good state + a_head_lt_depth: assert property (@(posedge clk) disable iff(rst) head < Depth) + else $error("Fail: a_head_lt_depth"); + a_tail_onehot: assert property (@(posedge clk) disable iff(rst) $onehot(tail)) + else $error("Fail: a_tail_onehot"); + a_used_onehot: assert property (@(posedge clk) disable iff(rst) $onehot(used)) + else $error("Fail: a_used_onehot"); + +// pragma coverage on +//VCS coverage on +`endif // ~SYNTHESIS + +endmodule diff --git a/rtl/noc/routing_engine.vhd b/rtl/noc/routing_engine.vhd deleted file mode 100644 index 55fd039d94..0000000000 --- a/rtl/noc/routing_engine.vhd +++ /dev/null @@ -1,175 +0,0 @@ --- Copyright (c) 2011-2023 Columbia University, System Level Design Group --- SPDX-License-Identifier: Apache-2.0 - ---/* --- * Module: routing_engine --- * Description: Input Routing Engine. --- * Placed at each input it gives the output port the worm must be forwarded --- * through for the next hop (routing look-ahead). The routing algorithm is XY Dimension Order. --- * This algorithm is proved to be deadlock-free over a Mesh topology. --- * Author: Michele Petracca --- * $ID$ --- * --- */ - -library IEEE; -use IEEE.STD_LOGIC_1164.all; -use ieee.std_logic_unsigned.all; -use ieee.std_logic_arith.all; - -entity routing_engine is - generic( - loc_port : integer --- localx : std_logic_vector(2 downto 0); --- localy : std_logic_vector(2 downto 0) - ); - - port( - clk : in std_logic; - rst : in std_logic; - - localx : in std_logic_vector(2 downto 0); - localy : in std_logic_vector(2 downto 0); - - --current hop routing; one-hot encoding - destination_port : in std_logic_vector(4 downto 0); - destx : in std_logic_vector(2 downto 0); - desty : in std_logic_vector(2 downto 0); - - --next hop routing; one-hot encoded - next_routing : out std_logic_vector(4 downto 0)); -end routing_engine; -architecture behavior of routing_engine is - -type coordinate_t is array (0 to 3) of std_logic_vector(2 downto 0); -signal next_localx, next_localy : coordinate_t; - -type routing_t is array (0 to 3) of std_logic_vector(4 downto 0); -signal next_hop_routing : routing_t; - -signal masked_destination_port : std_logic_vector(4 downto 0); - -begin - - --evaluation of the coordinates of the next hop - MASKING: for i in 0 to 4 generate - --begin - MSK0: if (i = loc_port) generate - --begin - masked_destination_port(i) <= '0'; - end generate; - MSK1: if (i /= loc_port) generate - --begin - masked_destination_port(i) <= destination_port(i); - end generate; - end generate; - - --evaluation of the coordinates of the next hop - process(rst,clk) - begin - if rst = '0' then - next_localx <= (others => (others => '0')); - next_localy <= (others => (others => '0')); - --next_localx(0) <= localx; - --next_localx(1) <= localx; - --next_localx(2)(2) <= (localx(2) and (localx(1) or localx(0))); - --next_localx(2)(1) <= not (localx(1) xor localx(0)); - --next_localx(2)(0) <= (not localx(0)); - --next_localx(3)(2) <= (localx(2) or (localx(1) and localx(0))); - --next_localx(3)(1) <= (localx(1) xor localx(0)); - --next_localx(3)(0) <= (not localx(0)); - --next_localy(0)(2) <= (localy(2) and (localy(1) or localy(0))); - --next_localy(0)(1) <= not (localy(1) xor localy(0)); - --next_localy(0)(0) <= (not localy(0)); - --next_localy(1)(2) <= (localy(2) or (localy(1) and localy(0))); - --next_localy(1)(1) <= (localy(1) xor localy(0)); - --next_localy(1)(0) <= (not localy(0)); - --next_localy(2) <= localy; - --next_localy(3) <= localy; - elsif clk'event and clk = '1' then - next_localx(0) <= localx; - next_localx(1) <= localx; - next_localx(2)(2) <= (localx(2) and (localx(1) or localx(0))); - next_localx(2)(1) <= not (localx(1) xor localx(0)); - next_localx(2)(0) <= (not localx(0)); - next_localx(3)(2) <= (localx(2) or (localx(1) and localx(0))); - next_localx(3)(1) <= (localx(1) xor localx(0)); - next_localx(3)(0) <= (not localx(0)); - next_localy(0)(2) <= (localy(2) and (localy(1) or localy(0))); - next_localy(0)(1) <= not (localy(1) xor localy(0)); - next_localy(0)(0) <= (not localy(0)); - next_localy(1)(2) <= (localy(2) or (localy(1) and localy(0))); - next_localy(1)(1) <= (localy(1) xor localy(0)); - next_localy(1)(0) <= (not localy(0)); - next_localy(2) <= localy; - next_localy(3) <= localy; - end if; - end process; - - process(next_localy(0), desty) - begin - if desty /= next_localy(0) then - next_hop_routing(0) <= "00001"; - else - next_hop_routing(0) <= "10000"; - end if; - end process; - - process(next_localy(1), desty) - begin - if desty /= next_localy(1) then - next_hop_routing(1) <= "00010"; - else - next_hop_routing(1) <= "10000"; - end if; - end process; - - process(next_localx(2), next_localy(2), desty, destx) - begin - if destx /= next_localx(2) then - next_hop_routing(2) <= "00100"; - elsif desty = next_localy(2) then - next_hop_routing(2) <= "10000"; - elsif desty > next_localy(2) then - next_hop_routing(2) <= "00010"; - else - next_hop_routing(2) <= "00001"; - end if; - end process; - - process(next_localx(3), next_localy(3), desty, destx) - begin - if destx /= next_localx(3) then - next_hop_routing(3) <= "01000"; - elsif desty = next_localy(3) then - next_hop_routing(3) <= "10000"; - elsif desty > next_localy(3) then - next_hop_routing(3) <= "00010"; - else - next_hop_routing(3) <= "00001"; - end if; - end process; - - --selection of next_hop_routing based on current routing - process(masked_destination_port, next_hop_routing) - begin - case masked_destination_port is - when "00001" => - next_routing <= next_hop_routing(0); - - when "00010" => - next_routing <= next_hop_routing(1); - - when "00100" => - next_routing <= next_hop_routing(2); - - when "01000" => - next_routing <= next_hop_routing(3); - - when others => - next_routing <= next_hop_routing(0); - - end case; - end process; - -end behavior; diff --git a/rtl/noc/rtr_arbitration_engine.vhd b/rtl/noc/rtr_arbitration_engine.vhd deleted file mode 100644 index 1fda7063bd..0000000000 --- a/rtl/noc/rtr_arbitration_engine.vhd +++ /dev/null @@ -1,116 +0,0 @@ --- Copyright (c) 2011-2023 Columbia University, System Level Design Group --- SPDX-License-Identifier: Apache-2.0 - -library ieee; -use IEEE.STD_LOGIC_1164.all; -use ieee.std_logic_unsigned.all; -use ieee.std_logic_arith.all; - -entity rtr_arbitration_engine is - port( - clk : in std_logic; - rst : in std_logic; - - --requests registers; one-hot encoded like the parameter - requests : in std_logic_vector(3 downto 0); - - shift_priority : in std_logic; - update_priority : in std_logic; - lock_priority : in std_logic; - - valid_no_collision : out std_logic; - valid_collision : out std_logic; - --grant registers; one-hot encoded like the parameter - grant_no_collision : out std_logic_vector(3 downto 0); - grant_collision : out std_logic_vector(3 downto 0)); -end rtr_arbitration_engine; - -architecture behavior of rtr_arbitration_engine is - -signal no_collision, collision_d, new_requests : std_logic; - -type masked_request_t is array (0 to 3) of std_logic_vector(3 downto 0); -signal masked_request : masked_request_t; -signal request_match, grant_int, saved_grant : std_logic_vector(3 downto 0); -signal enable_d : std_logic; - -signal grant_tmp, grant_tmp_debug, grant_i, final_grant_tmp : std_logic_vector(3 downto 0); - -type priority_t is array (0 to 3) of std_logic_vector(3 downto 0); ---signal priority_mask : std_logic_vector(3 downto 0); -signal priority_mask : priority_t; - -signal priority_locked : std_logic; - -signal arbitration_first_stage : priority_t; -signal arbitration_second_stage : priority_t; - - - -begin - valid_collision <= '0'; - grant_collision <= (others => '0'); - - new_requests <= requests(0) or requests(1) or requests(2) or requests(3); - - valid_no_collision <= new_requests and (not priority_locked); - grant_no_collision <= grant_tmp and requests; - - process(clk,rst) - begin - if rst = '0' then - priority_mask(0) <= "1110"; - priority_mask(1) <= "1100"; - priority_mask(2) <= "1000"; - priority_mask(3) <= "0000"; - elsif clk = '1' and clk'event then - if shift_priority = '1' then - if (grant_tmp(0) and requests(0)) = '1' then - priority_mask(0)(0) <= '1'; - priority_mask(1)(0) <= '1'; - priority_mask(2)(0) <= '1'; - priority_mask(3)(0) <= '1'; - priority_mask(0) <= (others => '0'); - end if; - if (grant_tmp(1) and requests(1)) = '1' then - priority_mask(0)(1) <= '1'; - priority_mask(1)(1) <= '1'; - priority_mask(2)(1) <= '1'; - priority_mask(3)(1) <= '1'; - priority_mask(1) <= (others => '0'); - end if; - if (grant_tmp(2) and requests(2)) = '1' then - priority_mask(0)(2) <= '1'; - priority_mask(1)(2) <= '1'; - priority_mask(2)(2) <= '1'; - priority_mask(3)(2) <= '1'; - priority_mask(2) <= (others => '0'); - end if; - if (grant_tmp(3) and requests(3)) = '1' then - priority_mask(0)(3) <= '1'; - priority_mask(1)(3) <= '1'; - priority_mask(2)(3) <= '1'; - priority_mask(3)(3) <= '1'; - priority_mask(3) <= (others => '0'); - end if; - end if; - end if; - end process; - - priority_locked <= lock_priority; - - TMP_GRANT: for i in 0 to 3 generate - TMP_GRANT_2: for j in 0 to 3 generate - arbitration_first_stage(i)(j) <= requests(j) and priority_mask(j)(i); - end generate; - - TMP_GRANT_3: for l in 0 to 1 generate - arbitration_second_stage(i)(l) <= arbitration_first_stage(i)(2*l) nor arbitration_first_stage(i)(2*l + 1); - end generate; - - grant_tmp(i) <= arbitration_second_stage(i)(0) and arbitration_second_stage(i)(1); - grant_tmp_debug(i) <= ((requests(0) and priority_mask(0)(i)) nor (requests(1) and priority_mask(1)(i))) and ((requests(2) and priority_mask(2)(i)) nor (requests(3) and priority_mask(3)(i))); - end generate TMP_GRANT; - - -end behavior; diff --git a/utils/flist/vhdl.flist b/utils/flist/vhdl.flist index e2678708de..058ea351a0 100644 --- a/utils/flist/vhdl.flist +++ b/utils/flist/vhdl.flist @@ -60,20 +60,16 @@ misc/ahbslm.vhd misc/esp_init.vhd misc/ahbram_dp.vhd misc/rstgen.vhd -noc/routing_engine.vhd -noc/bypassable_queue.vhd noc/sync_noc_set.vhd noc/noc32_xy.vhd noc/fifo.vhd noc/fifo2.vhd -noc/rtr_arbitration_engine.vhd noc/sync_noc_xy.vhd noc/inferred_async_fifo.vhd noc/fifo3.vhd noc/fifo1.vhd noc/sync_noc32_xy.vhd noc/router.vhd -noc/nobypassable_queue.vhd sockets/proxy/noc2apb.vhd sockets/proxy/noc2intreq.vhd sockets/proxy/intreq2noc.vhd diff --git a/utils/flist/vlog.flist b/utils/flist/vlog.flist index e7d0ae57e4..a3c2e09c50 100644 --- a/utils/flist/vlog.flist +++ b/utils/flist/vlog.flist @@ -28,3 +28,9 @@ caches/esp-caches/l2/rtl/l2_input_decoder.sv caches/esp-caches/common/rtl/interface_controller.sv misc/esplink.sv sockets/adapters/apb2axil.sv +noc/router/noc_pkg.sv +noc/router/router_fifo.sv +noc/router/router_arbiter.sv +noc/router/lookahead_routing.sv +noc/router/lookahead_router.sv +noc/router/lookahead_router_wrapper.sv