URL
https://opencores.org/ocsvn/xenie/xenie/trunk
Subversion Repositories xenie
[/] [xenie/] [trunk/] [examples/] [Eth_example/] [ip_repo/] [axi_mdio/] [src/] [axi_mdio_master_top.vhd] - Rev 4
Compare with Previous | Blame | View Log
------------------------------------------------------------------------------- -- -- (C) Copyright 2017 DFC Design, s.r.o., Brno, Czech Republic -- Author: Marek Kvas (m.kvas@dspfpga.com) ------------------------------------------------------------------------------- -- This file is part of UDP/IPv4 for 10 G Ethernet core. -- -- UDP/IPv4 for 10 G Ethernet core is free software: you can -- redistribute it and/or modify it under the terms of -- the GNU Lesser General Public License as published by the Free -- Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- UDP/IPv4 for 10 G Ethernet core 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 Lesser General Public License -- for more details. -- -- You should have received a copy of the GNU Lesser General Public -- License along with UDP/IPv4 for 10 G Ethernet core. If not, -- see <http://www.gnu.org/licenses/>. ------------------------------------------------------------------------------- -- -- This is top-level file of axi_mdio core that is meant to be used to -- control PHYs via clause 45 MDIO protocol. It can be connected to the system -- using AXI-lite bus. -- -- It is clause 45 MDIO master. -- -- This file replaced original top-level for wishbone access. -- -- -- ------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity mdio_master_top is generic( g_include_cmd_fifo : boolean := true; g_add_synchronizers : boolean := true; C_S_AXI_DATA_WIDTH : integer := 32; C_S_AXI_ADDR_WIDTH : integer := 7 ); port ( -- AXI Lite slave interface for control purposes -- AXI Lite bus Slave interface S_AXI_ACLK : in std_logic; S_AXI_ARESETN : in std_logic; S_AXI_AWADDR : in std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0); S_AXI_AWPROT : in std_logic_vector(2 downto 0); S_AXI_AWVALID : in std_logic; S_AXI_AWREADY : out std_logic; S_AXI_WDATA : in std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0); S_AXI_WSTRB : in std_logic_vector((C_S_AXI_DATA_WIDTH/8)-1 downto 0); S_AXI_WVALID : in std_logic; S_AXI_WREADY : out std_logic; S_AXI_BRESP : out std_logic_vector(1 downto 0); S_AXI_BVALID : out std_logic; S_AXI_BREADY : in std_logic; S_AXI_ARADDR : in std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0); S_AXI_ARPROT : in std_logic_vector(2 downto 0); S_AXI_ARVALID : in std_logic; S_AXI_ARREADY : out std_logic; S_AXI_RDATA : out std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0); S_AXI_RRESP : out std_logic_vector(1 downto 0); S_AXI_RVALID : out std_logic; S_AXI_RREADY : in std_logic; -- MDIO lines MDC : out std_logic; -- MDIO Clock output MDIO_I : in std_logic; -- MDIO Input data MDIO_O : out std_logic; -- MDIO Output data MDIO_OE : out std_logic -- MDIO Output Enable, active low!!! ); end entity mdio_master_top; architecture structural of mdio_master_top is COMPONENT cmd_fifo IS PORT ( clk : IN STD_LOGIC; srst : IN STD_LOGIC; din : IN STD_LOGIC_VECTOR(29 DOWNTO 0); wr_en : IN STD_LOGIC; rd_en : IN STD_LOGIC; dout : OUT STD_LOGIC_VECTOR(29 DOWNTO 0); full : OUT STD_LOGIC; empty : OUT STD_LOGIC; data_count : OUT STD_LOGIC_VECTOR(4 DOWNTO 0) ); END COMPONENT; -- registers signal divr : std_logic_vector(7 downto 0); -- clock prescale register signal mdio_run : std_logic; signal mdio_busy : std_logic; signal mdio_dout : std_logic_vector(15 downto 0); -- transmit register signal mdio_din : std_logic_vector(15 downto 0); -- receive register signal mdio_phyaddr : std_logic_vector(4 downto 0); signal mdio_regaddr : std_logic_vector(4 downto 0); signal mdio_op : std_logic_vector(1 downto 0); signal mdio_start : std_logic_vector(1 downto 0); signal mdio_preamb_sup : std_logic; -------------------------------------------------------------------------------- -- Signals for AXI state machines type read_fsm_type is (R_RESET, R_IDLE, R_READ, R_PRESENT); signal read_fsm_cur : read_fsm_type; signal axi_arready_i : std_logic; signal axi_rvalid_i : std_logic; signal axi_rresp_i : std_logic_vector(S_AXI_RRESP'range); type write_fsm_type is (W_RESET, W_IDLE, W_DATA, W_RESP); signal write_fsm_cur : write_fsm_type; signal axi_awready_i : std_logic; signal axi_wready_i : std_logic; signal axi_bvalid_i : std_logic; signal write_data_latched : std_logic; signal write_data : std_logic_vector(S_AXI_WDATA'range); signal write_strb : std_logic_vector(S_AXI_WSTRB'range); signal write_addr : std_logic_vector(S_AXI_AWADDR'range); signal reg_read_req : std_logic; signal reg_read_ack : std_logic; signal reg_write_req : std_logic; signal axi_bresp_i : std_logic_vector(S_AXI_BRESP'range); signal axi_rdata_i : std_logic_vector(S_AXI_RDATA'range); signal read_addr : std_logic_vector(S_AXI_ARADDR'range); signal reg_read_data : std_logic_vector(S_AXI_RDATA'range); -------------------------------------------------------------------------------- signal mdio_i_meta : std_logic; signal mdio_i_sync : std_logic; signal rst : std_logic; -- Command fifo handling signals signal cmd_fifo_in : std_logic_vector(29 downto 0); signal cmd_fifo_wren : std_logic; signal cmd_fifo_rden : std_logic; signal cmd_fifo_out : std_logic_vector(29 downto 0); signal cmd_fifo_full : std_logic := '0'; signal cmd_fifo_empty : std_logic := '1'; signal cmd_fifo_dcount : std_logic_vector(4 downto 0) := (others => '0'); signal mdio_busy_prolonged : std_logic; signal mdio_busy_prolong_cnt : unsigned(divr'left + 2 downto 0); begin rst <= not S_AXI_ARESETN; -------------------------------------------------------------------------------- -- AXI Lite slave registers -------------------------------------------------------------------------------- -- Read state machine read_fsm_proc : process (S_AXI_ACLK) begin if rising_edge(S_AXI_ACLK) then if S_AXI_ARESETN = '0' then axi_arready_i <= '0'; axi_rvalid_i <= '0'; read_fsm_cur <= R_RESET; reg_read_req <= '0'; else reg_read_req <= '0'; case read_fsm_cur is when R_RESET => read_fsm_cur <= R_IDLE; axi_arready_i <= '1'; when R_IDLE => if S_AXI_ARVALID = '1' then -- There is a valid address on the bus, so catch it and read read_fsm_cur <= R_READ; axi_arready_i <= '0'; read_addr <= S_AXI_ARADDR; reg_read_req <= '1'; end if; when R_READ => -- reg_read_req <= '1'; axi_rdata_i <= reg_read_data; if reg_read_ack = '1' then axi_rvalid_i <= '1'; read_fsm_cur <= R_PRESENT; reg_read_req <= '0'; end if; when R_PRESENT => if S_AXI_RREADY = '1' then -- Master consumed data, wait for another transaction axi_arready_i <= '1'; axi_rvalid_i <= '0'; read_fsm_cur <= R_IDLE; end if; end case; end if; end if; end process; axi_rresp_i <= (others => '0'); -- Read is always OK. --Registers read_data_mux_proc : process (S_AXI_ACLK) begin if rising_edge(S_AXI_ACLK) then reg_read_ack <= '0'; if reg_read_req = '1' then reg_read_ack <= '1'; reg_read_data <= (others => '0'); -- We don't need to decode MSB because it is already checked -- and we don't have to consider lowest two bits case to_integer(unsigned(read_addr(read_addr'left downto 2))) is when 0 => reg_read_data(divr'range) <= divr; when 1 => reg_read_data(0) <= mdio_busy or not cmd_fifo_empty; reg_read_data(12 downto 8) <= cmd_fifo_dcount; reg_read_data(13) <= cmd_fifo_full; reg_read_data(31 downto 24) <= x"11"; when 2 => reg_read_data(mdio_dout'range) <= mdio_dout; when 3 => reg_read_data(mdio_din'range) <= mdio_din; when 4 => reg_read_data(15 downto 0) <= "000000" & mdio_start & "000000" & mdio_op; when 5 => reg_read_data(15 downto 0) <= "000" & mdio_phyaddr & "000" & mdio_regaddr; when 6 => reg_read_data <= (others => '0'); when others => reg_read_data <= x"deaddead"; end case; end if; end if; -- clk end process; -- Write state machine write_fsm_proc : process (S_AXI_ACLK) begin if rising_edge(S_AXI_ACLK) then if S_AXI_ARESETN = '0' then axi_awready_i <= '0'; axi_wready_i <= '0'; axi_bvalid_i <= '0'; write_fsm_cur <= W_RESET; reg_write_req <= '0'; else reg_write_req <= '0'; case write_fsm_cur is when W_RESET => -- Make sure ready is always high in idle write_fsm_cur <= W_IDLE; axi_awready_i <= '1'; axi_wready_i <= '1'; write_data_latched <= '0'; when W_IDLE => -- Handle address - we can actually update it always write_addr <= S_AXI_AWADDR; if S_AXI_AWVALID = '1' then axi_awready_i <= '0'; if S_AXI_WVALID = '0' and write_data_latched = '0' then -- We need to wait for data write_fsm_cur <= W_DATA; else -- Data are already valid, so we can respond immediately write_fsm_cur <= W_RESP; -- Respond axi_bvalid_i <= '1'; -- Start real write to registers reg_write_req <= '1'; end if; end if; -- Latch data when valid if S_AXI_WVALID = '1' then write_data_latched <= '1'; write_data <= S_AXI_WDATA; write_strb <= S_AXI_WSTRB; axi_awready_i <= '0'; end if; when W_DATA => -- Latch data as soon as valid; address is already here if S_AXI_WVALID = '1' then write_fsm_cur <= W_RESP; axi_bvalid_i <= '1'; -- Start real write to registers reg_write_req <= '1'; -- Latch data write_data <= S_AXI_WDATA; write_strb <= S_AXI_WSTRB; axi_awready_i <= '0'; end if; when W_RESP => if S_AXI_BREADY = '1' then axi_bvalid_i <= '0'; axi_awready_i <= '1'; axi_awready_i <= '1'; write_fsm_cur <= W_IDLE; end if; -- This can be done always write_data_latched <= '0'; end case; end if; end if; end process; -- Writable registers handling wr_handling_proc : process (S_AXI_ACLK, S_AXI_ARESETN) begin if S_AXI_ARESETN = '0' then divr <= (others => '1'); mdio_dout <= (others => '0'); mdio_start <= (others => '0'); mdio_op <= (others => '0'); mdio_phyaddr <= (others => '0'); mdio_regaddr <= (others => '0'); mdio_run <= '0'; mdio_preamb_sup <= '0'; elsif rising_edge(S_AXI_ACLK) then mdio_run <= '0'; -- must be active one cycle only if reg_write_req = '1' then case to_integer(unsigned(write_addr(write_addr'left downto 2))) is when 0 => mdio_preamb_sup <= write_data(8); divr <= write_data(7 downto 0); when 1 => mdio_run <= write_data(1); when 2 => mdio_dout <= write_data(mdio_dout'range); when 3 => null; when 4 => mdio_start <= write_data(9 downto 8); mdio_op <= write_data(1 downto 0); when 5 => mdio_phyaddr <= write_data(12 downto 8); mdio_regaddr <= write_data(4 downto 0); when 6 => mdio_run <= write_data(31); mdio_start <= write_data(29 downto 28); mdio_op <= write_data(27 downto 26); mdio_phyaddr <= write_data(25 downto 21); mdio_regaddr <= write_data(20 downto 16); mdio_dout <= write_data(mdio_dout'range); when others => null; end case; end if; end if; end process; -- Response is always OK axi_bresp_i <= (others => '0'); fifo_gen : if g_include_cmd_fifo generate cmd_fifo_in <= mdio_start & mdio_op & mdio_phyaddr & mdio_regaddr & mdio_dout; cmd_fifo_wren <= mdio_run and not cmd_fifo_full; cmd_fifo_inst : cmd_fifo port map ( clk => S_AXI_ACLK, srst => rst, din => cmd_fifo_in, wr_en => cmd_fifo_wren, rd_en => cmd_fifo_rden, dout => cmd_fifo_out, full => cmd_fifo_full, empty => cmd_fifo_empty, data_count => cmd_fifo_dcount ); cmd_fifo_rden <= not cmd_fifo_empty and not mdio_busy and not mdio_busy_prolonged; mdio_c_inst : entity work.mdio_c port map ( CLK => S_AXI_ACLK, DIV_CLK => divr, PREAMB_SUP => mdio_preamb_sup, START_OP => cmd_fifo_out(29 downto 28), OPERATION => cmd_fifo_out(27 downto 26), PHY_ADDR => cmd_fifo_out(25 downto 21), REG_ADDR => cmd_fifo_out(20 downto 16), DATA_IN => cmd_fifo_out(15 downto 0), DATA_OUT => mdio_din, DATA_RDY => open, RUN_OP => cmd_fifo_rden, BUSY => mdio_busy, MDC => MDC, MDIO_I => mdio_i_sync, MDIO_O => MDIO_O, MDIO_OE => MDIO_OE ); -- prolong busy to keep at leas 2 bit intervals between -- consecutive operations prolong_busy_proc : process(S_AXI_ACLK) begin if rising_edge(S_AXI_ACLK) then if mdio_busy = '1' then mdio_busy_prolonged <= '1'; mdio_busy_prolong_cnt <= unsigned(divr) & "00"; elsif mdio_busy_prolong_cnt > 0 then mdio_busy_prolong_cnt <= mdio_busy_prolong_cnt - 1; else mdio_busy_prolonged <= '0'; end if; end if; end process; end generate; no_fifo_gen : if g_include_cmd_fifo = FALSE generate cmd_fifo_full <= mdio_busy; mdio_c_inst : entity work.mdio_c port map ( CLK => S_AXI_ACLK, DIV_CLK => divr, PREAMB_SUP => mdio_preamb_sup, START_OP => mdio_start, OPERATION => mdio_op, PHY_ADDR => mdio_phyaddr, REG_ADDR => mdio_regaddr, DATA_IN => mdio_dout, DATA_OUT => mdio_din, DATA_RDY => open, RUN_OP => mdio_run, BUSY => mdio_busy, MDC => MDC, MDIO_I => mdio_i_sync, MDIO_O => MDIO_O, MDIO_OE => MDIO_OE ); end generate; -- Add synchronizer on input if required mdio_i_sync_true : if g_add_synchronizers generate mdio_i_sync_proc : process(S_AXI_ACLK) begin if rising_edge(S_AXI_ACLK) then mdio_i_meta <= MDIO_I; mdio_i_sync <= mdio_i_meta; end if; end process; end generate; -- If input is already synchronized, don't add useles register chain mdio_i_sync_false : if not g_add_synchronizers generate mdio_i_sync <= MDIO_I; end generate; -- Outputs assignments S_AXI_AWREADY <= axi_arready_i; S_AXI_WREADY <= axi_wready_i; S_AXI_BRESP <= axi_bresp_i; S_AXI_BVALID <= axi_bvalid_i; S_AXI_ARREADY <= axi_arready_i; S_AXI_RDATA <= axi_rdata_i; S_AXI_RRESP <= axi_rresp_i; S_AXI_RVALID <= axi_rvalid_i; end architecture structural;