ArtyEthernet

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in April 2016

This project broadcasts UDP packets from Arty's Ethernet port (connected by MII), as fast as it can. Each packet has 16 bytes of user data, and the design sends about 130,000 packets per second.

Arty ethernet.png

With the larger packet size (that is in the GitHub repo), the link bandwidth is almost 100Mb/s: Arty ethernet performance.png

Contents

Peak data bandwidth

Modified to use full 1518 byte frames, the performance would be as follows:

Due to the 12-byte inter-packet gap, the peak packets per second = 100M / 8 /(1518+12) = 8169.9 packets per second.

Of the 1518 bytes, 18 are lost to the Ethernet Frame, 20 lost to the IP header and 8 to the UDP header, leaving 1472 bytes of user data.

Giving a maximum throughput of 12,026,092 bytes per second, or 96.2% of the raw bandwidth.

Source

I've now put the source in a GitHub repo - https://github.com/hamsternz/ArtyEtherentTX

ethernet_test.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz> 
-- 
-- Module Name: ethernet_test - Behavioral
--
-- Description: Sending UDP packets over Arty's Ethernet PHY 
-- 
-- Datasheet is available from http://www.ti.com.cn/cn/lit/ds/symlink/dp83848j.pdf
-- 
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

Library UNISIM;
use UNISIM.vcomponents.all;

entity ethernet_test is
    Port ( CLK100MHZ   : in    STD_LOGIC;
           -- control channel
           eth_mdio    : inout STD_LOGIC := '0';
           eth_mdc     : out   STD_LOGIC := '0';
           eth_rstn    : out   STD_LOGIC := '1';
           -- tx channel
           eth_tx_d    : out STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
           eth_tx_en   : out STD_LOGIC  := '0';
           eth_tx_clk  : in  STD_LOGIC;
           -- rx channel
           eth_rx_d    : in  STD_LOGIC_VECTOR(3 downto 0);
           eth_rx_err  : in  STD_LOGIC;
           eth_rx_dv   : in  STD_LOGIC;
           eth_rx_clk  : in  STD_LOGIC; 
           -- link status
           eth_col     : in  STD_LOGIC;
           eth_crs     : in  STD_LOGIC;
           -- reference clock
           eth_ref_clk : out STD_LOGIC);
end ethernet_test;

architecture Behavioral of ethernet_test is
    signal reset_counter : unsigned(24 downto 0)         := (others => '0');
    signal debug         : STD_LOGIC_VECTOR (5 downto 0) := (others => '0');
    signal phy_ready     : std_logic                     := '0';
    signal tx_ready_meta : std_logic                     := '0';
    signal tx_ready      : std_logic                     := '0';
    signal ok_to_send    : std_logic                     := '0';
    signal user_data     : std_logic                     := '0';

    component nibble_data is
        Port ( clk        : in STD_LOGIC;
               start      : in  STD_LOGIC;
               busy       : out STD_LOGIC;
               data       : out STD_LOGIC_VECTOR (3 downto 0);
               user_data  : out STD_LOGIC;
               data_valid : out STD_LOGIC);
    end component;

    signal nibble        : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
    signal nibble_valid  : std_logic                     := '0';

    component add_crc32 is
        Port ( clk             : in  STD_LOGIC;
               data_in         : in  STD_LOGIC_VECTOR (3 downto 0);
               data_enable_in  : in  STD_LOGIC;
               data_out        : out STD_LOGIC_VECTOR (3 downto 0);
               data_enable_out : out STD_LOGIC);
    end component;

    signal with_crc        : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
    signal with_crc_valid  : std_logic                     := '0';
    
    component add_preamble is
        Port ( clk             : in  STD_LOGIC;
               data_in         : in  STD_LOGIC_VECTOR (3 downto 0);
               data_enable_in  : in  STD_LOGIC;
               data_out        : out STD_LOGIC_VECTOR (3 downto 0);
               data_enable_out : out STD_LOGIC);
    end component;

    signal fully_framed        : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
    signal fully_framed_valid  : std_logic                     := '0';

    --------------------------------
    -- Clocking signals 
    -------------------------------- 
    signal clk50MHz : std_logic;
    signal clk25MHz : std_logic;
    signal clkfb    : std_logic;
