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

Subversion Repositories fade_ether_protocol

[/] [fade_ether_protocol/] [trunk/] [old_stable_version/] [FPGA_no_MAC/] [src/] [common/] [desc_manager_simple.vhd] - Rev 34

Compare with Previous | Blame | View Log

-------------------------------------------------------------------------------
-- Title      : FPGA Ethernet interface - descriptor manager
-- Project    : 
-------------------------------------------------------------------------------
-- File       : desc_manager.vhd
-- Author     : Wojciech M. Zabolotny (wzab@ise.pw.edu.pl)
-- License    : BSD License
-- Company    : 
-- Created    : 2012-03-30
-- Last update: 2012-09-01
-- Platform   : 
-- Standard   : VHDL'93
-------------------------------------------------------------------------------
-- Description: This file implements the state machine, which manages the
-- table of packet descriptors, used to resend only not confirmed packets
-------------------------------------------------------------------------------
-- Copyright (c) 2012 
-------------------------------------------------------------------------------
-- Revisions  :
-- Date        Version  Author  Description
-- 2012-03-30  1.0      WZab      Created
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
use ieee.std_logic_textio.all;
library work;
use work.pkt_ack_pkg.all;
 
package desc_mgr_pkg is
 
  constant N_OF_PKTS : integer := 32;
  constant N_OF_SETS : integer := 65536;
 
  type T_PKT_DESC is record
    set       : integer range 0 to N_OF_SETS-1;  -- number of sets
    confirmed : std_logic;
    valid     : std_logic;
    sent      : std_logic;
  end record;
 
end desc_mgr_pkg;
 
 
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
library work;
use work.pkt_ack_pkg.all;
use work.desc_mgr_pkg.all;
 
-- The below implementation of the descriptor memory is awfull,
-- but seemed to be necessary to force XST to infer it as an
-- single port BRAM.
-- I simply provide vector long enough to accomodate my T_PKT_DESC
-- type, and hope that the synthesis tool (XST) will optimize out
-- unused bits.should be inferred as block memory (so be carefull
-- when modifying the below process)!
 
entity desc_memory is
 
  port (
    clk       : in  std_logic;
    desc_we   : in  std_logic;
    desc_addr : in  integer range 0 to N_OF_PKTS-1;
    desc_out  : in  T_PKT_DESC;
    desc_in   : out T_PKT_DESC);
 
end desc_memory;
 
architecture beh1 of desc_memory is
 
  type T_PKT_DESC_MEM is array (0 to N_OF_PKTS-1) of unsigned(22 downto 0);
  signal desc_mem : T_PKT_DESC_MEM        := (others => (others => '0'));
  signal din      : unsigned(22 downto 0) := (others => '0');
  signal dout     : unsigned(22 downto 0) := (others => '0');
  signal rdaddr   : integer range 0 to N_OF_PKTS-1;
 
