OpenCores
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;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.