mirror of
https://github.com/lcbcFoo/ReonV.git
synced 2025-04-21 03:57:07 -04:00
735 lines
26 KiB
VHDL
735 lines
26 KiB
VHDL
------------------------------------------------------------------------------
|
|
-- This file is a part of the GRLIB VHDL IP LIBRARY
|
|
-- Copyright (C) 2003 - 2008, Gaisler Research
|
|
-- Copyright (C) 2008 - 2014, Aeroflex Gaisler
|
|
-- Copyright (C) 2015 - 2017, Cobham Gaisler
|
|
--
|
|
-- This program is free software; you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation; either version 2 of the License, or
|
|
-- (at your option) any later version.
|
|
--
|
|
-- This program is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-- GNU General Public License for more details.
|
|
--
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with this program; if not, write to the Free Software
|
|
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----------------------------------------------------------------------------
|
|
-- Entity: phy
|
|
-- File: phy.vhd
|
|
-- Description: Simulation model of an Ethernet PHY
|
|
-- Author: Marko Isomaki
|
|
------------------------------------------------------------------------------
|
|
|
|
-- pragma translate_off
|
|
|
|
library ieee;
|
|
library grlib;
|
|
|
|
use ieee.std_logic_1164.all;
|
|
use grlib.stdlib.all;
|
|
|
|
entity phy is
|
|
generic(
|
|
address : integer range 0 to 31 := 0;
|
|
extended_regs : integer range 0 to 1 := 1;
|
|
aneg : integer range 0 to 1 := 1;
|
|
base100_t4 : integer range 0 to 1 := 0;
|
|
base100_x_fd : integer range 0 to 1 := 1;
|
|
base100_x_hd : integer range 0 to 1 := 1;
|
|
fd_10 : integer range 0 to 1 := 1;
|
|
hd_10 : integer range 0 to 1 := 1;
|
|
base100_t2_fd : integer range 0 to 1 := 1;
|
|
base100_t2_hd : integer range 0 to 1 := 1;
|
|
base1000_x_fd : integer range 0 to 1 := 0;
|
|
base1000_x_hd : integer range 0 to 1 := 0;
|
|
base1000_t_fd : integer range 0 to 1 := 1;
|
|
base1000_t_hd : integer range 0 to 1 := 1;
|
|
rmii : integer range 0 to 1 := 0;
|
|
rgmii : integer range 0 to 1 := 0;
|
|
extrxclken : integer range 0 to 1 := 0; -- align rx_clk with extrxclk (gmii only)
|
|
gmii100 : integer range 0 to 1 := 0 -- force 10/100 to (x10/x100 repeating) gmii
|
|
);
|
|
port(
|
|
rstn : in std_logic;
|
|
mdio : inout std_logic;
|
|
tx_clk : out std_logic;
|
|
rx_clk : out std_logic;
|
|
rxd : out std_logic_vector(7 downto 0);
|
|
rx_dv : out std_logic;
|
|
rx_er : out std_logic;
|
|
rx_col : out std_logic;
|
|
rx_crs : out std_logic;
|
|
txd : in std_logic_vector(7 downto 0);
|
|
tx_en : in std_logic;
|
|
tx_er : in std_logic;
|
|
mdc : in std_logic;
|
|
gtx_clk : in std_logic;
|
|
extrxclk : in std_logic
|
|
);
|
|
end;
|
|
|
|
architecture behavioral of phy is
|
|
type mdio_state_type is (idle, start_of_frame, start_of_frame2, op, phyad, regad,
|
|
ta, rdata, wdata);
|
|
|
|
type ctrl_reg_type is record
|
|
reset : std_ulogic;
|
|
loopback : std_ulogic;
|
|
speedsel : std_logic_vector(1 downto 0);
|
|
anegen : std_ulogic;
|
|
powerdown : std_ulogic;
|
|
isolate : std_ulogic;
|
|
restartaneg : std_ulogic;
|
|
duplexmode : std_ulogic;
|
|
coltest : std_ulogic;
|
|
end record;
|
|
|
|
type status_reg_type is record
|
|
base100_t4 : std_ulogic;
|
|
base100_x_fd : std_ulogic;
|
|
base100_x_hd : std_ulogic;
|
|
fd_10 : std_ulogic;
|
|
hd_10 : std_ulogic;
|
|
base100_t2_fd : std_ulogic;
|
|
base100_t2_hd : std_ulogic;
|
|
extstat : std_ulogic;
|
|
mfpreamblesup : std_ulogic;
|
|
anegcmpt : std_ulogic;
|
|
remfault : std_ulogic;
|
|
anegability : std_ulogic;
|
|
linkstat : std_ulogic;
|
|
jabdetect : std_ulogic;
|
|
extcap : std_ulogic;
|
|
end record;
|
|
|
|
type aneg_ab_type is record
|
|
next_page : std_ulogic;
|
|
remote_fault : std_ulogic;
|
|
tech_ability : std_logic_vector(7 downto 0);
|
|
selector : std_logic_vector(4 downto 0);
|
|
end record;
|
|
|
|
type aneg_exp_type is record
|
|
par_detct_flt : std_ulogic;
|
|
lp_np_able : std_ulogic;
|
|
np_able : std_ulogic;
|
|
page_rx : std_ulogic;
|
|
lp_aneg_able : std_ulogic;
|
|
end record;
|
|
|
|
type aneg_nextpage_type is record
|
|
next_page : std_ulogic;
|
|
message_page : std_ulogic;
|
|
ack2 : std_ulogic;
|
|
toggle : std_ulogic;
|
|
message : std_logic_vector(10 downto 0);
|
|
end record;
|
|
|
|
type mst_slv_ctrl_type is record
|
|
tmode : std_logic_vector(2 downto 0);
|
|
manualcfgen : std_ulogic;
|
|
cfgval : std_ulogic;
|
|
porttype : std_ulogic;
|
|
base1000_t_fd : std_ulogic;
|
|
base1000_t_hd : std_ulogic;
|
|
end record;
|
|
|
|
type mst_slv_status_type is record
|
|
cfgfault : std_ulogic;
|
|
cfgres : std_ulogic;
|
|
locrxstate : std_ulogic;
|
|
remrxstate : std_ulogic;
|
|
lpbase1000_t_fd : std_ulogic;
|
|
lpbase1000_t_hd : std_ulogic;
|
|
idlerrcnt : std_logic_vector(7 downto 0);
|
|
end record;
|
|
|
|
type extended_status_reg_type is record
|
|
base1000_x_fd : std_ulogic;
|
|
base1000_x_hd : std_ulogic;
|
|
base1000_t_fd : std_ulogic;
|
|
base1000_t_hd : std_ulogic;
|
|
end record;
|
|
|
|
type reg_type is record
|
|
state : mdio_state_type;
|
|
cnt : integer;
|
|
op : std_logic_vector(1 downto 0);
|
|
phyad : std_logic_vector(4 downto 0);
|
|
regad : std_logic_vector(4 downto 0);
|
|
wr : std_ulogic;
|
|
regtmp : std_logic_vector(15 downto 0);
|
|
-- MII management registers
|
|
ctrl : ctrl_reg_type;
|
|
status : status_reg_type;
|
|
anegadv : aneg_ab_type;
|
|
aneglp : aneg_ab_type;
|
|
anegexp : aneg_exp_type;
|
|
anegnptx : aneg_nextpage_type;
|
|
anegnplp : aneg_nextpage_type;
|
|
mstslvctrl : mst_slv_ctrl_type;
|
|
mstslvstat : mst_slv_status_type;
|
|
extstatus : extended_status_reg_type;
|
|
rstcnt : integer;
|
|
anegcnt : integer;
|
|
end record;
|
|
|
|
signal r, rin : reg_type;
|
|
signal int_clk : std_ulogic := '0';
|
|
signal clkslow : std_ulogic := '0';
|
|
signal rcnt : integer;
|
|
signal anegact : std_ulogic;
|
|
|
|
constant qlength: integer := 4;
|
|
signal txdataq: std_logic_vector(0 to qlength*10-1);
|
|
signal txqpos,rxqpos: integer range 0 to qlength-1 := 0;
|
|
|
|
type phy_interface_mode_type is (mode_mii,mode_gmii,mode_rmii,mode_rgmii);
|
|
signal ifmode: phy_interface_mode_type;
|
|
|
|
signal lb_rxd: std_logic_vector(7 downto 0);
|
|
signal lb_rxdv,lb_rxer: std_ulogic;
|
|
|
|
signal erxclkdel : std_ulogic;
|
|
begin
|
|
|
|
-- Selects which interface standard should be implemented by the PHY
|
|
-- Note we assume selection between GMII and MII depending on speed setting
|
|
-- in MDIO reg
|
|
ifmode <= mode_rgmii when rgmii=1 else
|
|
mode_rmii when rmii=1 else
|
|
mode_gmii when (r.ctrl.speedsel = "01" or gmii100=1) else
|
|
mode_mii;
|
|
|
|
erxclkdel <= transport extrxclk after (8 ns - 2.5 ns);
|
|
|
|
int_clk <= erxclkdel when extrxclken=1 and ifmode=mode_gmii else
|
|
not int_clk after 10 ns when ifmode=mode_rmii else
|
|
not int_clk after 4 ns when ifmode=mode_rgmii and r.ctrl.speedsel = "01" else
|
|
not int_clk after 20 ns when ifmode=mode_rgmii and r.ctrl.speedsel = "10" else
|
|
not int_clk after 200 ns when ifmode=mode_rgmii and r.ctrl.speedsel = "00" else
|
|
not int_clk after 4 ns when ifmode=mode_gmii else
|
|
not int_clk after 20 ns when ifmode=mode_mii and r.ctrl.speedsel = "10" else
|
|
not int_clk after 200 ns when ifmode=mode_mii and r.ctrl.speedsel = "00";
|
|
|
|
clkslow <= not clkslow after 20 ns when r.ctrl.speedsel = "10" else
|
|
not clkslow after 200 ns;
|
|
|
|
rx_clk <= int_clk after 2.5 ns when ifmode=mode_gmii else
|
|
int_clk after 10 ns when ifmode=mode_mii else
|
|
int_clk;
|
|
tx_clk <= clkslow when ifmode/=mode_gmii else '0';
|
|
|
|
anegproc : process is
|
|
begin
|
|
loop
|
|
anegact <= '0';
|
|
while rstn /= '1' loop
|
|
wait on rstn;
|
|
end loop;
|
|
while rstn = '1' loop
|
|
if r.ctrl.anegen = '0' then
|
|
anegact <= '0';
|
|
wait on rstn, r.ctrl.anegen, r.ctrl.restartaneg;
|
|
else
|
|
if r.ctrl.restartaneg = '1' then
|
|
anegact <= '1';
|
|
wait on rstn, r.ctrl.restartaneg, r.ctrl.anegen for 2 us;
|
|
anegact <= '0';
|
|
wait on rstn, r.ctrl.anegen until r.ctrl.restartaneg = '0';
|
|
if (rstn and r.ctrl.anegen) = '1' then
|
|
wait on rstn, r.ctrl.anegen, r.ctrl.restartaneg;
|
|
end if;
|
|
else
|
|
anegact <= '0';
|
|
wait on rstn, r.ctrl.restartaneg, r.ctrl.anegen;
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
end loop;
|
|
end process;
|
|
|
|
mdiocomb : process(rstn, r, anegact, mdio) is
|
|
variable v : reg_type;
|
|
begin
|
|
v := r;
|
|
if anegact = '0' then
|
|
v.ctrl.restartaneg := '0';
|
|
end if;
|
|
case r.state is
|
|
when idle =>
|
|
mdio <= 'Z';
|
|
if to_X01(mdio) = '1' then
|
|
v.cnt := v.cnt + 1;
|
|
if v.cnt = 31 then
|
|
v.state := start_of_frame; v.cnt := 0;
|
|
end if;
|
|
else
|
|
v.cnt := 0;
|
|
end if;
|
|
when start_of_frame =>
|
|
if to_X01(mdio) = '0' then
|
|
v.state := start_of_frame2;
|
|
elsif to_X01(mdio) /= '1' then
|
|
v.state := idle;
|
|
end if;
|
|
when start_of_frame2 =>
|
|
if to_X01(mdio) = '1' then
|
|
v.state := op;
|
|
else
|
|
v.state := idle;
|
|
end if;
|
|
when op =>
|
|
v.cnt := v.cnt + 1;
|
|
v.op := r.op(0) & to_X01(mdio);
|
|
if r.cnt = 1 then
|
|
if (v.op = "01") or (v.op = "10") then
|
|
v.state := phyad; v.cnt := 0;
|
|
else
|
|
v.state := idle; v.cnt := 0;
|
|
end if;
|
|
end if;
|
|
when phyad =>
|
|
v.phyad := r.phyad(3 downto 0) & to_X01(mdio);
|
|
v.cnt := v.cnt + 1;
|
|
if r.cnt = 4 then
|
|
v.state := regad; v.cnt := 0;
|
|
end if;
|
|
when regad =>
|
|
v.regad := r.regad(3 downto 0) & to_X01(mdio);
|
|
v.cnt := v.cnt + 1;
|
|
if r.cnt = 4 then
|
|
v.cnt := 0;
|
|
if conv_integer(r.phyad) = address then
|
|
v.state := ta;
|
|
else
|
|
v.state := idle;
|
|
end if;
|
|
end if;
|
|
when ta =>
|
|
v.cnt := r.cnt + 1;
|
|
if r.cnt = 0 then
|
|
if (r.op = "01") and to_X01(mdio) /= '1' then
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
else
|
|
if r.op = "10" then
|
|
mdio <= '0'; v.cnt := 0; v.state := rdata;
|
|
case r.regad is
|
|
when "00000" => --ctrl (basic)
|
|
v.regtmp := r.ctrl.reset & r.ctrl.loopback &
|
|
r.ctrl.speedsel(1) & r.ctrl.anegen & r.ctrl.powerdown &
|
|
r.ctrl.isolate & r.ctrl.restartaneg & r.ctrl.duplexmode &
|
|
r.ctrl.coltest & r.ctrl.speedsel(0) & "000000";
|
|
when "00001" => --statuc (basic)
|
|
v.regtmp := r.status.base100_t4 & r.status.base100_x_fd &
|
|
r.status.base100_x_hd & r.status.fd_10 & r.status.hd_10 &
|
|
r.status.base100_t2_fd & r.status.base100_t2_hd &
|
|
r.status.extstat & '0' & r.status.mfpreamblesup &
|
|
r.status.anegcmpt & r.status.remfault & r.status.anegability &
|
|
r.status.linkstat & r.status.jabdetect & r.status.extcap;
|
|
when "00010" => --PHY ID (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := X"BBCD";
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "00011" => --PHY ID (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := X"9C83";
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "00100" => --Auto-neg adv. (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := r.anegadv.next_page & '0' & r.anegadv.remote_fault &
|
|
r.anegadv.tech_ability & r.anegadv.selector;
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "00101" => --Auto-neg link partner ability (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := r.aneglp.next_page & '0' & r.aneglp.remote_fault &
|
|
r.aneglp.tech_ability & r.aneglp.selector;
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "00110" => --Auto-neg expansion (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := "00000000000" & r.anegexp.par_detct_flt &
|
|
r.anegexp.lp_np_able & r.anegexp.np_able & r.anegexp.page_rx &
|
|
r.anegexp.lp_aneg_able;
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "00111" => --Auto-neg next page (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := r.anegnptx.next_page & '0' & r.anegnptx.message_page &
|
|
r.anegnptx.ack2 & r.anegnptx.toggle & r.anegnptx.message;
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "01000" => --Auto-neg link partner received next page (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := r.anegnplp.next_page & '0' & r.anegnplp.message_page &
|
|
r.anegnplp.ack2 & r.anegnplp.toggle & r.anegnplp.message;
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "01001" => --Master-slave control (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := r.mstslvctrl.tmode & r.mstslvctrl.manualcfgen &
|
|
r.mstslvctrl.cfgval & r.mstslvctrl.porttype &
|
|
r.mstslvctrl.base1000_t_fd & r.mstslvctrl.base1000_t_hd &
|
|
"00000000";
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "01010" => --Master-slave status (extended)
|
|
if extended_regs = 1 then
|
|
v.regtmp := r.mstslvstat.cfgfault & r.mstslvstat.cfgres &
|
|
r.mstslvstat.locrxstate & r.mstslvstat.remrxstate &
|
|
r.mstslvstat.lpbase1000_t_fd & r.mstslvstat.lpbase1000_t_hd &
|
|
"00" & r.mstslvstat.idlerrcnt;
|
|
else
|
|
v.cnt := 0; v.state := idle;
|
|
end if;
|
|
when "01111" =>
|
|
if (base1000_x_fd = 1) or (base1000_x_hd = 1) or
|
|
(base1000_t_fd = 1) or (base1000_t_hd = 1) then
|
|
v.regtmp := r.extstatus.base1000_x_fd &
|
|
r.extstatus.base1000_x_hd &
|
|
r.extstatus.base1000_t_fd &
|
|
r.extstatus.base1000_t_hd & X"000";
|
|
else
|
|
v.regtmp := (others => '0');
|
|
end if;
|
|
when others =>
|
|
--PHY shall not drive MDIO when unimplemented registers
|
|
--are accessed
|
|
v.cnt := 0; v.state := idle;
|
|
v.regtmp := (others => '0');
|
|
end case;
|
|
if r.ctrl.reset = '1' then
|
|
if r.regad = "00000" then
|
|
v.regtmp := X"8000";
|
|
else
|
|
v.regtmp := X"0000";
|
|
end if;
|
|
end if;
|
|
else
|
|
if to_X01(mdio) /= '0'then
|
|
v.cnt := 0; v.state := idle;
|
|
else
|
|
v.cnt := 0; v.state := wdata;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
when rdata =>
|
|
v.cnt := r.cnt + 1;
|
|
mdio <= r.regtmp(15-r.cnt);
|
|
if r.cnt = 15 then
|
|
v.state := idle; v.cnt := 0;
|
|
end if;
|
|
when wdata =>
|
|
v.cnt := r.cnt + 1;
|
|
v.regtmp := r.regtmp(14 downto 0) & to_X01(mdio);
|
|
if r.cnt = 15 then
|
|
v.state := idle; v.cnt := 0;
|
|
if r.ctrl.reset = '0' then
|
|
case r.regad is
|
|
when "00000" =>
|
|
v.ctrl.reset := v.regtmp(15);
|
|
v.ctrl.loopback := v.regtmp(14);
|
|
v.ctrl.speedsel(1) := v.regtmp(13);
|
|
v.ctrl.anegen := v.regtmp(12);
|
|
v.ctrl.powerdown := v.regtmp(11);
|
|
v.ctrl.isolate := v.regtmp(10);
|
|
v.ctrl.restartaneg := v.regtmp(9);
|
|
v.ctrl.duplexmode := v.regtmp(8);
|
|
v.ctrl.coltest := v.regtmp(7);
|
|
v.ctrl.speedsel(0) := v.regtmp(6);
|
|
when "00100" =>
|
|
if extended_regs = 1 then
|
|
v.anegadv.remote_fault := r.regtmp(13);
|
|
v.anegadv.tech_ability := r.regtmp(12 downto 5);
|
|
v.anegadv.selector := r.regtmp(4 downto 0);
|
|
end if;
|
|
when "00111" =>
|
|
if extended_regs = 1 then
|
|
v.anegnptx.next_page := r.regtmp(15);
|
|
v.anegnptx.message_page := r.regtmp(13);
|
|
v.anegnptx.ack2 := r.regtmp(12);
|
|
v.anegnptx.message := r.regtmp(10 downto 0);
|
|
end if;
|
|
when "01001" =>
|
|
if extended_regs = 1 then
|
|
v.mstslvctrl.tmode := r.regtmp(15 downto 13);
|
|
v.mstslvctrl.manualcfgen := r.regtmp(12);
|
|
v.mstslvctrl.cfgval := r.regtmp(11);
|
|
v.mstslvctrl.porttype := r.regtmp(10);
|
|
v.mstslvctrl.base1000_t_fd := r.regtmp(9);
|
|
v.mstslvctrl.base1000_t_hd := r.regtmp(8);
|
|
end if;
|
|
when others => --no writable bits for other regs
|
|
null;
|
|
end case;
|
|
end if;
|
|
end if;
|
|
when others =>
|
|
null;
|
|
end case;
|
|
if r.rstcnt > 19 then
|
|
v.ctrl.reset := '0'; v.rstcnt := 0;
|
|
else
|
|
v.rstcnt := r.rstcnt + 1;
|
|
end if;
|
|
if (v.ctrl.reset and not r.ctrl.reset) = '1' then
|
|
v.rstcnt := 0;
|
|
end if;
|
|
if r.ctrl.anegen = '1' then
|
|
if r.anegcnt < 10 then
|
|
v.anegcnt := r.anegcnt + 1;
|
|
else
|
|
v.status.anegcmpt := '1';
|
|
if (base1000_x_fd = 1) or (base1000_x_hd = 1) or
|
|
(r.mstslvctrl.base1000_t_fd = '1') or
|
|
(r.mstslvctrl.base1000_t_hd = '1') then
|
|
v.ctrl.speedsel(1 downto 0) := "01";
|
|
elsif (r.anegadv.tech_ability(4) = '1') or
|
|
(r.anegadv.tech_ability(3) = '1') or
|
|
(r.anegadv.tech_ability(2) = '1') or
|
|
(base100_t2_fd = 1) or (base100_t2_hd = 1) then
|
|
v.ctrl.speedsel(1 downto 0) := "10";
|
|
else
|
|
v.ctrl.speedsel(1 downto 0) := "00";
|
|
end if;
|
|
if ((base1000_x_fd = 1) or (r.mstslvctrl.base1000_t_fd = '1')) or
|
|
(((base100_t2_fd = 1) or (r.anegadv.tech_ability(3) = '1')) and
|
|
(r.mstslvctrl.base1000_t_hd = '0') and (base1000_x_hd = 0)) or
|
|
((r.anegadv.tech_ability(1) = '1') and (base100_t2_hd = 0) and
|
|
(r.anegadv.tech_ability(4) = '0') and
|
|
(r.anegadv.tech_ability(2) = '0')) then
|
|
v.ctrl.duplexmode := '1';
|
|
else
|
|
v.ctrl.duplexmode := '0';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
if r.ctrl.restartaneg = '1' then
|
|
v.anegcnt := 0;
|
|
v.status.anegcmpt := '0';
|
|
v.ctrl.restartaneg := '0';
|
|
end if;
|
|
rin <= v;
|
|
end process;
|
|
|
|
reg : process(rstn, mdc) is
|
|
begin
|
|
if rising_edge(mdc) then
|
|
r <= rin;
|
|
end if;
|
|
-- -- RESET DELAY
|
|
-- if rstd = '1' then
|
|
-- r.ctrl.reset <= '1';
|
|
-- else
|
|
-- r.ctrl.reset <= '0';
|
|
-- end if;
|
|
|
|
-- RESET
|
|
if (r.ctrl.reset or not rstn) = '1' then
|
|
r.ctrl.loopback <= '1'; r.anegcnt <= 0;
|
|
if (base1000_x_hd = 1) or (base1000_x_fd = 1) or (base1000_t_hd = 1) or
|
|
(base1000_t_fd = 1) then
|
|
r.ctrl.speedsel <= "01";
|
|
elsif (base100_x_hd = 1) or (base100_t2_hd = 1) or (base100_x_fd = 1) or
|
|
(base100_t2_fd = 1) or (base100_t4 = 1) then
|
|
r.ctrl.speedsel <= "10";
|
|
else
|
|
r.ctrl.speedsel <= "00";
|
|
end if;
|
|
|
|
r.ctrl.anegen <= conv_std_logic(aneg = 1);
|
|
r.ctrl.powerdown <= '0';
|
|
r.ctrl.isolate <= '0';
|
|
r.ctrl.restartaneg <= '0';
|
|
if (base100_x_hd = 0) and (hd_10 = 0) and (base100_t2_hd = 0) and
|
|
(base1000_x_hd = 0) and (base1000_t_hd = 0) then
|
|
r.ctrl.duplexmode <= '1';
|
|
else
|
|
r.ctrl.duplexmode <= '0';
|
|
end if;
|
|
r.ctrl.coltest <= '0';
|
|
|
|
r.status.base100_t4 <= conv_std_logic(base100_t4 = 1);
|
|
r.status.base100_x_fd <= conv_std_logic(base100_x_fd = 1);
|
|
r.status.base100_x_hd <= conv_std_logic(base100_x_hd = 1);
|
|
r.status.fd_10 <= conv_std_logic(fd_10 = 1);
|
|
r.status.hd_10 <= conv_std_logic(hd_10 = 1);
|
|
r.status.base100_t2_fd <= conv_std_logic(base100_t2_fd = 1);
|
|
r.status.base100_t2_hd <= conv_std_logic(base100_t2_hd = 1);
|
|
r.status.extstat <= conv_std_logic((base1000_x_fd = 1) or
|
|
(base1000_x_hd = 1) or
|
|
(base1000_t_fd = 1) or
|
|
(base1000_t_hd = 1));
|
|
r.status.mfpreamblesup <= '0';
|
|
r.status.anegcmpt <= '0';
|
|
r.status.remfault <= '0';
|
|
r.status.anegability <= conv_std_logic(aneg = 1);
|
|
r.status.linkstat <= '0';
|
|
r.status.jabdetect <= '0';
|
|
r.status.extcap <= conv_std_logic(extended_regs = 1);
|
|
|
|
r.anegadv.next_page <= '0';
|
|
r.anegadv.remote_fault <= '0';
|
|
r.anegadv.tech_ability <= "000" & conv_std_logic(base100_t4 = 1) &
|
|
conv_std_logic(base100_x_fd = 1) & conv_std_logic(base100_x_hd = 1) &
|
|
conv_std_logic(fd_10 = 1) & conv_std_logic(hd_10 = 1);
|
|
r.anegadv.selector <= "00001";
|
|
|
|
r.aneglp.next_page <= '0';
|
|
r.aneglp.remote_fault <= '0';
|
|
r.aneglp.tech_ability <= "000" & conv_std_logic(base100_t4 = 1) &
|
|
conv_std_logic(base100_x_fd = 1) & conv_std_logic(base100_x_hd = 1) &
|
|
conv_std_logic(fd_10 = 1) & conv_std_logic(hd_10 = 1);
|
|
r.aneglp.selector <= "00001";
|
|
|
|
r.anegexp.par_detct_flt <= '0';
|
|
r.anegexp.lp_np_able <= '0';
|
|
r.anegexp.np_able <= '0';
|
|
r.anegexp.page_rx <= '0';
|
|
r.anegexp.lp_aneg_able <= '0';
|
|
|
|
r.anegnptx.next_page <= '0';
|
|
r.anegnptx.message_page <= '1';
|
|
r.anegnptx.ack2 <= '0';
|
|
r.anegnptx.toggle <= '0';
|
|
r.anegnptx.message <= "00000000001";
|
|
|
|
r.anegnplp.next_page <= '0';
|
|
r.anegnplp.message_page <= '1';
|
|
r.anegnplp.ack2 <= '0';
|
|
r.anegnplp.toggle <= '0';
|
|
r.anegnplp.message <= "00000000001";
|
|
|
|
r.mstslvctrl.tmode <= (others => '0');
|
|
r.mstslvctrl.manualcfgen <= '0';
|
|
r.mstslvctrl.cfgval <= '0';
|
|
r.mstslvctrl.porttype <= '0';
|
|
r.mstslvctrl.base1000_t_fd <= conv_std_logic(base1000_t_fd = 1);
|
|
r.mstslvctrl.base1000_t_hd <= conv_std_logic(base1000_t_fd = 1);
|
|
|
|
r.mstslvstat.cfgfault <= '0';
|
|
r.mstslvstat.cfgres <= '1';
|
|
r.mstslvstat.locrxstate <= '1';
|
|
r.mstslvstat.remrxstate <= '1';
|
|
r.mstslvstat.lpbase1000_t_fd <= conv_std_logic(base1000_t_fd = 1);
|
|
r.mstslvstat.lpbase1000_t_hd <= conv_std_logic(base1000_t_fd = 1);
|
|
r.mstslvstat.idlerrcnt <= (others => '0');
|
|
|
|
r.extstatus.base1000_x_fd <= conv_std_logic(base1000_x_fd = 1);
|
|
r.extstatus.base1000_x_hd <= conv_std_logic(base1000_x_hd = 1);
|
|
r.extstatus.base1000_t_fd <= conv_std_logic(base1000_t_fd = 1);
|
|
r.extstatus.base1000_t_hd <= conv_std_logic(base1000_t_hd = 1);
|
|
|
|
end if;
|
|
|
|
if rstn = '0' then
|
|
r.cnt <= 0; r.state <= idle; r.rstcnt <= 0;
|
|
r.ctrl.reset <= '1';
|
|
end if;
|
|
end process;
|
|
|
|
loopback_sel : process(r, gtx_clk, txd, tx_en, tx_er, lb_rxd, lb_rxer, lb_rxdv) is
|
|
begin
|
|
rx_col <= '0'; rx_crs <= '1';
|
|
rxd <= (others => '0'); rx_dv <= '0'; rx_er <= '0';
|
|
if r.ctrl.loopback = '1' then
|
|
case ifmode is
|
|
when mode_mii | mode_gmii =>
|
|
-- Use TX ring buffer
|
|
rxd <= lb_rxd;
|
|
rx_dv <= lb_rxdv;
|
|
rx_er <= lb_rxer;
|
|
when mode_rmii =>
|
|
rx_dv <= '1'; rx_er <= '1'; --unused should not affect anything
|
|
rx_col <= '0'; rx_crs <= tx_en;
|
|
if tx_en = '1' then
|
|
rxd(1 downto 0) <= txd(1 downto 0);
|
|
end if;
|
|
when mode_rgmii =>
|
|
if (gtx_clk = '1' and tx_en = '0') then
|
|
rxd(3 downto 0) <= r.ctrl.duplexmode & r.ctrl.speedsel & r.status.linkstat;
|
|
end if;
|
|
end case;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
txsamp: process(txd,tx_en,tx_er,gtx_clk,int_clk,clkslow)
|
|
|
|
variable lasttxtr, lastclk: time;
|
|
variable lastclk_valid: boolean := false;
|
|
constant tSU_GMII : time := 2.5 ns;
|
|
constant tH_GMII : time := 0.5 ns;
|
|
constant tCTOmax_MII : time := 25.0 ns;
|
|
|
|
procedure sample_data(tSU: time) is
|
|
begin
|
|
txdataq(txqpos*10 to txqpos*10+9) <= (txd & tx_en & tx_er);
|
|
txqpos <= (txqpos+1) mod qlength;
|
|
assert (now-lasttxtr) >= tSU
|
|
report "Setup violation on txd/tx_en/tx_er" severity warning;
|
|
lastclk := now;
|
|
lastclk_valid := true;
|
|
end procedure;
|
|
|
|
procedure holdcheck(tH: time) is
|
|
begin
|
|
assert (not lastclk_valid) or (lastclk >= lasttxtr) or (lasttxtr-lastclk >= tH)
|
|
report "Hold violation on txd/tx_en/tx_er" severity warning;
|
|
end procedure;
|
|
|
|
begin
|
|
if txd'event or tx_en'event or tx_er'event then lasttxtr:=now; end if;
|
|
case ifmode is
|
|
when mode_gmii =>
|
|
if r.ctrl.speedsel="01" or gmii100=1 then
|
|
if rising_edge(gtx_clk) then sample_data(2.5 ns); end if;
|
|
holdcheck(0.5 ns);
|
|
else
|
|
if rising_edge(clkslow) then sample_data(15.0 ns); end if;
|
|
end if;
|
|
when mode_mii =>
|
|
if rising_edge(int_clk) then sample_data(15.0 ns); end if;
|
|
when others =>
|
|
end case;
|
|
end process;
|
|
|
|
rxloopback: process(int_clk)
|
|
variable twin: time;
|
|
begin
|
|
if rising_edge(int_clk) then
|
|
if r.ctrl.loopback='0' then
|
|
rxqpos <= (txqpos + (qlength/2)) mod qlength;
|
|
else
|
|
rxqpos <= (rxqpos+1) mod qlength;
|
|
end if;
|
|
case ifmode is
|
|
when mode_mii => twin := 20.0 ns;
|
|
when mode_gmii => twin := 3.0 ns;
|
|
when others => twin := 1.0 ns;
|
|
end case;
|
|
lb_rxd <= txdataq(rxqpos*10 to rxqpos*10+7), (others => '0') after twin;
|
|
lb_rxdv <= txdataq(rxqpos*10+8), '0' after twin;
|
|
lb_rxer <= txdataq(rxqpos*10+9), '0' after twin;
|
|
end if;
|
|
end process;
|
|
|
|
end;
|
|
-- pragma translate_on
|
|
|