begin  -- beh1
 
  process (desc_out, dout)
  begin  -- process
    din               <= (others => '0');
    din(22)           <= desc_out.valid;
    din(21)           <= desc_out.confirmed;
    din(20)           <= desc_out.sent;
    din(19 downto 0)  <= to_unsigned(desc_out.set, 20);
    desc_in.valid     <= dout(22);
    desc_in.confirmed <= dout(21);
    desc_in.sent      <= dout(20);
    desc_in.set       <= to_integer(dout(19 downto 0));
  end process;
 
  process (clk)
  begin  -- process
    if (clk'event and clk = '1') then   -- rising clock edge
      if (desc_we = '1') then
        desc_mem(desc_addr) <= din;
      end if;
      rdaddr <= desc_addr;
    end if;
  end process;
  dout <= desc_mem(rdaddr);
 
end beh1;
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
library work;
use work.pkt_ack_pkg.all;
use work.desc_mgr_pkg.all;
 
entity desc_manager is
 
  generic (
    N_OF_PKTS : integer := 64);         -- Number of packet_logi buffers
 
  port (
    -- Data input interface
    dta        : in  std_logic_vector(31 downto 0);
    dta_we     : in  std_logic;
    dta_ready  : out std_logic;
    -- ETH Sender interface
    set_number : out unsigned(15 downto 0);
    pkt_number : out unsigned(15 downto 0);
    snd_start  : out std_logic;
    snd_ready  : in  std_logic;
 
    -- Data memory interface
    dmem_addr      : out std_logic_vector(13 downto 0);
    dmem_dta       : out std_logic_vector(31 downto 0);
    dmem_we        : out std_logic;
    -- Interface to the ACK FIFO
    ack_fifo_empty : in  std_logic;
    ack_fifo_rd_en : out std_logic;
    ack_fifo_dout  : in  std_logic_vector(pkt_ack_width-1 downto 0);
 
    --
    transmit_data : in  std_logic;
    transm_delay  : out unsigned(31 downto 0);
 
    --
    clk   : in std_logic;
    rst_n : in std_logic);
 
end desc_manager;
 
architecture dmgr_a1 of desc_manager is
 
  constant PKT_CNT_MAX : integer := 3000;
 
  -- To simplify description of state machines, all registers are grouped
  -- in a record:
 
  type T_DESC_MGR_REGS is record
    set            : integer range 0 to N_OF_SETS-1;
    cur_set        : integer range 0 to N_OF_SETS-1;
    all_pkt_count  : integer range 0 to PKT_CNT_MAX;
    retr_pkt_count : integer range 0 to PKT_CNT_MAX;
    retr_delay     : unsigned(31 downto 0);
    transm_delay   : unsigned(31 downto 0);
    nxt            : integer range 0 to N_OF_PKTS-1;
    tail_ptr       : integer range 0 to N_OF_PKTS-1;
    head_ptr       : integer range 0 to N_OF_PKTS-1;
    retr_ptr       : integer range 0 to N_OF_PKTS-1;  -- buffer, which is retransmitted
                                        -- when equal to head_ptr -
                                        -- retransmission is finished
    retr_nxt       : integer range 0 to N_OF_PKTS-1;  -- buffer, which will be
                                                      -- retransmitted next
                                                      -- when equal to head_ptr -- no retransmission
                                                      -- is performed
  end record;
 
  constant DESC_MGR_REGS_INI : T_DESC_MGR_REGS := (
    retr_delay     => (others => '0'),
    transm_delay   => to_unsigned(10000, 32),
    all_pkt_count  => 0,
    retr_pkt_count => 0,
    set            => 0,
    cur_set        => 0,
    nxt            => 0,
    tail_ptr       => 0,
    head_ptr       => 0,
    retr_ptr       => 0,
    retr_nxt       => 0
    );
 
  -- To simplify setting of outputs of my Mealy state machine, all combinatorial
  -- outputs are grouped in a record
  type T_DESC_MGR_COMB is record
    dta_buf_free : std_logic;
    desc_addr    : integer range 0 to N_OF_PKTS-1;
    desc_we      : std_logic;
    ack_rd       : std_logic;
    snd_start    : std_logic;
    desc_out     : T_PKT_DESC;
  end record;
 
  constant DESC_MGR_COMB_DEFAULT : T_DESC_MGR_COMB := (
    dta_buf_free => '0',
    desc_addr    => 0,
    desc_we      => '0',
    ack_rd       => '0',
    snd_start    => '0',
    desc_out     => (confirmed => '0', valid => '0', sent => '0', set => 0)
    );
 
  type T_DESC_MGR_STATE is (ST_DMGR_IDLE, ST_DMGR_START, ST_DMGR_RST, ST_DMGR_RST1,
                            ST_DMGR_ACK1, ST_DMGR_INS1, ST_DMGR_INS2, ST_DMGR_ACK_TAIL,
                            ST_DMGR_ACK_TAIL_1,
                            ST_DMGR_RETR, ST_DMGR_RETR_2);
 
  signal desc_in : T_PKT_DESC;
 
  signal r, r_i                      : T_DESC_MGR_REGS  := DESC_MGR_REGS_INI;
  signal c                           : T_DESC_MGR_COMB;
  signal dmgr_state, dmgr_state_next : T_DESC_MGR_STATE := ST_DMGR_RST;
  attribute keep                     : string;
  attribute keep of dmgr_state       : signal is "true";
 
  signal dta_buf_full : std_logic := '0';
 
  signal ack_pkt_in : pkt_ack;
 
  signal wrd_addr : integer range 0 to 255;
 
  component desc_memory
    port (
      clk       : in  std_logic;
      desc_we   : in  std_logic;
      desc_addr : in  integer range 0 to N_OF_PKTS-1;
      desc_out  : in  T_PKT_DESC;
      desc_in   : out T_PKT_DESC);
  end component;
 
 
begin  -- dmgr_a1
 
  transm_delay   <= r.transm_delay;
  set_number     <= to_unsigned(r.set, 16);
  pkt_number     <= to_unsigned(r.retr_ptr, 16);
  dta_ready      <= not dta_buf_full;
  snd_start      <= c.snd_start;
  ack_fifo_rd_en <= c.ack_rd;
 
  ack_pkt_in <= stlv_to_pkt_ack(ack_fifo_dout);
 
 
  -- Packet descriptors are stored in the desc_memory
 
  desc_memory_1 : desc_memory
    port map (
      clk       => clk,
      desc_we   => c.desc_we,
      desc_addr => c.desc_addr,
      desc_out  => c.desc_out,
      desc_in   => desc_in);
 
  -- Process used to fill the buffer memory with the data to be transmitted
  -- We simply write words to the memory buffer pointed by r.head_ptr
  -- When we write the last (0xff-th) word, we signal that the buffer
  -- is full. Only after reception of 
  dta_rcv : process (clk, rst_n)
  begin  -- process dta_rcv
    if rst_n = '0' then                 -- asynchronous reset (active low)
      wrd_addr     <= 0;
      dta_buf_full <= '0';
      dmem_we      <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
      dmem_we <= '0';
      -- if we signalled "data full", we are only waiting for
      -- dta_buf_free;
      if dta_buf_full = '1' then
        if c.dta_buf_free = '1' then
          dta_buf_full <= '0';
          wrd_addr     <= 0;
        end if;
      else
        -- if data write requested - write it
        if dta_we = '1' then
          dmem_addr <= std_logic_vector(to_unsigned(r.head_ptr, 6)) &
                       std_logic_vector(to_unsigned(wrd_addr, 8));
          dmem_we  <= '1';
          dmem_dta <= dta;
          if wrd_addr < 255 then
            wrd_addr <= wrd_addr + 1;
          else
            dta_buf_full <= '1';
          end if;
        end if;
      end if;
    end if;
  end process dta_rcv;
 
 
  c1 : process (ack_fifo_empty, ack_pkt_in, desc_in, dmgr_state, dta_buf_full,
                r, snd_ready)
  begin  -- process c1
    c   <= DESC_MGR_COMB_DEFAULT;       -- set defaults
    r_i <= r;                           -- avoid latches
 
    if r.retr_delay /= to_unsigned(0, r.retr_delay'length) then
      r_i.retr_delay <= r.retr_delay-1;
    end if;
    dmgr_state_next <= dmgr_state;
    -- State machine
    case dmgr_state is
      when ST_DMGR_RST =>
        dmgr_state_next <= ST_DMGR_RST1;
      when ST_DMGR_RST1 =>
        -- We should initialize the 0th position of list descriptors
        c.desc_addr          <= r.head_ptr;
        c.desc_out           <= desc_in;
        c.desc_out.confirmed <= '0';
        c.desc_out.valid     <= '0';
        c.desc_out.sent      <= '0';
        c.desc_out.set       <= 0;
        c.desc_we            <= '1';
        dmgr_state_next      <= ST_DMGR_IDLE;
      when ST_DMGR_IDLE =>
        -- First we check, if there are any packets to acknowledge
        if ack_fifo_empty = '0' then
          -- Read the description of the acknowledged packet
          c.desc_addr     <= to_integer(ack_pkt_in.pkt);
          dmgr_state_next <= ST_DMGR_ACK1;
        elsif dta_buf_full = '1' then
          -- We should handle reception of data.
          -- If the previously filled buffer is full, pass it for transmission,
          -- and allocate the next one.
          --
          -- Calculate the number of the packet, which shoud be the next "head"
          -- packet.
          if r.head_ptr = N_OF_PKTS-1 then
            r_i.nxt <= 0;
          else
            r_i.nxt <= r.head_ptr + 1;
          end if;
          -- Prepare for reading of the current "head" descriptor
          c.desc_addr     <= r.head_ptr;
          dmgr_state_next <= ST_DMGR_INS1;
        elsif (r.tail_ptr /= r.head_ptr) and (r.retr_delay = to_unsigned(0, r.retr_delay'length)) then
          -- We need to (re)transmit some buffers
          -- prepare reading of the descriptor, which should be transmitted
          c.desc_addr     <= r.retr_nxt;
          dmgr_state_next <= ST_DMGR_RETR;
        end if;
      when ST_DMGR_INS1 =>
        -- First we check, if there is free space, r.nxt is the number of the
        -- future head packet.
        if (r.nxt = r.tail_ptr) then
          -- No free place! The packet, which we would like to fill is still
          -- occupied.
          -- Return to idle, waiting until something is freed.
          -- In this case we should also force retransmission
          if r.retr_delay = 0 then
            c.desc_addr     <= r.retr_nxt;
            dmgr_state_next <= ST_DMGR_RETR;
          else
            dmgr_state_next <= ST_DMGR_IDLE;
          end if;
        else
          -- We can fill the next buffer
          -- First we mark the previous head packet
          -- as valid and not confirmed
          c.desc_addr          <= r.head_ptr;
          c.desc_out           <= desc_in;
          c.desc_out.confirmed <= '0';
          c.desc_out.valid     <= '1';
          c.desc_we            <= '1';
          -- Now we move the "head" pointer
          r_i.head_ptr         <= r.nxt;
          -- Increase the set number if we wrapped around
          if r.nxt = 0 then
            if r.cur_set = N_OF_SETS-1 then
              r_i.cur_set <= 0;
            else
              r_i.cur_set <= r.cur_set + 1;
            end if;
          end if;
          dmgr_state_next <= ST_DMGR_INS2;
        end if;
      when ST_DMGR_INS2 =>
        -- We fill the new head descriptor
        c.desc_addr          <= r.head_ptr;
        c.desc_out.set       <= r.cur_set;
        c.desc_out.confirmed <= '0';
        c.desc_out.valid     <= '0';
        c.desc_out.sent      <= '0';
        c.desc_we            <= '1';
        -- Signal, that the buffer is freed
        c.dta_buf_free       <= '1';
        dmgr_state_next      <= ST_DMGR_IDLE;
      when ST_DMGR_ACK1 =>
        -- In this state the desc memory should respond with the data of the
        -- buffered packet, so we can state, if this packet is really correctly
        -- acknowledged (here we also ignore the NACK packets!
        if (ack_pkt_in.set = desc_in.set) and
          (ack_pkt_in.cmd = to_unsigned(3,ack_pkt_in.cmd'length)) and 
          (desc_in.valid = '1') then
          -- This is really correct, unconfirmed packet
          -- Increase the counter of not-repeated ACK packets
          -- Write the confirmation
          c.desc_addr          <= to_integer(ack_pkt_in.pkt);
          c.desc_out           <= desc_in;
          c.desc_out.valid     <= '0';
          c.desc_out.confirmed <= '1';
          c.desc_we            <= '1';
          -- Here we also handle the case, if the acknowledged packet was
          -- the one which is now scheduled for retransmission...
          if ack_pkt_in.pkt = r.retr_nxt then
            if r.retr_nxt < N_OF_PKTS-1 then
              r_i.retr_nxt <= r.retr_nxt + 1;
            else
              r_i.retr_nxt <= 0;
            end if;
          end if;
          -- Check, if we need to update the "tail" pointer
          if r.tail_ptr = ack_pkt_in.pkt then
            c.ack_rd        <= '1';
            dmgr_state_next <= ST_DMGR_ACK_TAIL;
          else
            c.ack_rd        <= '1';
            dmgr_state_next <= ST_DMGR_IDLE;
          end if;
        else
          -- This packet was already confirmed or it was NACK
          -- just flush the ack_fifo
          c.ack_rd        <= '1';
          dmgr_state_next <= ST_DMGR_IDLE;
        end if;
      when ST_DMGR_ACK_TAIL =>
        c.desc_addr     <= r.tail_ptr;
        dmgr_state_next <= ST_DMGR_ACK_TAIL_1;
      when ST_DMGR_ACK_TAIL_1 =>
        -- In this state we update the "tail" pointer if necessary
        if r.tail_ptr /= r.head_ptr then
          if desc_in.confirmed = '1' then
            if r.tail_ptr < N_OF_PKTS-1 then
              r_i.tail_ptr <= r.tail_ptr + 1;
              c.desc_addr  <= r.tail_ptr + 1;
            else
              r_i.tail_ptr <= 0;
              c.desc_addr  <= 0;
            end if;
            -- We remain in that state, to check the next packet descriptor
          else
            -- We return to idle
            dmgr_state_next <= ST_DMGR_IDLE;
          end if;
        else
          -- Buffer is empty - return to idle
          dmgr_state_next <= ST_DMGR_IDLE;
        end if;
      when ST_DMGR_RETR =>
        -- Here we handle the transmission of a new packet, 
        -- retransmission of not confirmed packet
        -- We must be sure, that the transmitter is ready
        if snd_ready = '0' then
          -- transmitter not ready, return to idle
          dmgr_state_next <= ST_DMGR_IDLE;
        else
          -- We will be able to send the next packet, but let's check if
          -- this is not the currently filled packet
          if r.retr_nxt = r.head_ptr then
            -- All packets (re)transmitted, go to the begining of the list
            -- and return to idle.
            r_i.retr_nxt    <= r.tail_ptr;
            dmgr_state_next <= ST_DMGR_IDLE;
          else
            -- before jumping to ST_DMGR_RETR, the address bus
            -- was set to the address of r.retr_nxt, so now
            -- we can read the descriptor, and check if the packet
            -- needs to be retransmitted at all...
            r_i.set      <= desc_in.set;
            r_i.retr_ptr <= r.retr_nxt;
            if r.retr_nxt < N_OF_PKTS-1 then
              r_i.retr_nxt <= r.retr_nxt + 1;
            else
              r_i.retr_nxt <= 0;
            end if;
            if desc_in.valid = '1' and desc_in.confirmed = '0' then
              if desc_in.sent = '1' then
                -- Increase count of retransmitted packets for
                -- adaptive adjustment of delay
                if r.retr_pkt_count < PKT_CNT_MAX then
                  r_i.retr_pkt_count <= r.retr_pkt_count + 1;
                end if;
              end if;
              -- Increase count of all packets for adaptive adjustment
              -- of delay
              if r.all_pkt_count < PKT_CNT_MAX then
                r_i.all_pkt_count <= r.all_pkt_count + 1;
              end if;
              -- Mark the packet as sent
              c.desc_addr     <= r.retr_nxt;
              c.desc_out      <= desc_in;
              c.desc_out.sent <= '1';
              c.desc_we       <= '1';
              dmgr_state_next <= ST_DMGR_RETR_2;
            else
              dmgr_state_next <= ST_DMGR_IDLE;
            end if;
          end if;
        end if;
      when ST_DMGR_RETR_2 =>
        -- In this state, we simply trigger the sender!
        c.snd_start    <= '1';
        r_i.retr_delay <= r.transm_delay;
        -- And we update the delay using the packet statistics
        -- You may change the constants used in expressions
        -- below to change speed of adjustment
        if r.all_pkt_count >= PKT_CNT_MAX then
          if r.retr_pkt_count < PKT_CNT_MAX/32 then
            if r.transm_delay > 16 then
              r_i.transm_delay <= r.transm_delay-r.transm_delay/16;
            end if;
          elsif r.retr_pkt_count > PKT_CNT_MAX/8 then
            if r.transm_delay < 1000000 then
              r_i.transm_delay <= r.transm_delay+r.transm_delay/4;
            end if;
          end if;
          r_i.all_pkt_count  <= 0;
          r_i.retr_pkt_count <= 0;
        end if;
        dmgr_state_next <= ST_DMGR_IDLE;
      when others => null;
    end case;
  end process c1;
 
-- Synchronous process
  process (clk, rst_n)
  begin  -- process
    if rst_n = '0' then                 -- asynchronous reset (active low)
      r          <= DESC_MGR_REGS_INI;
      dmgr_state <= ST_DMGR_RST;
    elsif clk'event and clk = '1' then  -- rising clock edge
      r          <= r_i;
      dmgr_state <= dmgr_state_next;
    end if;
  end process;
 
-- Process debugging the descriptors memory - for simulation only!
--  process (clk, rst_n)
--    variable L : line;
--  begin  -- process
--    if rst_n = '0' then                 -- asynchronous reset (active low)
--      null;
--    elsif clk'event and clk = '1' then  -- rising clock edge
--      if c.desc_we = '1' then
--        write(L, string'("nr="));
--        write(L, c.desc_addr);
--        write(L, string'(" set="));
--        write(L, c.desc_out.set);
--        write(L, string'(" valid="));
--        --write(L,c.desc_out.valid);
--        if c.desc_out.valid = '1' then
--          write(L, string'("1"));
--        else
--          write(L, string'("0"));
--        end if;
--        write(L, string'(" confirmed="));
--        --write(L,c.desc_out.valid);
--        if c.desc_out.confirmed = '1' then
--          write(L, string'("1"));
--        else
--          write(L, string'("0"));
--        end if;
--        write(L, string'(" r.tail="));
--        write(L, r.tail_ptr);
--        write(L, string'(" r.head="));
--        write(L, r.head_ptr);
--        writeline(output, L);
--      end if;
--    end if;
--  end process;
 
end dmgr_a1;
 
 
 
 

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.