2021-08-27 20:11:07 +02:00
|
|
|
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
|
2021-08-28 14:22:28 +02:00
|
|
|
signal rx : std_logic;
|
|
|
|
signal rx_last : std_logic;
|
|
|
|
signal rx_last_static : std_logic;
|
|
|
|
signal rx_edge : std_logic;
|
2021-08-27 20:11:07 +02:00
|
|
|
|
|
|
|
-- 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
|
|
|
|
);
|
|
|
|
|
2021-08-28 14:22:28 +02:00
|
|
|
-- Edge detector for RX (+glitch filter)
|
|
|
|
edgedet : process(clk, rst) is
|
|
|
|
begin
|
|
|
|
if rst then
|
|
|
|
rx_last <= '0';
|
|
|
|
rx_last_static <= '0';
|
|
|
|
rx_edge <= '0';
|
|
|
|
|
|
|
|
elsif rising_edge(clk) then
|
|
|
|
rx_edge <= '0';
|
|
|
|
if (rx_last = rx) then
|
|
|
|
rx_edge <= rx_last_static xor rx;
|
|
|
|
rx_last_static <= rx;
|
|
|
|
end if;
|
|
|
|
rx_last <= rx;
|
|
|
|
end if;
|
|
|
|
end process edgedet;
|
2021-08-27 20:11:07 +02:00
|
|
|
|
|
|
|
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
|
2021-08-28 14:23:15 +02:00
|
|
|
type demanchestization_state_t is (SYNC, DATA, ERROR);
|
2021-08-27 20:11:07 +02:00
|
|
|
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
|
2021-08-28 14:23:44 +02:00
|
|
|
when LONG => -- @suppress: Exit condition through indirect assignment
|
2021-08-27 20:11:07 +02:00
|
|
|
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';
|
2021-08-28 14:23:15 +02:00
|
|
|
rx_error <= '0';
|
2021-08-27 20:11:07 +02:00
|
|
|
elsif rising_edge(clk) then
|
2021-08-28 14:23:15 +02:00
|
|
|
bit_stb <= '0';
|
|
|
|
rx_error <= '0';
|
|
|
|
|
2021-08-27 20:11:07 +02:00
|
|
|
if (bit_ev /= NONE) then
|
|
|
|
case demanchestization_state is
|
|
|
|
when SYNC =>
|
|
|
|
if (bit_ev = KEEP) then
|
|
|
|
bit_value <= '1';
|
|
|
|
demanchestization_state <= DATA;
|
2021-08-28 14:23:15 +02:00
|
|
|
rx_active <= '1';
|
2021-08-27 20:11:07 +02:00
|
|
|
end if;
|
2021-08-28 14:23:15 +02:00
|
|
|
when DATA => -- @suppress: Condition outside of case allows to exit this state
|
2021-08-27 20:11:07 +02:00
|
|
|
bit_value <= not bit_value when bit_ev = TOGGLE else bit_value;
|
|
|
|
bit_stb <= '1';
|
2021-08-28 14:23:15 +02:00
|
|
|
when ERROR => -- @suppress: Condition outside of case allows to exit this state
|
|
|
|
null;
|
2021-08-27 20:11:07 +02:00
|
|
|
end case;
|
|
|
|
end if;
|
|
|
|
|
2021-08-28 14:23:15 +02:00
|
|
|
if (bit_ev = ERROR) then
|
|
|
|
rx_error <= '1';
|
|
|
|
demanchestization_state <= ERROR;
|
|
|
|
end if;
|
|
|
|
|
2021-08-27 20:11:07 +02:00
|
|
|
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;
|