begin
   ---------------------------------------------------
   -- Strapping signals
   ----------------------------------------------------
   -- No pullups/pulldowns added
 
   ----------------------------------------------------
   -- Data for the packet packet 
   ----------------------------------------------------
data: nibble_data port map ( 
      clk        => eth_tx_clk,
      start      => '1',
      busy       => open,
      data       => nibble,
      user_data  => user_data,
      Data_valid => nibble_valid);

i_add_crc32: add_crc32 port map (
      clk             => eth_tx_clk,
      data_in         => nibble,
      data_enable_in  => nibble_valid,
      data_out        => with_crc,
      data_enable_out => with_crc_valid);

i_add_preamble: add_preamble port map (
      clk             => eth_tx_clk,
      data_in         => with_crc,
      data_enable_in  => with_crc_valid,
      data_out        => fully_framed,
      data_enable_out => fully_framed_valid);
      
   ----------------------------------------------------
   -- Send the data out to the ethernet PHY
   -- But only when it is OK to send after the
   -- PHY has been out of reset for long enough 
   ----------------------------------------------------
send_data_out: process(eth_tx_clk)
    begin
       if falling_edge(eth_tx_clk) then
           eth_tx_d    <= fully_framed;
           eth_tx_en   <= fully_framed_valid and ok_to_send;
       end if;
    end process;
    
monitor_reset_state: process(eth_tx_clk)
    begin
       if rising_edge(eth_tx_clk) then
          tx_ready      <= tx_ready_meta;
          tx_ready_meta <= phy_ready;
          if tx_ready = '1' and fully_framed_valid = '0' then
             ok_to_send    <= '1';
          end if;
       end if;
    end process;

    ----------------------------------------
    -- Control reseting the PHY
    ----------------------------------------
