Implement PHY RX
This commit is contained in:
parent
85f3a3021a
commit
08bd5f166b
26
trashernet/synchronizer.vhd
Normal file
26
trashernet/synchronizer.vhd
Normal 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;
|
248
trashernet/trashernet_phy.vhd
Normal file
248
trashernet/trashernet_phy.vhd
Normal 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;
|
Loading…
Reference in New Issue
Block a user