------------------------------------------------------------------------------- -- Title : xemac_host_shim -- Project : ------------------------------------------------------------------------------- -- File : xemac_host_shim.vhd -- Author : Marc Kelly -- Company : University of Manchester (Particle Physics Group) -- Created : 2008-04-02 -- Last update: 2009/08/12 -- Platform : -- Standard : VHDL'93/02 ------------------------------------------------------------------------------- -- Description: This sits infront of the GigEthernet host interface and allows -- the setup auto negotiation to take place automaticly. It then allows the SM -- access to the Host interface incase we want to pull values out of it via the -- link at any time. It needs to take inputs for what MAC value to poke into -- the device, and also take in the interrupt when link auto neg happens. It -- sets some defaults, then turns on the MDIO and handles auto-neg. After -- autoneg it needs to Poke some flow control stuff into the MAC as well, to -- make sure we're okay with it all. -- -- State machine is probably the best idea here, with some code stolen from the -- V4FX design to drive the interface state transations. ------------------------------------------------------------------------------- -- Copyright (c) 2008 University of Manchester (Particle Physics Group) ------------------------------------------------------------------------------- -- Revisions : -- Date Version Author Description -- 2008-04-02 1.0 mpkelly Created ------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.std_logic_unsigned.all; entity xemac_host_shim is generic( DEBUG_MODE : std_logic := '0' ); port( rst : in std_logic; clk : in std_logic; slowclk : in std_logic; gb_autoneg_mac : in std_logic_vector (47 downto 0); gb_autoneg_irq : in std_logic; gb_autoneg_done : out std_logic; gb_host_clk : out std_logic; gb_host_opcode : out std_logic_vector (1 downto 0); gb_host_addr : out std_logic_vector (9 downto 0); gb_host_wr_data : out std_logic_vector (31 downto 0); gb_host_req : out std_logic; gb_host_miim_sel : out std_logic; gb_host_rd_data : in std_logic_vector (31 downto 0); gb_host_miim_rdy : in std_logic; sm_host_available : out std_logic; -- SM has access? sm_host_opcode : in std_logic_vector (1 downto 0); sm_host_addr : in std_logic_vector (9 downto 0); sm_host_wr_data : in std_logic_vector (31 downto 0); sm_host_req : in std_logic; sm_host_miim_sel : in std_logic; sm_host_rd_data : out std_logic_vector (31 downto 0); sm_host_miim_rdy : out std_logic ); -- Declarations end xemac_host_shim ; architecture rtl of xemac_host_shim is -- some constants here for poking into host -- MAC is AC:DE:48:C2:50:01 constant MAC_MDIO_DIVIDE : std_logic_vector(31 downto 0) := x"00000039"; constant MAC_LOWER_DATA : std_logic_vector(31 downto 0) := x"C248DEAC"; constant MAC_UPPER_DATA : std_logic_vector(31 downto 0) := x"10000150"; constant MAC_TXCFG_DATA : std_logic_vector(31 downto 0) := x"10000000"; constant MAC_FLOW_DATA : std_logic_vector(31 downto 0) := x"60000000"; constant MAC_FILTER_DATA : std_logic_vector(31 downto 0) := x"00000000"; constant MAC_AUTONEG_CLEAR1_DATA : std_logic_vector(31 downto 0) := x"00000140"; constant MAC_AUTONEG_CLEAR2_DATA : std_logic_vector(31 downto 0) := x"00000001"; constant MAC_AUTONEG_LINK_DATA : std_logic_vector(31 downto 0) := x"0000" & "0000000110100000"; constant MAC_AUTONEG_START_DATA : std_logic_vector(31 downto 0) := x"00001340"; -- some addresses here to remember constant MAC_LOWER_ADDR : std_logic_vector(11 downto 0) := x"200"; --"001000000000"; constant MAC_UPPER_ADDR : std_logic_vector(11 downto 0) := x"240"; --"001001000000"; constant MAC_TXCFG_ADDR : std_logic_vector(11 downto 0) := x"280"; --"001010000000"; constant MAC_FLOW_ADDR : std_logic_vector(11 downto 0) := x"2C0"; --"001011000000"; constant MAC_MODE : std_logic_vector(11 downto 0) := x"300"; --"001100000000"; constant MAC_RS_GMII : std_logic_vector(11 downto 0) := x"320"; --"001100100000"; constant MAC_MDIO_ADDR : std_logic_vector(11 downto 0) := x"340"; --"001101000000"; constant MAC_LOWER2_ADDR : std_logic_vector(11 downto 0) := x"380"; --"001110000000"; constant MAC_UPPER2_ADDR : std_logic_vector(11 downto 0) := x"384"; --"001110000100"; constant MAC_FILTER_ADDR : std_logic_vector(11 downto 0) := x"390"; --"001110010000"; constant MAC_MDIO_REG0 : std_logic_vector(11 downto 0) := x"000"; --"000000000000"; constant MAC_MDIO_REG4 : std_logic_vector(11 downto 0) := x"004"; --"000000000100"; constant MAC_MDIO_REG16 : std_logic_vector(11 downto 0) := x"010"; --"000000010000"; component xemac_host_shim_if port ( clk : in std_logic; rst : in std_logic; ADDR : in std_logic_vector(9 downto 0); WR_DATA : in std_logic_vector(31 downto 0); RD_DATA : out std_logic_vector(31 downto 0); STROBE : in std_logic; MDIO_ACCESS : in std_logic; WR_ACCESS : in std_logic; ACCESS_DONE : out std_logic; HOSTADDR : out std_logic_vector(9 downto 0); HOSTMIIMSEL : out std_logic; HOSTOPCODE : out std_logic_vector(1 downto 0); HOSTREQ : out std_logic; HOSTMIIMRDY : in std_logic; HOSTWRDATA : out std_logic_vector(31 downto 0); HOSTRDDATA : in std_logic_vector(31 downto 0)); end component xemac_host_shim_if; signal host_access_i : std_logic; -- Who has access? 1 = SM; 0 = autoneg signal first_time_done_i : std_logic; -- 0 = first time, 1 = done -- Signals for host access from the simple state machine signal gb_host_opcode_i : std_logic_vector(1 downto 0); signal gb_host_addr_i : std_logic_vector(9 downto 0); signal gb_host_wr_data_i : std_logic_vector(31 downto 0); signal gb_host_req_i : std_logic; signal gb_host_miim_sel_i : std_logic; signal gb_host_rd_data_i : std_logic_vector(31 downto 0); signal gb_host_miim_rdy_i : std_logic; -- Status signals signal auto_neg_Access_Done_i : std_logic; signal auto_neg_WR_Access_i : std_logic; signal auto_neg_MDIO_Access_i : std_logic; signal auto_neg_Strobe_i : std_logic; signal auto_neg_Strobe_pre_i : std_logic; signal auto_neg_RD_Data_i : std_logic_vector(31 downto 0); signal auto_neg_WR_Data_i : std_logic_vector(31 downto 0); signal auto_neg_Addr_i : std_logic_vector(11 downto 0); signal auto_neg_Addr_short_i : std_logic_vector(9 downto 0); type host_register_states is (START, LOWER_ADD, UPPER_ADD, TXCFG_ADD, FLOW_ADD, LOWER2_ADD, UPPER2_ADD, FILTER_ADD, MDIO_ADD, AUTONEG_CLEAR1, AUTONEG_CLEAR2, AUTONEG_LINK, AUTONEG_START, AUTONEG_WAIT, AUTONEG_CONFIRM, MAC_RDREG200, MAC_RDREG240, MAC_RDREG280, MAC_RDREG2C0, MAC_RDREG300, MAC_RDREG320, MAC_RDREG340, MAC_RDREG380, MAC_RDREG384, MAC_RDREG390, IDLE); signal statemachine : host_register_states := IDLE; signal start_delay : std_logic_vector(1 downto 0); signal auto_neg_Access_Done_iq0 : std_logic; begin -- architecture rtl gb_host_clk <= clk; -- We use 125Mhz clock. -- Mux block to arbitrate access to the outputs. gb_host_opcode <= sm_host_opcode when host_access_i = '1' else gb_host_opcode_i; gb_host_addr <= sm_host_addr when host_access_i = '1' else gb_host_addr_i; gb_host_wr_data <= sm_host_wr_data when host_access_i = '1' else gb_host_wr_data_i; gb_host_req <= sm_host_req when host_access_i = '1' else gb_host_req_i; gb_host_miim_sel <= sm_host_miim_sel when host_access_i = '1' else gb_host_miim_sel_i; sm_host_miim_rdy <= gb_host_miim_rdy and host_access_i; gb_host_miim_rdy_i <= gb_host_miim_rdy and not host_access_i; -- Mux the readback data depending on the mode. sm_host_rd_data <= gb_host_rd_data when host_access_i = '1'; gb_host_rd_data_i <= gb_host_rd_data when host_access_i = '0'; -- Only strobe when we are the one with access. auto_neg_Strobe_i <= auto_neg_Strobe_pre_i and not host_access_i; sm_host_available <= host_access_i; -- This is the HOSTAccess_SM : xemac_host_shim_if port map ( clk => clk, rst => rst, ADDR => auto_neg_Addr_short_i, WR_DATA => auto_neg_WR_Data_i, RD_DATA => auto_neg_RD_Data_i, STROBE => auto_neg_Strobe_i, MDIO_ACCESS => auto_neg_MDIO_Access_i, WR_ACCESS => auto_neg_WR_Access_i, ACCESS_DONE => auto_neg_Access_Done_iq0, HOSTADDR => gb_host_addr_i, HOSTMIIMSEL => gb_host_miim_sel_i, HOSTOPCODE => gb_host_opcode_i, HOSTREQ => gb_host_req_i, HOSTMIIMRDY => gb_host_miim_rdy_i, HOSTWRDATA => gb_host_wr_data_i, HOSTRDDATA => gb_host_rd_data_i ); -- expand to 12 bits for easy x"nnn" usage auto_neg_Addr_short_i <= auto_neg_Addr_i(9 downto 0); auto_neg_Access_Done_i <= auto_neg_Access_Done_iq0; -- This is the main state machine autoneg_state_machine_control : process (clk, rst) begin -- process autoneg_state_machine if rst = '1' then -- asynchronous reset (active high) start_delay <= (others => '1'); statemachine <= START; -- Start state. --auto_neg_Access_Done_i <= '0'; --elsif clk'event and clk = '1' then -- rising clock edge elsif rising_edge(clk) then -- delay by a clock --auto_neg_Access_Done_i <= auto_neg_Access_Done_iq0; start_delay <= start_delay - '1'; case statemachine is when START => start_delay <= (others => '1'); statemachine <= IDLE; when IDLE => if (start_delay = 0) then statemachine <= LOWER_ADD; end if; when LOWER_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= UPPER_ADD; end if; when UPPER_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= TXCFG_ADD; end if; when TXCFG_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= FLOW_ADD; end if; when FLOW_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= LOWER2_ADD; end if; when LOWER2_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= UPPER2_ADD; end if; when UPPER2_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= FILTER_ADD; end if; when FILTER_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= MDIO_ADD; end if; when MDIO_ADD => if (auto_neg_Access_Done_i = '1') then statemachine <= AUTONEG_CLEAR1; end if; when AUTONEG_CLEAR1 => if (auto_neg_Access_Done_i = '1') then statemachine <= AUTONEG_CLEAR2; end if; when AUTONEG_CLEAR2 => if (auto_neg_Access_Done_i = '1') then if DEBUG_MODE = '0' then statemachine <= AUTONEG_LINK; else statemachine <= AUTONEG_WAIT; end if; end if; when AUTONEG_LINK => if (auto_neg_Access_Done_i = '1') then statemachine <= AUTONEG_START; end if; -- Now we do autoneg loop when AUTONEG_START => if (auto_neg_Access_Done_i = '1') then statemachine <= AUTONEG_WAIT; end if; when AUTONEG_WAIT => -- we can allow others access here.. if (gb_autoneg_irq = '1') then -- Bit nasty, could be during a transaction statemachine <= AUTONEG_CONFIRM; end if; when AUTONEG_CONFIRM => if (auto_neg_Access_Done_i = '1') then --***statemachine <= AUTONEG_WAIT; -- Back to waiting... statemachine <= MAC_RDREG200; -- lets see readback all the MAC -- registers for fun end if; when MAC_RDREG200 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG240; end if; when MAC_RDREG240 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG280; end if; when MAC_RDREG280 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG2C0; end if; when MAC_RDREG2C0 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG300; end if; when MAC_RDREG300 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG320; end if; when MAC_RDREG320 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG340; end if; when MAC_RDREG340 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG380; end if; when MAC_RDREG380 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG384; end if; when MAC_RDREG384 => if (auto_neg_Access_Done_i = '1') then statemachine <= MAC_RDREG390; end if; when MAC_RDREG390 => if (auto_neg_Access_Done_i = '1') then statemachine <= START; end if; when others => statemachine <= START; end case; end if; end process autoneg_state_machine_control; -- Output side of the state machine now. autoneg_state_machine_states : process (clk, rst) is begin -- process autoneg_state_machine if rst = '1' then -- asynchronous reset (active high) gb_autoneg_done <= '0'; first_time_done_i <= '0'; -- first loop host_access_i <= '0'; -- autoneg has access auto_neg_WR_Access_i <= '0'; -- Clear all these. auto_neg_MDIO_Access_i <= '0'; auto_neg_Strobe_pre_i <= '0'; auto_neg_WR_Data_i <= (others => '0'); auto_neg_Addr_i <= (others => '0'); --elsif clk'event and clk = '1' then -- rising clock edge elsif rising_edge(clk) then --defaults gb_autoneg_done <= '0'; host_access_i <= '0'; auto_neg_WR_Access_i <= '0'; auto_neg_MDIO_Access_i <= '0'; auto_neg_Strobe_pre_i <= '0'; auto_neg_WR_Data_i <= (others => '0'); auto_neg_Addr_i <= (others => '0'); --first_time_done_i <= '0'; -- first loop case statemachine is when IDLE => gb_autoneg_done <= '0'; first_time_done_i <= '0'; -- first loop host_access_i <= '0'; -- autoneg has access auto_neg_WR_Access_i <= '0'; -- Clear all these. auto_neg_MDIO_Access_i <= '0'; auto_neg_Strobe_pre_i <= '0'; auto_neg_WR_Data_i <= (others => '0'); auto_neg_Addr_i <= (others => '0'); --------------- Reg Reads ------------------ when MAC_RDREG200 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"200"; when MAC_RDREG240 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"240"; when MAC_RDREG280 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"280"; when MAC_RDREG2C0 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"2C0"; when MAC_RDREG300 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"300"; when MAC_RDREG320 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"320"; when MAC_RDREG340 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"340"; when MAC_RDREG380 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"380"; when MAC_RDREG384 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"384"; when MAC_RDREG390 => auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= x"390"; --------------------------------------------------- when LOWER_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_Addr_i <= MAC_LOWER_ADDR; -- MAC is in funny byte ordering.. very very annoying auto_neg_WR_Data_i <= gb_autoneg_mac( (3*8)-1 downto (2*8) ) & gb_autoneg_mac( (4*8)-1 downto (3*8) ) & gb_autoneg_mac( (5*8)-1 downto (4*8) ) & gb_autoneg_mac( (6*8)-1 downto (5*8) ); when UPPER_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; -- Again, a crazy format. But with high bit set.. auto_neg_WR_Data_i <= x"1000" & gb_autoneg_mac( (1*8)-1 downto (0*8) ) & gb_autoneg_mac( (2*8)-1 downto (1*8) ); auto_neg_Addr_i <= MAC_UPPER_ADDR; when TXCFG_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_TXCFG_DATA; auto_neg_Addr_i <= MAC_TXCFG_ADDR; when FLOW_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_FLOW_DATA; auto_neg_Addr_i <= MAC_FLOW_ADDR; when LOWER2_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; --auto_neg_WR_Data_i <= MAC_LOWER_DATA; auto_neg_Addr_i <= MAC_LOWER2_ADDR; auto_neg_WR_Data_i <= gb_autoneg_mac( (3*8)-1 downto (2*8) ) & gb_autoneg_mac( (4*8)-1 downto (3*8) ) & gb_autoneg_mac( (5*8)-1 downto (4*8) ) & gb_autoneg_mac( (6*8)-1 downto (5*8) ); when UPPER2_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; --auto_neg_WR_Data_i <= MAC_UPPER_DATA and x"0000FFFF"; auto_neg_Addr_i <= MAC_UPPER2_ADDR; auto_neg_WR_Data_i <= x"0000" & gb_autoneg_mac( (1*8)-1 downto (0*8) ) & gb_autoneg_mac( (2*8)-1 downto (1*8) ); when FILTER_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_FILTER_DATA; auto_neg_Addr_i <= MAC_FILTER_ADDR; when MDIO_ADD => auto_neg_WR_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; if DEBUG_MODE = '0' then auto_neg_WR_Data_i <= (MAC_MDIO_DIVIDE or x"00000040"); --enable MDIO too else auto_neg_WR_Data_i <= (x"00000022" or x"00000040"); --enable MDIO too end if; auto_neg_Addr_i <= MAC_MDIO_ADDR; -- This is an MDIO access to stop autoneg, and clear the registers. when AUTONEG_CLEAR1 => auto_neg_WR_Access_i <= '1'; auto_neg_MDIO_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_AUTONEG_CLEAR1_DATA; auto_neg_Addr_i <= MAC_MDIO_REG0; -- Clear and enable IRQ line. when AUTONEG_CLEAR2 => auto_neg_WR_Access_i <= '1'; auto_neg_MDIO_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_AUTONEG_CLEAR2_DATA; auto_neg_Addr_i <= MAC_MDIO_REG16; -- Start Autoneg when AUTONEG_LINK => auto_neg_WR_Access_i <= '1'; auto_neg_MDIO_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_AUTONEG_LINK_DATA; auto_neg_Addr_i <= MAC_MDIO_REG4; -- Now we do autoneg loop -- Start Autoneg when AUTONEG_START => auto_neg_WR_Access_i <= '1'; auto_neg_MDIO_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_AUTONEG_START_DATA; auto_neg_Addr_i <= MAC_MDIO_REG0; when AUTONEG_WAIT => -- we can allow others access here.. if DEBUG_MODE = '1' then gb_autoneg_done <= '1'; -- Fake it, we never did auto neg end if; host_access_i <= first_time_done_i; -- when AUTONEG_CONFIRM => gb_autoneg_done <= '1'; first_time_done_i <= '1'; -- We've loopd once.. auto_neg_WR_Access_i <= '1'; auto_neg_MDIO_Access_i <= '1'; auto_neg_Strobe_pre_i <= '1' and not auto_neg_Access_Done_i; auto_neg_WR_Data_i <= MAC_AUTONEG_CLEAR2_DATA; auto_neg_Addr_i <= MAC_MDIO_REG16; when others => null; end case; end if; end process autoneg_state_machine_states; end architecture rtl;