control_reset: process(clk25MHz)
    begin
       if rising_edge(clk25MHz) then           
          if reset_counter(reset_counter'high) = '0' then
              reset_counter <= reset_counter + 1;
          end if; 
          eth_rstn  <= reset_counter(reset_counter'high) or reset_counter(reset_counter'high-1);
          phy_ready <= reset_counter(reset_counter'high);
       end if;
    end process;
    
   ----------------------------------------------------
   -- Correctly forward the clock out,so rising edge 
   -- will be in the middle of the valid data 
   ----------------------------------------------------
clock_fwd_ddr : ODDR
   generic map(
      DDR_CLK_EDGE => "SAME_EDGE", 
      INIT         => '0',
      SRTYPE       => "SYNC")
   port map (
      Q  => eth_ref_clk,
      C  => clk25MHz,
      CE => '1', R  => '0', S  => '0',
      D1 => '0', D2 => '1'
   );

   -------------------------------------------------------
   -- Generate a 25MHz and 50Mhz clocks from the 100MHz 
   -- system clock 
   ------------------------------------------------------- 
clocking : PLLE2_BASE
   generic map (
      BANDWIDTH          => "OPTIMIZED",
      CLKFBOUT_MULT      => 8,
      CLKFBOUT_PHASE     => 0.0,
      CLKIN1_PERIOD      => 10.0,

      -- CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: Divide amount for each CLKOUT (1-128)
      CLKOUT0_DIVIDE     => 32,  CLKOUT1_DIVIDE     => 16, CLKOUT2_DIVIDE      => 16, 
      CLKOUT3_DIVIDE     => 16,  CLKOUT4_DIVIDE     => 16, CLKOUT5_DIVIDE      => 16,

      -- CLKOUT0_DUTY_CYCLE - CLKOUT5_DUTY_CYCLE: Duty cycle for each CLKOUT (0.001-0.999).
      CLKOUT0_DUTY_CYCLE => 0.5, CLKOUT1_DUTY_CYCLE => 0.5, CLKOUT2_DUTY_CYCLE => 0.5,
      CLKOUT3_DUTY_CYCLE => 0.5, CLKOUT4_DUTY_CYCLE => 0.5, CLKOUT5_DUTY_CYCLE => 0.5,

      -- CLKOUT0_PHASE - CLKOUT5_PHASE: Phase offset for each CLKOUT (-360.000-360.000).
      CLKOUT0_PHASE      => 0.0, CLKOUT1_PHASE      => 0.0, CLKOUT2_PHASE      => 0.0,
      CLKOUT3_PHASE      => 0.0, CLKOUT4_PHASE      => 0.0, CLKOUT5_PHASE      => 0.0,

      DIVCLK_DIVIDE      => 1,
      REF_JITTER1        => 0.0,
      STARTUP_WAIT       => "FALSE"
   )
   port map (
      CLKIN1   => CLK100MHz,
      CLKOUT0 => CLK25MHz, CLKOUT1 => CLK50Mhz, 
      CLKOUT2 => open,     CLKOUT3  => open,
      CLKOUT4 => open,     CLKOUT5 => open,
      LOCKED   => open,
      PWRDWN   => '0', 
      RST      => '0',
      CLKFBOUT => clkfb,
      CLKFBIN  => clkfb
   );
end Behavioral;

nibble_data.vhd

I agree that this is fugly, but it is supposed to be instructive and explicit, rather than optimal and concise...

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Description: Data for sending an empty UDP packet out over the MII interface.
--              "user_data" is asserted where you should replace 'nibble' with 
--              data that you wish to send.
----------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity nibble_data is
    Port ( clk        : in  STD_LOGIC;
           start      : in  STD_LOGIC;
           busy       : out STD_LOGIC;
           data       : out STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
           user_data  : out STD_LOGIC                     := '0';
           data_valid : out STD_LOGIC                     := '0');
end nibble_data;

architecture Behavioral of nibble_data is
    constant ip_header_bytes   : integer := 20;
    constant udp_header_bytes  : integer := 8;
    constant data_bytes        : integer := 16;
    constant ip_total_bytes    : integer := ip_header_bytes + udp_header_bytes + data_bytes;
    constant udp_total_bytes   : integer := udp_header_bytes + data_bytes;

    signal counter : unsigned(11 downto 0) := (others => '0');
    
    
    -- Ethernet frame header 
    signal eth_src_mac       : std_logic_vector(47 downto 0) := x"DEADBEEF0123";
    signal eth_dst_mac       : std_logic_vector(47 downto 0) := x"FFFFFFFFFFFF";
    signal eth_type          : std_logic_vector(15 downto 0) := x"0800";

    -- IP header
    signal ip_version        : std_logic_vector( 3 downto 0) := x"4";
    signal ip_header_len     : std_logic_vector( 3 downto 0) := x"5";
    signal ip_dscp_ecn       : std_logic_vector( 7 downto 0) := x"00";
    signal ip_identification : std_logic_vector(15 downto 0) := x"0000";     -- Checksum is optional
    signal ip_length         : std_logic_vector(15 downto 0) := std_logic_vector(to_unsigned(ip_total_bytes, 16));
    signal ip_flags_and_frag : std_logic_vector(15 downto 0) := x"0000";     -- no flags48 bytes
    signal ip_ttl            : std_logic_vector( 7 downto 0)  := x"80";
    signal ip_protocol       : std_logic_vector( 7 downto 0)  := x"11";
    signal ip_checksum       : std_logic_vector(15 downto 0) := x"0000";   -- Calcuated later on
    signal ip_src_addr       : std_logic_vector(31 downto 0) := x"C0A40140"; -- 192.168.1.64
    signal ip_dst_addr       : std_logic_vector(31 downto 0) := x"FFFFFFFF"; -- 255.255.255.255
    -- for calculating the checksum 
    signal ip_checksum1     : unsigned(31 downto 0) := (others => '0');
    signal ip_checksum2     : unsigned(15 downto 0) := (others => '0');
    
    -- UDP Header
    signal udp_src_port      : std_logic_vector(15 downto 0) := x"1000";     -- port 4096
    signal udp_dst_port      : std_logic_vector(15 downto 0) := x"1000";     -- port 4096
    signal udp_length        : std_logic_vector(15 downto 0) := std_logic_vector(to_unsigned(udp_total_bytes, 16)); 
    signal udp_checksum      : std_logic_vector(15 downto 0) := x"0000";     -- Checksum is optional, and if presentincludes the data
begin
   ---------------------------------------------
   -- Calutate the TCP checksum using logic
   -- This should all colapse down to a constant
   -- at build-time (example #s found on the web)
   ----------------------------------------------
   --- Step 1) 4500 + 0030 + 4422 + 4000 + 8006 + 0000 + (0410 + 8A0C + FFFF + FFFF) = 0002BBCF (32-bit sum)
   ip_checksum1 <= to_unsigned(0,32) 
                 + unsigned(ip_version & ip_header_len & ip_dscp_ecn)
                 + unsigned(ip_identification)
                 + unsigned(ip_length)
                 + unsigned(ip_flags_and_frag)
                 + unsigned(ip_ttl & ip_protocol)
                 + unsigned(ip_src_addr(31 downto 16))
                 + unsigned(ip_src_addr(15 downto  0))
                 + unsigned(ip_dst_addr(31 downto 16))
                 + unsigned(ip_dst_addr(15 downto  0));
   -- Step 2) 0002 + BBCF = BBD1 = 1011101111010001 (1's complement 16-bit sum, formed by "end around carry" of 32-bit 2's complement sum)
   ip_checksum2 <= ip_checksum1(31 downto 16) + ip_checksum1(15 downto 0);
   -- Step 3) ~BBD1 = 0100010000101110 = 442E (1's complement of 1's complement 16-bit sum)
   ip_checksum  <= NOT std_logic_vector(ip_checksum2);

