diff --git a/trashernet/synchronizer.vhd b/trashernet/synchronizer.vhd new file mode 100644 index 0000000..f29901f --- /dev/null +++ b/trashernet/synchronizer.vhd @@ -0,0 +1,26 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity synchronizer is + port( + clk : in std_logic; + rst : in std_logic; + data_in : in std_logic; + data_out : out std_logic + ); +end entity synchronizer; + +architecture RTL of synchronizer is + signal sr : std_logic_vector(2 downto 0); +begin + sync : process(clk, rst) is + begin + if rst then + sr <= (others => '0'); + elsif rising_edge(clk) then + sr <= sr(sr'high - 1 downto sr'low) & data_in; + end if; + end process sync; + data_out <= sr(sr'high); +end architecture RTL; diff --git a/trashernet/trashernet_phy.vhd b/trashernet/trashernet_phy.vhd new file mode 100644 index 0000000..2e085f2 --- /dev/null +++ b/trashernet/trashernet_phy.vhd @@ -0,0 +1,248 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity trashernet_phy is + generic( + F_CLK : in integer + ); + port( + -- Global + clk : in std_logic; -- Global clock + rst : in std_logic; -- Asynchronous reset + + -- Eth + data_rx : out std_logic_vector(7 downto 0); -- RX Data + data_rx_valid : out std_logic; -- RX Data valid + rx_active : out std_logic; -- RX of packet in progress + + data_tx : in std_logic_vector(7 downto 0); -- TX Data + data_tx_en : in std_logic; + data_tx_ack : out std_logic; -- + + carrier_detect : out std_logic; -- + rx_error : out std_logic; -- + + -- Ethernet physical signals + rx_p : in std_logic; + tx_p : out std_logic + ); +end entity trashernet_phy; + +architecture rtl of trashernet_phy is + constant F_ETH : integer := 10000000; + +begin + + receiver : block + -- Signal conditioning + signal rx : std_logic; + signal rx_last : std_logic; + signal rx_edge : std_logic; + + -- Bit recovery + signal bit_value : std_logic; + signal bit_stb : std_logic; + + -- Bytizer + signal bit_cnt : integer range 0 to 7; + + -- NLP supervision + constant NLP_TIMEOUT_CNT_MAX : integer := integer(round(real(F_CLK) * 16.0 * 10.0**(-3))); -- Every 16 ms + signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX; + begin + -- Synchronize RX input + synchronizer_inst : entity work.synchronizer + port map( + clk => clk, + rst => rst, + data_in => rx_p, + data_out => rx + ); + + -- Edge detector for RX + rx_last <= '0' when rst + else rx when rising_edge(clk) + ; + rx_edge <= rx_last xor rx; + + demanchestizer : block + -- Transition detector + constant BIT_LENGTH_LONG : integer := F_CLK / F_ETH - 1; + constant BIT_LENGTH_SHORT : integer := F_CLK / (F_ETH * 2) - 1; + constant BIT_LENGTH_TOLERANCE : integer := (F_CLK / F_ETH) / 5; + constant BIT_LENGTH_TIMEOUT : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1; + signal sample_count : integer range 0 to BIT_LENGTH_TIMEOUT; + type transition_duration_t is (SHORT, LONG); + signal transition_duration : transition_duration_t; + signal transition_stb : std_logic; + signal transition_activity : std_logic; + + -- Transition analysis + signal last_transition : transition_duration_t; + type bit_ev_t is (NONE, TOGGLE, KEEP, ERROR); + signal bit_ev : bit_ev_t; + + -- Bit recovery + type demanchestization_state_t is (SYNC, DATA); + signal demanchestization_state : demanchestization_state_t; + begin + -- Detects spacing of transitions + transition_detector : process(clk, rst) is + begin + if rst then + transition_stb <= '0'; + sample_count <= 0; + + elsif rising_edge(clk) then + transition_stb <= '0'; + + if rx_edge then + if (sample_count = BIT_LENGTH_TIMEOUT) then -- First edge, ignore this transition + sample_count <= 0; + else + if ((sample_count >= BIT_LENGTH_SHORT - BIT_LENGTH_TOLERANCE) and (sample_count <= BIT_LENGTH_SHORT + BIT_LENGTH_TOLERANCE)) then + transition_duration <= SHORT; + transition_stb <= '1'; + sample_count <= 0; + elsif ((sample_count >= BIT_LENGTH_LONG - BIT_LENGTH_TOLERANCE) and (sample_count <= BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE)) then + transition_duration <= LONG; + transition_stb <= '1'; + sample_count <= 0; + end if; + end if; + else + if (sample_count /= BIT_LENGTH_TIMEOUT) then + sample_count <= sample_count + 1; + end if; + end if; + end if; + end process transition_detector; + transition_activity <= '1' when sample_count /= BIT_LENGTH_TIMEOUT else '0'; + + -- Converts the spacing of transitions into a toggle-no-toggle stream + transition_analyzer : process(clk, rst) is + begin + if rst then + last_transition <= LONG; + bit_ev <= NONE; + + elsif rising_edge(clk) then + bit_ev <= NONE; + + if transition_stb then + case last_transition is + when LONG => + if transition_duration = LONG then + bit_ev <= TOGGLE; + end if; + last_transition <= transition_duration; + when SHORT => + if (transition_duration = SHORT) then + bit_ev <= KEEP; + else + bit_ev <= ERROR; + end if; + last_transition <= LONG; + end case; + end if; + end if; + end process transition_analyzer; + + -- Synchronizes the Manchester level to the Ethernet header which is TOGGLE TOGGLE .. TOGGLE NO-TOGGLE for ..1010101011 + bit_recovery : process(clk, rst) is + begin + if rst then + demanchestization_state <= SYNC; + bit_stb <= '0'; + rx_active <= '0'; + + elsif rising_edge(clk) then + bit_stb <= '0'; + if (bit_ev /= NONE) then + rx_active <= '1'; + case demanchestization_state is + when SYNC => + if (bit_ev = KEEP) then + bit_value <= '1'; + demanchestization_state <= DATA; + end if; + when DATA => + bit_value <= not bit_value when bit_ev = TOGGLE else bit_value; + bit_stb <= '1'; + end case; + end if; + + if (not transition_activity) then + demanchestization_state <= SYNC; + rx_active <= '0'; + end if; + end if; + end process bit_recovery; + end block demanchestizer; + + -- Latches data into an 8-bit vector + bytizer : process(clk, rst) is + begin + if rst then + data_rx_valid <= '0'; + data_rx <= (others => '0'); + + elsif rising_edge(clk) then + data_rx_valid <= '0'; + + if rx_active then + if (bit_stb) then + data_rx <= data_rx(data_rx'high - 1 downto 0) & bit_value; + if (bit_cnt = 7) then + data_rx_valid <= '1'; + bit_cnt <= 0; + else + bit_cnt <= bit_cnt + 1; + end if; + end if; + else + bit_cnt <= 0; + end if; + end if; + end process bytizer; + + -- Supervises for NLP or data presence + nlp_timeout_p : process(clk, rst) is + begin + if rst then + nlp_timeout_cnt <= NLP_TIMEOUT_CNT_MAX; + elsif rising_edge(clk) then + if rx_edge then -- Technically, we should use only the rising edge here, but a project called `trashernet` probably won't mind ;) + nlp_timeout_cnt <= NLP_TIMEOUT_CNT_MAX; + else + if (nlp_timeout_cnt /= 0) then + nlp_timeout_cnt <= nlp_timeout_cnt - 1; + end if; + end if; + end if; + end process nlp_timeout_p; + carrier_detect <= '1' when nlp_timeout_cnt /= 0 else '0'; + end block receiver; + + -- ------------------------------------------------------------------------- + + transmitter : block + constant TRIG_CNT_MAX : integer := integer(round(real(F_CLK) * 16.0 * 10.0**(-3))); -- Every 16 ms + constant TRIG_CNT_MIN : integer := -integer(round(real(F_CLK) * 100.0 * 10.0**(-9))); -- For 100 ns + signal cnt : integer range TRIG_CNT_MIN to TRIG_CNT_MAX; + begin + -- Generates NLP to keep the link up + nlp : process(clk, rst) is + begin + if rst then + cnt <= TRIG_CNT_MIN; + elsif rising_edge(clk) then + cnt <= TRIG_CNT_MAX when (cnt = TRIG_CNT_MIN) else cnt - 1; + end if; + end process nlp; + tx_p <= '1' when cnt < 0 else '0'; + end block transmitter; + +end architecture rtl;