-------------------------------------------------------------------------------- -- File : address_swap.vhd -- Author : Xilinx Inc. -- Project : Xilinx LogiCORE Virtex-6 Embedded Tri-Mode Ethernet MAC -- File : address_swap.vhd -- Version : 2.3 ------------------------------------------------------------------------------- -- -- (c) Copyright 2010 Xilinx, Inc. All rights reserved. -- -- This file contains confidential and proprietary information -- of Xilinx, Inc. and is protected under U.S. and -- international copyright and other intellectual property -- laws. -- -- DISCLAIMER -- This disclaimer is not a license and does not grant any -- rights to the materials distributed herewith. Except as -- otherwise provided in a valid license issued to you by -- Xilinx, and to the maximum extent permitted by applicable -- law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND -- WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES -- AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING -- BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON- -- INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and -- (2) Xilinx shall not be liable (whether in contract or tort, -- including negligence, or under any other theory of -- liability) for any loss or damage of any kind or nature -- related to, arising under or in connection with these -- materials, including for any direct, or any indirect, -- special, incidental, or consequential loss or damage -- (including loss of data, profits, goodwill, or any type of -- loss or damage suffered as a result of any action brought -- by a third party) even if such damage or loss was -- reasonably foreseeable or Xilinx had been advised of the -- possibility of the same. -- -- CRITICAL APPLICATIONS -- Xilinx products are not designed or intended to be fail- -- safe, or for use in any application requiring fail-safe -- performance, such as life-support or safety devices or -- systems, Class III medical devices, nuclear facilities, -- applications related to the deployment of airbags, or any -- other applications that could lead to death, personal -- injury, or severe property or environmental damage -- (individually and collectively, "Critical -- Applications"). Customer assumes the sole risk and -- liability of any use of Xilinx products in Critical -- Applications, subject only to applicable laws and -- regulations governing limitations on product liability. -- -- THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS -- PART OF THIS FILE AT ALL TIMES. -- -- Description: This address swap block will accept the first 12 byte of a packet before -- starting to loop it out. At this point both the source and destination fields have -- been completely captured and can therefore be swapped. -- -------------------------------------------------------------------------------- library unisim; use unisim.vcomponents.all; library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.numeric_std.all; entity address_swap is port ( axi_tclk : in std_logic; axi_tresetn : in std_logic; en_address_swap : in std_logic; -- data from the RX FIFO rx_axis_fifo_tdata : in std_logic_vector(7 downto 0); rx_axis_fifo_tvalid : in std_logic; rx_axis_fifo_tlast : in std_logic; rx_axis_fifo_tready : out std_logic; -- data TO the tx fifo tx_axis_fifo_tdata : out std_logic_vector(7 downto 0); tx_axis_fifo_tvalid : out std_logic; tx_axis_fifo_tlast : out std_logic; tx_axis_fifo_tready : in std_logic ); end address_swap; architecture rtl of address_swap is -- State machine type rd_state_typ is (IDLE, WAIT_S, READ_DEST, READ_SRC, READ_DEST2, READ_SRC2, READ); type wr_state_typ is (IDLE_W, WRITE_SLOT1, WRITE_SLOT2, WRITE); signal next_rd_state : rd_state_typ; signal rd_state : rd_state_typ; signal next_wr_state : wr_state_typ; signal wr_state : wr_state_typ; signal rx_axis_fifo_tvalid_reg : std_logic; signal rx_axis_fifo_tlast_reg : std_logic; signal wr_count : unsigned(3 downto 0); signal fifo_full : std_logic; signal wr_slot : unsigned(2 downto 0); signal wr_addr : unsigned(2 downto 0); signal dia : std_logic_vector(8 downto 0); signal doa : std_logic_vector(8 downto 0); signal wea : std_logic; signal rd_count : unsigned(3 downto 0); signal fifo_empty : std_logic; signal rd_slot : unsigned(2 downto 0); signal rd_addr : unsigned(2 downto 0); signal dob : std_logic_vector(8 downto 0); signal tx_axis_fifo_tvalid_int : std_logic; signal tx_axis_fifo_tlast_int : std_logic; signal rx_axis_fifo_tready_int : std_logic; signal axi_treset : std_logic; signal new_packet_start : std_logic; signal rd_count_6 : std_logic; signal rd_count_12 : std_logic; signal slot_diff : unsigned(2 downto 0); signal packet_waiting : std_logic; begin rx_axis_fifo_tready <= rx_axis_fifo_tready_int; axi_treset <= not axi_tresetn; -- capture a new packet starting as we only want to start taking it once the read side is idle new_pkt_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then new_packet_start <= '0'; elsif wr_state = IDLE_W and packet_waiting = '0' and rx_axis_fifo_tvalid = '1' and (rx_axis_fifo_tvalid_reg = '0' or rx_axis_fifo_tlast_reg = '1') then new_packet_start <= '1'; elsif wr_state /= IDLE_W then new_packet_start <= '0'; end if; end if; end process new_pkt_p; -- need to monitor the RX FIFO AXI interface and when a new transaction starts capture the first -- 6 bytes of the frame. Use a LUT6 to capture the data - allows some backoff to take place -- need to maintain a read an write interface.. -- Write interface reg_axi_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then rx_axis_fifo_tvalid_reg <= rx_axis_fifo_tvalid; rx_axis_fifo_tlast_reg <= rx_axis_fifo_tlast; end if; end process reg_axi_p; -- simple write state machine next_wr_s : process(wr_state, rx_axis_fifo_tvalid, wr_count, rx_axis_fifo_tlast, new_packet_start, rd_state) begin next_wr_state <= wr_state; case wr_state is -- detect a rising edge on TVALID OR TLAST on previous cycle AND TVALID when IDLE_W => if rd_state = IDLE and new_packet_start = '1' then next_wr_state <= WRITE_SLOT1; end if; -- finish writing when tlast is high when WRITE_SLOT1 => if wr_count = X"6" and rx_axis_fifo_tvalid = '1' then next_wr_state <= WRITE_SLOT2; elsif rx_axis_fifo_tlast = '1' and rx_axis_fifo_tvalid = '1' then next_wr_state <= IDLE_W; end if; when WRITE_SLOT2 => if wr_count = X"c" and rx_axis_fifo_tvalid = '1' then next_wr_state <= WRITE; elsif rx_axis_fifo_tlast = '1' and rx_axis_fifo_tvalid = '1' then next_wr_state <= IDLE_W; end if; when WRITE => if rx_axis_fifo_tlast = '1' and rx_axis_fifo_tvalid = '1' then next_wr_state <= IDLE_W; end if; end case; end process; wr_state_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then wr_state <= IDLE_W; elsif fifo_full = '0' then wr_state <= next_wr_state; end if; end if; end process wr_state_p; packet_wait_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then packet_waiting <= '0'; elsif wr_state = IDLE_W and next_wr_state = WRITE_SLOT1 then packet_waiting <= '1'; elsif rd_state /= IDLE then packet_waiting <= '0'; end if; end if; end process packet_wait_p; -- generate a write count to control where the data is written wr_count_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then wr_count <= (others => '0'); else if wr_state = IDLE_W and next_wr_state = WRITE_SLOT1 then wr_count <= X"1"; elsif wr_state /= IDLE_W and rx_axis_fifo_tvalid = '1' and fifo_full = '0' and wr_count /= x"f" then wr_count <= wr_count + X"1"; end if; end if; end if; end process wr_count_p; -- we have a 64 deep lut - to simplify storing/fetching of data this is split into 8 address slots. When -- a new packet starts the first byte of the address is stored in the next available address slot, with the next address being -- stored in the next slot (i.e after a gap of two locations). Once the addresses have been stored the data starts -- at the next slot and then continues until completion. wr_slot_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then wr_slot <= (others => '0'); wr_addr <= (others => '0'); elsif wr_state = IDLE_W and next_wr_state = WRITE_SLOT1 then wr_slot <= "000"; wr_addr <= (others => '0'); elsif wr_state = WRITE_SLOT1 and next_wr_state = WRITE_SLOT2 then wr_slot <= "001"; wr_addr <= (others => '0'); elsif wr_state = WRITE_SLOT2 and next_wr_state = WRITE then wr_slot <= "010"; wr_addr <= (others => '0'); elsif rx_axis_fifo_tready_int = '1' and rx_axis_fifo_tvalid = '1' and fifo_full = '0' then wr_addr <= wr_addr + "001"; if wr_addr = "111" then wr_slot <= wr_slot + "001"; end if; end if; end if; end process wr_slot_p; slot_diff <= rd_slot - wr_slot; -- need to generate full logic to generate the ready - simplified by there only being -- one clock domain.. -- to allow for reaction time generate as full when we are only one slot away fifo_full_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if (slot_diff = "010" or slot_diff = "001") and wr_state = WRITE then fifo_full <= '1'; else fifo_full <= '0'; end if; end if; end process fifo_full_p; rx_axis_fifo_tready_int <= '1' when fifo_full = '0' and wr_state /= IDLE_W else '0'; dia <= rx_axis_fifo_tlast & rx_axis_fifo_tdata; wea <= '1' when rx_axis_fifo_tready_int = '1' else '0'; LUT6_gen : for I in 0 to 8 generate begin RAM64X1D_inst : RAM64X1D port map ( DPO => dob(I), SPO => doa(I), A0 => wr_addr(0), A1 => wr_addr(1), A2 => wr_addr(2), A3 => wr_slot(0), A4 => wr_slot(1), A5 => wr_slot(2), D => dia(I), DPRA0 => rd_addr(0), DPRA1 => rd_addr(1), DPRA2 => rd_addr(2), DPRA3 => rd_slot(0), DPRA4 => rd_slot(1), DPRA5 => rd_slot(2), WCLK => axi_tclk, WE => wea ); end generate; -- read logic - this is kicked into action when the wr_state moves from IDLE but will not start to read until -- the wr_state moves to WRITE as the two addresses are then in situ -- can then choose if we wish to addess swap or not - if a small packet is rxd which is less than the required 12 bytes -- the read logic will revert to non address swap and just output what is there.. next_rd_s : process(rd_state, en_address_swap, rd_count_6, rd_count_12, tx_axis_fifo_tready, dob, wr_state, tx_axis_fifo_tvalid_int) begin next_rd_state <= rd_state; case rd_state is when IDLE => if wr_state /= IDLE_W then next_rd_state <= WAIT_S; end if; when WAIT_S => if wr_state = IDLE_W then next_rd_state <= READ_DEST2; elsif wr_state = WRITE then if en_address_swap = '1' then next_rd_state <= READ_SRC; else next_rd_state <= READ_DEST2; end if; end if; when READ_SRC => if rd_count_6 = '1' and tx_axis_fifo_tready = '1' and tx_axis_fifo_tvalid_int = '1' then next_rd_state <= READ_DEST; end if; when READ_DEST => if rd_count_12 = '1' and tx_axis_fifo_tready = '1' and tx_axis_fifo_tvalid_int = '1' then next_rd_state <= READ; end if; when READ_DEST2 => if rd_count_6 = '1' and tx_axis_fifo_tready = '1' and tx_axis_fifo_tvalid_int = '1' then next_rd_state <= READ_SRC2; end if; when READ_SRC2 => if rd_count_12 = '1' and tx_axis_fifo_tready = '1' and tx_axis_fifo_tvalid_int = '1' then next_rd_state <= READ; end if; when READ => if dob(8) = '1' and tx_axis_fifo_tready = '1' and tx_axis_fifo_tvalid_int = '1' then next_rd_state <= IDLE; end if; when others => next_rd_state <= IDLE; end case; end process; rd_state_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then rd_state <= IDLE; elsif tx_axis_fifo_tready = '1' then rd_state <= next_rd_state; end if; end if; end process rd_state_p; -- generate a read count to control where the data is read rd_count_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then rd_count <= (others => '0'); rd_count_6 <= '0'; rd_count_12 <= '0'; else if rd_state = WAIT_S then rd_count <= X"1"; elsif rd_state /= IDLE and tx_axis_fifo_tvalid_int = '1' and tx_axis_fifo_tready = '1' and rd_count /= X"f" then rd_count <= rd_count + X"1"; if rd_count = X"5" then rd_count_6 <= '1'; else rd_count_6 <= '0'; end if; if rd_count = X"b" then rd_count_12 <= '1'; else rd_count_12 <= '0'; end if; end if; end if; end if; end process rd_count_p; -- we have a 64 deep lut - to simplify storing/fetching of data this is split into 8 address slots. When -- a new packet starts the first byte of the address is stored in the next available address slot, with the next address being -- stored in the next slot (i.e after a gap of two locations). Once the addresses have been stored the data starts -- at the next slot and then continues until completion. rd_slot_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if axi_treset = '1' then rd_slot <= (others => '0'); rd_addr <= (others => '0'); elsif rd_state = WAIT_S and next_rd_state = READ_DEST2 then rd_slot <= "000"; rd_addr <= (others => '0'); elsif rd_state = WAIT_S and next_rd_state = READ_SRC then rd_slot <= "001"; rd_addr <= (others => '0'); elsif rd_state = READ_DEST2 and next_rd_state = READ_SRC2 then rd_slot <= "001"; rd_addr <= (others => '0'); elsif rd_state = READ_SRC and next_rd_state = READ_DEST then rd_slot <= "000"; rd_addr <= (others => '0'); elsif rd_state = READ_DEST and next_rd_state = READ then rd_slot <= "010"; rd_addr <= (others => '0'); elsif rd_state = READ_SRC2 and next_rd_state = READ then rd_slot <= "010"; rd_addr <= (others => '0'); elsif tx_axis_fifo_tvalid_int = '1' and tx_axis_fifo_tready = '1' then rd_addr <= rd_addr + "001"; if rd_addr = "111" then rd_slot <= rd_slot + "001"; end if; end if; end if; end process rd_slot_p; -- need to generate empty to generate the tvalid for the tx_fifo interface - the empty is purely a compare of the -- rd/wr access point - if the same and TLAST (dob[8]) is low then must still be in packet so drop tvalid - and stall read empty_p : process (wr_slot, wr_addr, rd_slot, rd_addr) begin if wr_slot = rd_slot and wr_addr = rd_addr then fifo_empty <= '1'; else fifo_empty <= '0'; end if; end process; -- generate the tvalid valid_p : process (rd_state, fifo_empty, tx_axis_fifo_tready, dob) begin if rd_state = IDLE then tx_axis_fifo_tvalid_int <= '0'; elsif rd_state /= WAIT_S then if fifo_empty = '1' and tx_axis_fifo_tready = '1' and dob(8) = '0' then tx_axis_fifo_tvalid_int <= '0'; else tx_axis_fifo_tvalid_int <= '1'; end if; else tx_axis_fifo_tvalid_int <= '0'; end if; end process; -- and the output data/tlast rd_data_p : process (axi_tclk) begin if axi_tclk'event and axi_tclk = '1' then if tx_axis_fifo_tready = '1' then if fifo_empty = '0' then tx_axis_fifo_tdata <= dob(7 downto 0); end if; tx_axis_fifo_tvalid <= tx_axis_fifo_tvalid_int; tx_axis_fifo_tlast_int <= dob(8); end if; end if; end process rd_data_p; tx_axis_fifo_tlast <= tx_axis_fifo_tlast_int; end rtl;