generate_nibbles: process (clk) 
    begin
        if rising_edge(clk) then
            -- Update the counter of where we are 
            -- in the packet
            if counter /= 0 or start = '1' then
               counter <= counter + 1;
            end if;
            
            -- Note, this uses the current value of counter, not the one assigned above!
            data <= "0000";
            case counter is 
              -- We pause at 0 count when idle (see below case statement)
              when x"000" => NULL;
              -----------------------------
              -- MAC Header 
              -----------------------------
              -- Ethernet destination
              when x"001" => data <= eth_dst_mac(43 downto 40); data_valid <= '1';
              when x"002" => data <= eth_dst_mac(47 downto 44);
              when x"003" => data <= eth_dst_mac(35 downto 32);
              when x"004" => data <= eth_dst_mac(39 downto 36);
              when x"005" => data <= eth_dst_mac(27 downto 24);
              when x"006" => data <= eth_dst_mac(31 downto 28);
              when x"007" => data <= eth_dst_mac(19 downto 16);
              when x"008" => data <= eth_dst_mac(23 downto 20);
              when x"009" => data <= eth_dst_mac(11 downto  8);
              when x"00A" => data <= eth_dst_mac(15 downto 12);
              when x"00B" => data <= eth_dst_mac( 3 downto  0);
              when x"00C" => data <= eth_dst_mac( 7 downto  4);
              -- Ethernet source
              when x"00D" => data <= eth_src_mac(43 downto 40);
              when x"00E" => data <= eth_src_mac(47 downto 44);
              when x"00F" => data <= eth_src_mac(35 downto 32);
              when x"010" => data <= eth_src_mac(39 downto 36);
              when x"011" => data <= eth_src_mac(27 downto 24);
              when x"012" => data <= eth_src_mac(31 downto 28);
              when x"013" => data <= eth_src_mac(19 downto 16);
              when x"014" => data <= eth_src_mac(23 downto 20);
              when x"015" => data <= eth_src_mac(11 downto  8);
              when x"016" => data <= eth_src_mac(15 downto 12);
              when x"017" => data <= eth_src_mac( 3 downto  0);
              when x"018" => data <= eth_src_mac( 7 downto  4);
              -- Ether Type 08:00
              when x"019" => data <= eth_type(11 downto  8);
              when x"01A" => data <= eth_type(15 downto 12); 
              when x"01B" => data <= eth_type( 3 downto  0);
              when x"01C" => data <= eth_type( 7 downto  4);
              -------------------------
              -- User data packet
              ------------------------------
              -- IPv4 Header
              ----------------------------
              when x"01D" => data <= ip_header_len;
              when x"01E" => data <= ip_version;
              
              when x"01F" => data <= ip_dscp_ecn( 3 downto  0);
              when x"020" => data <= ip_dscp_ecn( 7 downto  4);
              -- Length of total packet (excludes etherent header and ethernet FCS) = 0x0030
              when x"021" => data <= ip_length(11 downto  8);
              when x"022" => data <= ip_length(15 downto 12);
              when x"023" => data <= ip_length( 3 downto  0);
              when x"024" => data <= ip_length( 7 downto  4);
              -- all zeros
              when x"025" => data <= ip_identification(11 downto  8);
              when x"026" => data <= ip_identification(15 downto 12);
              when x"027" => data <= ip_identification( 3 downto  0);
              when x"028" => data <= ip_identification( 7 downto  4);
              -- No flags, no frament offset.
              when x"029" => data <= ip_flags_and_frag(11 downto  8);
              when x"02A" => data <= ip_flags_and_frag(15 downto 12);
              when x"02B" => data <= ip_flags_and_frag( 3 downto  0);
              when x"02C" => data <= ip_flags_and_frag( 7 downto  4);
              -- Time to live
              when x"02D" => data <= ip_ttl( 3 downto  0);
              when x"02E" => data <= ip_ttl( 7 downto  4);
              -- Protocol (UDP)
              when x"02F" => data <= ip_protocol( 3 downto  0);
              when x"030" => data <= ip_protocol( 7 downto  4);
              -- Header checksum
              when x"031" => data <= ip_checksum(11 downto  8);
              when x"032" => data <= ip_checksum(15 downto 12);
              when x"033" => data <= ip_checksum( 3 downto  0);
              when x"034" => data <= ip_checksum( 7 downto  4);
              -- source address
              when x"035" => data <= ip_src_addr(27 downto 24);
              when x"036" => data <= ip_src_addr(31 downto 28);
              when x"037" => data <= ip_src_addr(19 downto 16);
              when x"038" => data <= ip_src_addr(23 downto 20);
              when x"039" => data <= ip_src_addr(11 downto  8);
              when x"03A" => data <= ip_src_addr(15 downto 12);
              when x"03B" => data <= ip_src_addr( 3 downto  0);
              when x"03C" => data <= ip_src_addr( 7 downto  4);
              -- dest address
              when x"03D" => data <= ip_dst_addr(27 downto 24);
              when x"03E" => data <= ip_dst_addr(31 downto 28);
              when x"03F" => data <= ip_dst_addr(19 downto 16);
              when x"040" => data <= ip_dst_addr(23 downto 20);
              when x"041" => data <= ip_dst_addr(11 downto  8);
              when x"042" => data <= ip_dst_addr(15 downto 12);
              when x"043" => data <= ip_dst_addr( 3 downto  0);
              when x"044" => data <= ip_dst_addr( 7 downto  4);
              -- No options in this packet
              
              ------------------------------------------------
              -- UDP/IP Header - from port 4096 to port 4096
              ------------------------------------------------
              -- Source port 4096
              when x"045" => data <= udp_src_port(11 downto  8);
              when x"046" => data <= udp_src_port(15 downto 12);
              when x"047" => data <= udp_src_port( 3 downto  0);
              when x"048" => data <= udp_src_port( 7 downto  4);
              -- Target port 4096
              when x"049" => data <= udp_dst_port(11 downto  8);
              when x"04A" => data <= udp_dst_port(15 downto 12);
              when x"04B" => data <= udp_dst_port( 3 downto  0);
              when x"04C" => data <= udp_dst_port( 7 downto  4);
              -- UDP Length (header + data) 24 octets
              when x"04D" => data <= udp_length(11 downto  8);
              when x"04E" => data <= udp_length(15 downto 12);
              when x"04F" => data <= udp_length( 3 downto  0);
              when x"050" => data <= udp_length( 7 downto  4);
              -- UDP Checksum not suppled
              when x"051" => data <= udp_checksum(11 downto  8);
              when x"052" => data <= udp_checksum(15 downto 12);
              when x"053" => data <= udp_checksum( 3 downto  0);
              when x"054" => data <= udp_checksum( 7 downto  4);
              --------------------------------------------
              -- Finally! 16 bytes of user data (defaults 
              -- to "0000" due to assignement above CASE).
              ---------------------------------------------
              when x"055" => user_data <= '1';
              when x"056" => NULL; 
              when x"057" => NULL; 
              when x"058" => NULL; 
              when x"059" => NULL; 
              when x"05A" => NULL; 
              when x"05B" => NULL; 
              when x"05C" => NULL; 
              when x"05D" => NULL; 
              when x"05E" => NULL; 
              when x"05F" => NULL; 
              when x"060" => NULL; 
              when x"061" => NULL; 
              when x"062" => NULL; 
              when x"063" => NULL; 
              when x"064" => NULL; 
              when x"065" => NULL; 
              when x"066" => NULL; 
              when x"067" => NULL; 
              when x"068" => NULL; 
              when x"069" => NULL; 
              when x"06A" => NULL; 
              when x"06B" => NULL; 
              when x"06C" => NULL; 
              when x"06D" => NULL; 
              when x"06E" => NULL; 
              when x"06F" => NULL; 
              when x"070" => NULL; 
              when x"071" => NULL; 
              when x"072" => NULL; 
              when x"073" => NULL; 
              when x"074" => NULL; 
              --------------------------------------------
              -- Ethernet Frame Check Sequence (CRC) will 
              -- be added here, overwriting these nibbles
              --------------------------------------------
              when x"075" => data_valid <= '0'; user_data <= '0';
              when x"076" => NULL;
              when x"077" => NULL;
              when x"078" => NULL;
              when x"079" => NULL;
              when x"07A" => NULL;
              when x"07B" => NULL;
              when x"07C" => NULL;
              ----------------------------------------------------------------------------------
              -- End of frame - there needs to be at least 28 octets (56 counts) before  sending 
              -- the next packet, (maybe more depending  on medium?) 12 are for the inter packet
              -- gap, 16 allow for the preamble that will be added to the start of this packet.
              --
              -- Note that when the count of 0000 adds one  more nibble, so if start is assigned 
              -- '1' this should be minimum that is  within spec.
              ----------------------------------------------------------------------------------
              when x"0A3" => counter <= (others => '0'); busy  <= '0';
              when others => data <= "0000";
            end case;
         end if;    
    end process;
