OpenCores
URL https://opencores.org/ocsvn/funbase_ip_library/funbase_ip_library/trunk

Subversion Repositories funbase_ip_library

[/] [funbase_ip_library/] [trunk/] [TUT/] [ip.hwp.interface/] [eth_dm9000a_ctrl/] [1.0/] [vhd/] [DM9kA_send_module.vhd] - Rev 145

Compare with Previous | Blame | View Log

-------------------------------------------------------------------------------
-- Title      : DM9kA controller, sender module
-- Project    : 
-------------------------------------------------------------------------------
-- File       : DM9kA_send_module.vhd
-- Author     : Jussi Nieminen  <niemin95@galapagosinkeiju.cs.tut.fi>
-- Last update: 2012-04-04
-------------------------------------------------------------------------------
-- Description: Handles sending procedures application -> eth
-- Includes a state machine where two states (init and done) include their own
-- sub-state machines.
-------------------------------------------------------------------------------
-- Revisions  :
-- Date        Version  Author  Description
-- 2009/08/25  1.0      niemin95        Created
-------------------------------------------------------------------------------
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.DM9kA_ctrl_pkg.all;
 
 
entity DM9kA_send_module is
 
  port (
    clk                     : in  std_logic;
    rst_n                   : in  std_logic;
    -- from interrupt handler
    tx_completed_in         : in  std_logic;
    -- to and from comm module
    comm_req_out            : out std_logic;
    comm_grant_in           : in  std_logic;
    reg_addr_out            : out std_logic_vector(7 downto 0);
    config_data_out         : out std_logic_vector(7 downto 0);
    read_not_write_out      : out std_logic;
    config_valid_out        : out std_logic;
    data_from_comm_in       : in  std_logic_vector(data_width_c-1 downto 0);
    data_from_comm_valid_in : in  std_logic;
    comm_busy_in            : in  std_logic;
    -- to comm module
    tx_data_out             : out std_logic_vector(data_width_c-1 downto 0);
    tx_data_valid_out       : out std_logic;
    tx_re_in                : in  std_logic;
    -- from upper level
    tx_data_in              : in  std_logic_vector(data_width_c-1 downto 0);
    tx_data_valid_in        : in  std_logic;
    tx_re_out               : out std_logic;
    tx_MAC_addr_in          : in  std_logic_vector(47 downto 0);
    new_tx_in               : in  std_logic;
    tx_len_in               : in  std_logic_vector(tx_len_w_c-1 downto 0);
    tx_frame_type_in        : in  std_logic_vector(15 downto 0)
    );
 
end DM9kA_send_module;
 
 
architecture rtl of DM9kA_send_module is
 
  -- There are 3 state machines.
  -- not much to initialize, but our own MAC we must get
  type   init_state_type is (get_MAC, done);
  signal init_state_r : init_state_type;
 
  -- This FSM is used during init to read mac addr
  type   conf_state_type is (write_conf, read_reply, wait_busy);
  signal conf_state_r : conf_state_type;
 
  -- counter used when getting the MAC address
  signal addr_cnt_r : integer range 0 to MAC_len_c-1;
 
  -- vhdl doesn't understand signals indexing std_logic_vectors, so we have to
  -- do this:
  type MAC_addr_type is array (0 to MAC_len_c-1) of std_logic_vector(7 downto 0);
 
  signal own_MAC_r  : MAC_addr_type;
  signal trgt_MAC_r : MAC_addr_type;
 
  -- This FSM is used in normal operation
  type tx_state_type is (wait_tx, conf_len_hi, conf_len_lo, conf_tx_reg,
                         write_trgt_MAC, write_own_MAC, write_frame_type,
                         count_data, send_cmd);
  signal tx_state_r : tx_state_type;
 
 
 
  signal tx_len_r      : std_logic_vector(tx_len_w_c-1 downto 0);
  signal tx_len_int    : integer range 0 to 2**tx_len_w_c-1;
  signal tx_data_cnt_r : integer range 0 to 2**tx_len_w_c-1;
 
  signal tx_data_r       : std_logic_vector(data_width_c-1 downto 0);
  signal tx_data_valid_r : std_logic;
 
  signal comm_req_r       : std_logic;
  signal start_counting_r : std_logic;
 
  -- there can be maximum of 2 transfers at a time
  signal tx_count_r : integer range 0 to 2;
 
 
