diff --git a/trashernet/trashernet_phy.vhd b/trashernet/trashernet_phy.vhd index 09732b9..ab08b8e 100644 --- a/trashernet/trashernet_phy.vhd +++ b/trashernet/trashernet_phy.vhd @@ -26,7 +26,8 @@ entity trashernet_phy is -- Ethernet physical signals rx_p : in std_logic; - tx_p : out std_logic + tx_p : out std_logic; + tx_n : out std_logic ); end entity trashernet_phy; @@ -254,19 +255,143 @@ begin -- ------------------------------------------------------------------------- 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; + constant TX_STB_CNT_MAX : integer := F_CLK / (F_ETH * 2) - 1; + signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX; + + type tx_state_t is (IDLE, NLP, TX, IPG); + signal tx_state : tx_state_t; + signal sr : std_logic_vector(tx_data'range); + signal bit_stage : std_logic; + + constant BIT_CNT_MAX_NLP : integer := 16000 / 100; -- 16 ms (timebase 100 ns) + constant BIT_CNT_MAX_IPG : integer := 96; + constant BIT_CNT_MAX_DATA : integer := sr'length - 1; + signal bit_cnt : integer range 0 to maximum(maximum(BIT_CNT_MAX_NLP, BIT_CNT_MAX_DATA), BIT_CNT_MAX_IPG); + + type tx_mode_t is (OFF, NLP, ACTIVE); + signal tx_mode : tx_mode_t; begin - -- Generates NLP to keep the link up - nlp : process(clk, rst) is + tx_main : process(clk, rst) is + procedure transmit_byte is + begin + tx_state <= TX; + tx_mode <= ACTIVE; + + sr <= tx_data; + bit_stage <= '0'; + bit_cnt <= BIT_CNT_MAX_DATA; + tx_data_ack <= '0'; + end procedure transmit_byte; + + procedure transmit_ipg is + begin + tx_state <= IPG; + tx_mode <= OFF; + + bit_cnt <= BIT_CNT_MAX_IPG; + bit_stage <= '0'; + end procedure transmit_ipg; + + procedure transmit_nlp is + begin + tx_state <= NLP; + tx_mode <= NLP; + end procedure transmit_nlp; + + procedure go_idle is + begin + tx_state <= IDLE; + tx_mode <= OFF; + + bit_cnt <= BIT_CNT_MAX_NLP; + bit_stage <= '0'; + end procedure go_idle; + + variable tx_stb : std_logic; -- Strobe every 50 ns (20 MHz) + begin if rst then - cnt <= TRIG_CNT_MIN; + tx_data_ack <= '0'; + tx_stb_cnt <= TX_STB_CNT_MAX; + tx_mode <= OFF; + go_idle; + elsif rising_edge(clk) then - cnt <= TRIG_CNT_MAX when (cnt = TRIG_CNT_MIN) else cnt - 1; + tx_data_ack <= '0'; + + if tx_stb_cnt = 0 then + tx_stb_cnt <= TX_STB_CNT_MAX; + else + tx_stb_cnt <= tx_stb_cnt - 1; + end if; + tx_stb := '1' when tx_stb_cnt = 0 else '0'; + + if tx_stb then + bit_stage <= not bit_stage; + end if; + if tx_stb and bit_stage then -- 100 ns (1 bit) + if (bit_cnt /= 0) then + bit_cnt <= bit_cnt - 1; + end if; + end if; + + case tx_state is + when IDLE => + if tx_data_en then -- New packet to TX + transmit_byte; + tx_stb_cnt <= TX_STB_CNT_MAX; -- resync + + elsif bit_cnt = 0 then -- NLP timeout + transmit_nlp; + + --bit_cnt <= 1; Let's save some resources here... + bit_stage <= '0'; + tx_stb_cnt <= TX_STB_CNT_MAX; -- resync + end if; + when NLP => + if tx_stb and bit_stage then -- 100 ns duration + go_idle; + end if; + when TX => + if tx_stb then + if (bit_stage = '1') then + sr <= '0' & sr(sr'high downto sr'low + 1); + if bit_cnt = 0 then + if tx_data_en then + transmit_byte; + else + transmit_ipg; + end if; + end if; + end if; + end if; + when IPG => + if bit_cnt = 0 then + go_idle; + end if; + end case; end if; - end process nlp; - tx_p <= '1' when cnt < 0 else '0'; + end process tx_main; + + driver : process(clk, rst) is + begin + if rst then + tx_p <= '0'; + tx_n <= '0'; + elsif rising_edge(clk) then + case tx_mode is + when OFF => + tx_p <= '0'; + tx_n <= '0'; + when NLP => + tx_p <= '1'; + tx_n <= '0'; + when ACTIVE => + tx_p <= sr(sr'low) xor bit_stage; + tx_n <= not (sr(sr'low) xor bit_stage); + end case; + end if; + end process driver; + end block transmitter; end architecture rtl;