end Behavioral;

add_crc32.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz> 
-- 
-- Module Name: add_crc32 - Behavioral
--
-- Description: Add the required 16 nibbles of preamble to the data packet. 
-- 
----------------------------------------------------------------------------------


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity add_crc32 is
    Port ( clk             : in  STD_LOGIC;
           data_in         : in  STD_LOGIC_VECTOR (3 downto 0);
           data_enable_in  : in  STD_LOGIC;
           data_out        : out STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
           data_enable_out : out STD_LOGIC                     := '0');
end add_crc32;

architecture Behavioral of add_crc32 is
    signal crc               : std_logic_vector(31 downto 0)   := (others => '1');
    signal trailer_left      : std_logic_vector(7 downto 0) := (others => '0');
begin

add_crc_proc: process(clk)
        variable v_crc : std_logic_vector(31 downto 0) := (others => '1');
    begin
        if rising_edge(clk) then
            if data_enable_in = '1' then
                -- Pass the data through
                data_out        <= data_in;
                data_enable_out <= '1';
                -- Flag that we need to output 8 bytes of CRC
                trailer_left    <= (others => '1');
                
                ----------------------------------------
                -- Update the CRC
                --
                -- This uses a variable to make the code 
                -- simple to follow and more compact
                ---------------------------------------- 
                v_crc := crc;
                for i in 0 to 3 loop
                    if data_in(i) = v_crc(31) then
                       v_crc := v_crc(30 downto 0) & '0';
                    else
                       v_crc := (v_crc(30 downto 0)& '0') xor x"04C11DB7";
                    end if;
                end loop;
                crc <= v_crc; 
                
            elsif trailer_left(trailer_left'high)= '1' then
                -- append the CRC
                data_out        <= not (crc(28) & crc(29) & crc(30) & crc(31));
                crc             <= crc(27 downto 0) & "1111";
                trailer_left    <= trailer_left(trailer_left'high-1 downto 0) & '0';
                data_enable_out <= '1';        
            else
                -- Idle
                data_out        <= "0000"; 
                data_enable_out <= '0';                
            end if;
        end if;
    end process;
end Behavioral;

add_preamble.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz> 
-- 
-- Module Name: add_preamble - Behavioral
--
-- Description: Add the required 16 nibbles of preamble to the data packet. 
-- 
----------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity add_preamble is
    Port ( clk             : in  STD_LOGIC;
           data_in         : in  STD_LOGIC_VECTOR (3 downto 0);
           data_enable_in  : in  STD_LOGIC;
           data_out        : out STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
           data_enable_out : out STD_LOGIC                     := '0');
end add_preamble;

architecture Behavioral of add_preamble is
    signal delay_data        : std_logic_vector(16*4-1 downto 0) := (others => '0');
    signal delay_data_enable : std_logic_vector(16-1 downto 0) := (others => '0');
begin

process(clk)
    begin
        if rising_edge(clk) then
            if delay_data_enable(delay_data_enable'high)= '1' then
                -- Passing through data
                data_out        <= delay_data(delay_data'high downto delay_data'high-3);
                data_enable_out <= '1';        
            elsif delay_data_enable(delay_data_enable'high-1)= '1' then
                -- Start Frame Delimiter
                data_out        <= "1101"; 
                data_enable_out <= '1';
            elsif data_enable_in = '1' then
                -- Preamble nibbles
                data_out        <= "0101"; 
                data_enable_out <= '1';        
            else
                -- Link idle
                data_out        <= "0000"; 
                data_enable_out <= '0';                
            end if;
            -- Move the data through the delay line
            delay_data        <= delay_data(delay_data'high-4 downto 0) & data_in;  
            delay_data_enable <= delay_data_enable(delay_data_enable'high-1 downto 0) & data_enable_in;
        end if;
    end process;

end Behavioral;

arty.xdc

# Clock signal
set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { CLK100MHZ }]; #IO_L12P_T1_MRCC_35 Sch=gclk[100]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {CLK100MHZ}];


##SMSC Ethernet PHY
set_property -dict { PACKAGE_PIN D17   IOSTANDARD LVCMOS33 } [get_ports { eth_col     }];
set_property -dict { PACKAGE_PIN G14   IOSTANDARD LVCMOS33 } [get_ports { eth_crs     }];
set_property -dict { PACKAGE_PIN F16   IOSTANDARD LVCMOS33 } [get_ports { eth_mdc     }];
set_property -dict { PACKAGE_PIN K13   IOSTANDARD LVCMOS33 } [get_ports { eth_mdio    }];
set_property -dict { PACKAGE_PIN G18   IOSTANDARD LVCMOS33 } [get_ports { eth_ref_clk }];
set_property -dict { PACKAGE_PIN C16   IOSTANDARD LVCMOS33 } [get_ports { eth_rstn    }];
set_property -dict { PACKAGE_PIN F15   IOSTANDARD LVCMOS33 } [get_ports { eth_rx_clk  }];
set_property -dict { PACKAGE_PIN G16   IOSTANDARD LVCMOS33 } [get_ports { eth_rx_dv   }];
set_property -dict { PACKAGE_PIN D18   IOSTANDARD LVCMOS33 } [get_ports { eth_rx_d[0] }];
set_property -dict { PACKAGE_PIN E17   IOSTANDARD LVCMOS33 } [get_ports { eth_rx_d[1] }];
set_property -dict { PACKAGE_PIN E18   IOSTANDARD LVCMOS33 } [get_ports { eth_rx_d[2] }];
set_property -dict { PACKAGE_PIN G17   IOSTANDARD LVCMOS33 } [get_ports { eth_rx_d[3] }];
set_property -dict { PACKAGE_PIN C17   IOSTANDARD LVCMOS33 } [get_ports { eth_rx_err  }];
set_property -dict { PACKAGE_PIN H16   IOSTANDARD LVCMOS33 } [get_ports { eth_tx_clk  }];
set_property -dict { PACKAGE_PIN H15   IOSTANDARD LVCMOS33 } [get_ports { eth_tx_en   }];
set_property -dict { PACKAGE_PIN H14   IOSTANDARD LVCMOS33 } [get_ports { eth_tx_d[0] }];
set_property -dict { PACKAGE_PIN J14   IOSTANDARD LVCMOS33 } [get_ports { eth_tx_d[1] }];
set_property -dict { PACKAGE_PIN J13   IOSTANDARD LVCMOS33 } [get_ports { eth_tx_d[2] }];
set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { eth_tx_d[3] }];

Personal tools