-------------------------------------------------------------------------------
begin  -- rtl
-------------------------------------------------------------------------------
 
  --
  -- Combinatorial process
  -- switch between inner registers and data input
  -- 
  tx_data_mux : process (tx_data_in, tx_data_valid_in, tx_state_r,
                        tx_data_r, tx_data_valid_r, tx_re_in)
  begin  -- process tx_data_mux
 
    if tx_state_r /= count_data then
      -- writing ethernet frame headers
      tx_data_out       <= tx_data_r;
      tx_data_valid_out <= tx_data_valid_r;
      -- no re to upper level
      tx_re_out         <= '0';
 
    else
      -- writing payload
      tx_data_out       <= tx_data_in;
      tx_data_valid_out <= tx_data_valid_in;
      tx_re_out         <= tx_re_in;
 
    end if;
  end process tx_data_mux;
 
  comm_req_out <= comm_req_r;
  tx_len_int   <= to_integer(unsigned(tx_len_r));
 
 
  --
  -- Sequential process for state machine
  --
  main : process (clk, rst_n)
 
    -- helping with odd length transfers
    variable odd_len_compensation_v : integer range 0 to 1;
 
  begin  -- process main
    if rst_n = '0' then                 -- asynchronous reset (active low)
 
      init_state_r <= get_MAC;
      tx_state_r   <= wait_tx;
      conf_state_r <= write_conf;
 
      own_MAC_r  <= (others => (others => '0'));
      trgt_MAC_r <= (others => (others => '0'));
      addr_cnt_r <= 0;
 
      reg_addr_out       <= (others => '0');
      config_data_out    <= (others => '0');
      config_valid_out   <= '0';
      read_not_write_out <= '0';
 
      comm_req_r       <= '0';
      tx_data_r        <= (others => '0');
      tx_data_valid_r  <= '0';
      tx_len_r         <= (others => '0');
      start_counting_r <= '0';
      tx_count_r       <= 0;
 
    elsif clk'event and clk = '1' then  -- rising clock edge
 
      -- decrease counter when a tx is completed
      if tx_completed_in = '1' then
        tx_count_r <= tx_count_r - 1;
      end if;
 
 
 
      case init_state_r is
        -----------------------------------------------------------------------
        -- Getting our own MAC address, needs another state machine
        when get_MAC =>
 
          comm_req_r <= '1';
 
          if comm_grant_in = '1' then
            -- sender has comm modules attention
 
            case conf_state_r is
              when write_conf =>
 
                -- MAC address is in registers x'10 -> x'15
                reg_addr_out       <= std_logic_vector(unsigned(MAC6_c) - to_unsigned(addr_cnt_r, 8));
                read_not_write_out <= '1';
                config_valid_out   <= '1';
 
                -- when busy comes up, comm module has started working
                if comm_busy_in = '1' then
                  conf_state_r <= read_reply;
                end if;
 
              when read_reply =>
 
                if data_from_comm_valid_in = '1' then
                  own_MAC_r(addr_cnt_r) <= data_from_comm_in(7 downto 0);
                  conf_state_r          <= wait_busy;
                end if;
 
              when wait_busy =>
 
                if comm_busy_in = '0' then
 
                  if addr_cnt_r = MAC_len_c-1 then
                    init_state_r     <= done;
                    addr_cnt_r       <= 0;
                    comm_req_r       <= '0';
                    config_valid_out <= '0';
                  else
                    addr_cnt_r <= addr_cnt_r + 1;
                  end if;
                  conf_state_r <= write_conf;
 
                end if;
              when others => null;
            end case;
          end if;
          -----------------------------------------------------------------------
          -- MAC address received, moving on
 
        when done =>
          -- init done, meaning normal operation
 
          -- new_tx_in, tx_len_in and tx_MAC_addr_in must remain stable until
          -- the first data is read from the upper level.
 
          -- tx state machine
          case tx_state_r is
            when wait_tx =>
 
              -- new transfer waiting, and less than 2 on the way
              if new_tx_in = '1' and tx_count_r /= 2 then
                -- add here checking of number of on-going txs
 
                comm_req_r <= '1';
                tx_len_r   <= std_logic_vector(unsigned(tx_len_in) + to_unsigned(eth_header_len_c, tx_len_w_c));
 
                for n in 0 to MAC_len_c-1 loop
                  trgt_MAC_r(n) <= tx_MAC_addr_in((n+1)*8-1 downto n*8);
                end loop;  -- n
 
                if comm_grant_in = '1' and comm_req_r = '1' then
                  -- increase number of txs
                  tx_count_r <= tx_count_r + 1;
                  tx_state_r <= conf_len_hi;
                end if;
              end if;
 
            when conf_len_hi =>
 
              -- write high part of the tx len
              case conf_state_r is
                when write_conf =>
 
                  reg_addr_out                             <= TXPLH_c;
                  -- upper bits remain zero while lower ones get the values of
                  -- higher tx len bits
                  config_data_out                          <= (others => '0');
                  config_data_out(tx_len_w_c-8-1 downto 0) <= tx_len_r(tx_len_w_c-1 downto 8);
                  read_not_write_out                       <= '0';
                  config_valid_out                         <= '1';
 
                  if comm_busy_in = '1' then
                    conf_state_r <= wait_busy;
                  end if;
 
                when wait_busy =>
 
                  if comm_busy_in = '0' then
                    config_valid_out <= '0';
                    conf_state_r     <= write_conf;
                    tx_state_r       <= conf_len_lo;
                  end if;
 
                when others => null;
              end case;
 
            when conf_len_lo =>
 
              -- write lower part of the tx len
              case conf_state_r is
                when write_conf =>
 
                  reg_addr_out       <= TXPLL_c;
                  config_data_out    <= tx_len_r(7 downto 0);
                  read_not_write_out <= '0';
                  config_valid_out   <= '1';
 
                  if comm_busy_in = '1' then
                    conf_state_r <= wait_busy;
                  end if;
 
                when wait_busy =>
 
                  if comm_busy_in = '0' then
                    config_valid_out <= '0';
                    conf_state_r     <= write_conf;
                    tx_state_r       <= conf_tx_reg;
                  end if;
 
                when others => null;
              end case;
 
 
            when conf_tx_reg =>
 
              -- when the address of tx register is written, comm module
              -- automagically switches to tx mode and starts receiving tx data
              -- until config_valid_out signal is lowered.
 
              reg_addr_out       <= tx_data_reg_c;
              read_not_write_out <= '0';
              config_valid_out   <= '1';
 
              -- when comm busy, move to send phase
              if comm_busy_in = '1' then
                tx_state_r <= write_trgt_MAC;
                addr_cnt_r <= 0;
              end if;
 
 
            when write_trgt_MAC =>
 
              -- write two bytes of address at once (MSB first)
              tx_data_r <= trgt_MAC_r(MAC_len_c - 2*addr_cnt_r - 2)
                           & trgt_MAC_r(MAC_len_c - 2*addr_cnt_r - 1);
              tx_data_valid_r <= '1';
 
              -- when comm reads, increase counter or move on
              if tx_re_in = '1' then
                if addr_cnt_r = MAC_len_c/2 - 1 then
                  tx_data_valid_r <= '0';
                  addr_cnt_r      <= 0;
                  tx_state_r      <= write_own_MAC;
                else
                  addr_cnt_r <= addr_cnt_r + 1;
                end if;
              end if;
 
 
            when write_own_MAC =>
 
              tx_data_r <= own_MAC_r(MAC_len_c - 2*addr_cnt_r - 2)
                           & own_MAC_r(MAC_len_c - 2*addr_cnt_r - 1);
              tx_data_valid_r <= '1';
 
              -- when comm reads, increase counter or move on
              if tx_re_in = '1' then
                if addr_cnt_r = MAC_len_c/2 - 1 then
                  tx_data_valid_r <= '0';
                  addr_cnt_r      <= 0;
                  tx_state_r      <= write_frame_type;
                else
                  addr_cnt_r <= addr_cnt_r + 1;
                end if;
              end if;
 
 
            when write_frame_type =>
 
              tx_data_r       <= tx_frame_type_in(7 downto 0) & tx_frame_type_in(15 downto 8);
              tx_data_valid_r <= '1';
 
              if tx_re_in = '1' then
                -- the tx_data mux now switches the output, so we can clear
                -- these registers
                tx_data_r        <= (others => '0');
                tx_data_valid_r  <= '0';
                tx_data_cnt_r    <= 0;
                start_counting_r <= '1';
              end if;
 
              -- delay, so that changing input of tx_re_out doesn't cause a glitch
              if start_counting_r = '1' then
                tx_data_r        <= (others => '0');
                tx_data_valid_r  <= '0';
                tx_data_cnt_r    <= 0;
                start_counting_r <= '0';
                tx_state_r       <= count_data;
              end if;
 
 
            when count_data =>
              -- data comes straight from the upper level, just count how much
              -- has been sent and stop the transmission in time
 
              if tx_re_in = '1' then
 
                -- tx len in bytes but writing words, that's why the division
                -- by two
                odd_len_compensation_v := to_integer(unsigned(tx_len_r(0 downto 0)));
                if tx_data_cnt_r = (tx_len_int - eth_header_len_c) / 2 - 1 + odd_len_compensation_v then
                  -- All sent. Notify the comm module by lowering the config_valid_out
                  config_valid_out <= '0';
                  tx_data_cnt_r    <= 0;
 
                  -- request send if constant says so (if DM9kA is configured
                  -- to start sending in advance, there is no need to raise the
                  -- tx request bit)
                  if send_cmd_en_c = 1 then
                    tx_state_r <= send_cmd;
                  else
                    tx_state_r <= wait_tx;
                    comm_req_r <= '0';
                  end if;
 
                else
                  tx_data_cnt_r <= tx_data_cnt_r + 1;
                end if;
              end if;
 
 
            when send_cmd =>
 
              -- feed in send cmd
              case conf_state_r is
                when write_conf =>
 
                  reg_addr_out       <= TCR_c;
                  -- start tx
                  config_data_out    <= x"01";
                  read_not_write_out <= '0';
                  config_valid_out   <= '1';
 
                  if comm_busy_in = '1' then
                    conf_state_r <= wait_busy;
                  end if;
 
                when wait_busy =>
 
                  if comm_busy_in = '0' then
                    config_valid_out <= '0';
                    conf_state_r     <= write_conf;
                    tx_state_r       <= wait_tx;
                    comm_req_r       <= '0';
                  end if;
 
                when others => null;
              end case;
 
 
            when others => null;
          end case;  -- tx_state_r
        when others => null;
      end case;  -- init_state_r
    end if;
  end process main;
 
 
end rtl;
 

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.