263 lines
6.7 KiB
VHDL
263 lines
6.7 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
|
|
-- -------------------------------------------------------------------------- --
|
|
-- TODO
|
|
-- -------------------------------------------------------------------------- --
|
|
-- Author : Markus Koch <markus@notsyncing.net>
|
|
-- Contributors : None
|
|
-- License : Mozilla Public License (MPL) Version 2
|
|
-- -------------------------------------------------------------------------- --
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library trashernet;
|
|
use trashernet.trashernet_pkg.all;
|
|
|
|
library generics;
|
|
use generics.all;
|
|
use generics.wishbone_pkg.all;
|
|
|
|
entity trashernet_phy_wb is
|
|
generic(
|
|
F_CLK : in integer; -- Clock frequency in Hz
|
|
F_CLK_PHY : in integer
|
|
);
|
|
port(
|
|
-- Global
|
|
clk : in std_logic; -- Global clock
|
|
phy_clk : in std_logic; -- PHY clock
|
|
rst : in std_logic; -- Asynchronous reset
|
|
|
|
-- Wishbone IF
|
|
wb_o : out wishbone_slave_out; -- Wishbone bus (out)
|
|
wb_i : in wishbone_slave_in; -- Wishbone bus (in)
|
|
|
|
-- Ethernet physical signals
|
|
rx_p : in std_logic;
|
|
tx_p : out std_logic;
|
|
tx_n : out std_logic;
|
|
-- LEDs
|
|
led_tx : out std_logic;
|
|
led_rx : out std_logic
|
|
);
|
|
end entity trashernet_phy_wb;
|
|
|
|
architecture RTL of trashernet_phy_wb is
|
|
-- PHY application interface
|
|
signal phy_out : phy_out_t; -- PHY application IF (out)
|
|
signal phy_in : phy_in_t; -- PHY application IF (in)
|
|
|
|
signal mac_out : raw_mac_out_t; -- MAC application IF (out)
|
|
signal mac_in : raw_mac_in_t; -- MAC application IF (in)
|
|
|
|
signal wb_adr : unsigned(2 downto 2);
|
|
|
|
signal status_register : std_logic_vector(31 downto 0);
|
|
|
|
signal rx_fifo_read : std_logic;
|
|
signal rx_fifo_data : std_logic_vector(7 downto 0);
|
|
|
|
signal tx_fifo_write : std_logic;
|
|
signal tx_fifo_commit : std_logic;
|
|
signal tx_fifo_empty : std_logic;
|
|
signal tx_fifo_data : std_logic_vector(7 downto 0);
|
|
|
|
signal rx_fifo_empty : std_logic;
|
|
signal rx_bitcount : std_logic_vector(10 downto 0);
|
|
signal rx_fifo_block_count : std_logic_vector(rx_bitcount'range);
|
|
signal rx_block_next : std_logic;
|
|
begin
|
|
trashernet_phy_inst : entity trashernet.trashernet_phy_cdc
|
|
generic map(
|
|
F_CLK => F_CLK,
|
|
F_CLK_PHY => F_CLK_PHY
|
|
)
|
|
port map(
|
|
clk => clk,
|
|
phy_clk => phy_clk,
|
|
rst => rst,
|
|
phy_out => phy_out,
|
|
phy_in => phy_in,
|
|
rx_p => rx_p,
|
|
tx_p => tx_p,
|
|
tx_n => tx_n
|
|
);
|
|
|
|
trashernet_raw_mac_inst : entity trashernet.trashernet_raw_mac
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
phy_out => phy_out,
|
|
phy_in => phy_in,
|
|
mac_out => mac_out,
|
|
mac_in => mac_in
|
|
);
|
|
|
|
rx_fifo_inst : entity generics.fifo_block
|
|
generic map(
|
|
SIZE => 2047
|
|
)
|
|
port map(
|
|
clk => clk,
|
|
rst_a => '0',
|
|
clr => rst,
|
|
data_in => mac_out.rx_mac_data,
|
|
write => mac_out.rx_mac_valid,
|
|
commit => mac_out.rx_mac_crc_ok,
|
|
abort => mac_out.rx_mac_crc_error,
|
|
full => open,
|
|
data_out => rx_fifo_data,
|
|
data_first => open,
|
|
empty => rx_fifo_empty,
|
|
read => rx_fifo_read,
|
|
usage => open
|
|
);
|
|
|
|
fifo_inst : entity generics.fifo
|
|
generic map(
|
|
SIZE => 31
|
|
)
|
|
port map(
|
|
clk => clk,
|
|
rst_a => '0',
|
|
clr => rst,
|
|
data_in => rx_bitcount,
|
|
write => mac_out.rx_mac_crc_ok,
|
|
full => open,
|
|
data_out => rx_fifo_block_count,
|
|
empty => open,
|
|
read => rx_block_next,
|
|
usage => open
|
|
);
|
|
|
|
rxcount : process(rst, clk) is
|
|
begin
|
|
if rst then
|
|
rx_bitcount <= (others => '0');
|
|
|
|
elsif rising_edge(clk) then
|
|
if mac_out.rx_mac_crc_error or mac_out.rx_mac_crc_ok then
|
|
rx_bitcount <= (others => '0');
|
|
elsif mac_out.rx_mac_valid then
|
|
rx_bitcount <= std_logic_vector(unsigned(rx_bitcount) + 1);
|
|
end if;
|
|
end if;
|
|
end process rxcount;
|
|
tx_fifo_inst : entity generics.fifo_block
|
|
generic map(
|
|
SIZE => 2047
|
|
)
|
|
port map(
|
|
clk => clk,
|
|
rst_a => '0',
|
|
clr => rst,
|
|
data_in => wb_i.dat(7 downto 0),
|
|
write => tx_fifo_write,
|
|
commit => tx_fifo_commit,
|
|
abort => '0',
|
|
full => open,
|
|
data_out => tx_fifo_data,
|
|
data_first => open,
|
|
empty => tx_fifo_empty,
|
|
read => mac_out.tx_mac_data_ack,
|
|
usage => open
|
|
);
|
|
|
|
wb_adr <= unsigned(wb_i.adr(wb_adr'range));
|
|
wbif : process(clk, rst) is
|
|
begin
|
|
if rst then
|
|
wb_o.ack <= '0';
|
|
rx_fifo_read <= '0';
|
|
tx_fifo_write <= '0';
|
|
tx_fifo_commit <= '0';
|
|
rx_block_next <= '0';
|
|
|
|
elsif rising_edge(clk) then
|
|
wb_o.ack <= '0';
|
|
rx_fifo_read <= '0';
|
|
tx_fifo_write <= '0';
|
|
tx_fifo_commit <= '0';
|
|
rx_block_next <= '0';
|
|
|
|
if (wb_i.cyc and wb_i.stb) then
|
|
wb_o.ack <= '1';
|
|
|
|
if wb_adr = 0 then
|
|
wb_o.dat <= status_register;
|
|
if wb_o.ack = '0' then
|
|
if wb_i.we = '1' then
|
|
tx_fifo_commit <= wb_i.dat(2);
|
|
rx_block_next <= wb_i.dat(1);
|
|
end if;
|
|
end if;
|
|
else
|
|
wb_o.dat <= (others => '0');
|
|
|
|
if wb_o.ack = '0' then
|
|
if wb_i.we = '1' then
|
|
tx_fifo_write <= '1';
|
|
else
|
|
rx_fifo_read <= '1';
|
|
end if;
|
|
end if;
|
|
wb_o.dat(7 downto 0) <= rx_fifo_data;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process wbif;
|
|
|
|
wb_o.err <= '0';
|
|
wb_o.rty <= '1';
|
|
wb_o.stall <= '0';
|
|
|
|
mac_in.tx_mac_data_en <= not tx_fifo_empty; -- FIXME: this does not care about inter-packet gaps indicated by `data_first`
|
|
mac_in.tx_mac_data <= tx_fifo_data;
|
|
|
|
status_register <= ("0" & x"0" & rx_fifo_block_count & -- 31..16
|
|
x"000" & "0" & (not tx_fifo_empty) & (not rx_fifo_empty) & phy_out.carrier_detect);
|
|
|
|
ledstretch : process(clk) is
|
|
constant CMAX : integer := integer(0.1 / (1.0 / real(F_CLK)));
|
|
constant CMIN : integer := -integer(0.1 / (1.0 / real(F_CLK)));
|
|
variable cnt : integer range CMIN to CMAX;
|
|
begin
|
|
if rising_edge(clk) then
|
|
if cnt = CMIN then
|
|
led_rx <= phy_out.carrier_detect;
|
|
if phy_out.rx_active then
|
|
cnt := CMAX;
|
|
led_rx <= not phy_out.carrier_detect;
|
|
end if;
|
|
elsif cnt = 0 then
|
|
led_rx <= phy_out.carrier_detect;
|
|
cnt := cnt - 1;
|
|
else
|
|
cnt := cnt - 1;
|
|
end if;
|
|
end if;
|
|
end process ledstretch;
|
|
|
|
ledstretch2 : process(clk) is
|
|
constant CMAX : integer := integer(0.1 / (1.0 / real(F_CLK)));
|
|
constant CMIN : integer := -integer(0.1 / (1.0 / real(F_CLK)));
|
|
variable cnt : integer range CMIN to CMAX;
|
|
begin
|
|
if rising_edge(clk) then
|
|
if cnt = CMIN then
|
|
if phy_out.tx_active then
|
|
cnt := CMAX;
|
|
led_tx <= '1';
|
|
end if;
|
|
elsif cnt = 0 then
|
|
led_tx <= '0';
|
|
cnt := cnt - 1;
|
|
else
|
|
cnt := cnt - 1;
|
|
end if;
|
|
end if;
|
|
end process ledstretch2;
|
|
end architecture RTL;
|