Implement PHY RX

This commit is contained in:
Markus Koch 2021-08-27 20:11:07 +02:00
parent 85f3a3021a
commit 08bd5f166b
2 changed files with 274 additions and 0 deletions

View File

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

View File

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