Skip to content
Snippets Groups Projects
Commit 7ce22b6d authored by Eric Kooistra's avatar Eric Kooistra
Browse files

SVN copy-renamed tb_ip_stratixiv_tse_sgmii_lvds.vhd to ip_arria10. No functional changes yet.

parent 55c051be
No related branches found
No related tags found
No related merge requests found
-------------------------------------------------------------------------------
--
-- Copyright (C) 2014
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program 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 General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
-------------------------------------------------------------------------------
-- Purpose: Testbench for ip_arria10_tse_sgmii_lvds.
-- Description:
-- The testbench in /testbench/tse_sgmii_lvds/tse_sgmii_lvds_tb.vhd that is
-- generated by the MegaWizard provides an elaborate testbench. For
-- Uniboard purposes in tb/ a minimal testbench tb_tse_sgmii_lvds.vhd was
-- derived manually from the generated testbench. This tb_tse_sgmii_lvds
-- is more easy to use.
-- The tb is self checking based on that tx_pkt_cnt=rx_pkt_cnt must be true
-- at the tb_end.
-- Usage:
-- > as 10
-- > run -all
LIBRARY IEEE, common_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
ENTITY tb_ip_arria10_tse_sgmii_lvds IS
END tb_ip_arria10_tse_sgmii_lvds;
ARCHITECTURE tb OF tb_ip_arria10_tse_sgmii_lvds IS
CONSTANT sys_clk_period : TIME := 10 ns; -- 100 MHz
CONSTANT eth_clk_period : TIME := 8 ns; -- 125 MHz
CONSTANT c_tse_reg_addr_w : NATURAL := 8; -- = max 256 MAC registers
CONSTANT c_tse_byte_addr_w : NATURAL := c_tse_reg_addr_w + 2;
CONSTANT c_tse_byte_addr_pcs_offset : NATURAL := 16#200#; -- table 4.8, 4.9 in ug_ethernet.pdf
CONSTANT c_tse_data_w : NATURAL := c_word_w; -- = 32
CONSTANT c_tse_symbol_w : NATURAL := c_byte_w; -- = 8
CONSTANT c_tse_symbol_max : NATURAL := 2**c_tse_symbol_w-1; -- = 255
CONSTANT c_tse_symbols_per_beat : NATURAL := c_tse_data_w / c_tse_symbol_w; -- = 4
CONSTANT c_tse_pcs_reg_addr_w : NATURAL := 5; -- = max 32 PCS registers
CONSTANT c_tse_pcs_halfword_addr_w : NATURAL := c_tse_pcs_reg_addr_w + 1; -- table 4.17 in ug_ethernet.pdf
CONSTANT c_tse_pcs_byte_addr_w : NATURAL := c_tse_pcs_reg_addr_w + 2;
CONSTANT c_tse_pcs_data_w : NATURAL := c_halfword_w; -- = 16;
CONSTANT c_tse_empty_w : NATURAL := 2;
CONSTANT c_tse_tx_error_w : NATURAL := 1;
CONSTANT c_tse_rx_error_w : NATURAL := 6;
CONSTANT c_tse_error_w : NATURAL := largest(c_tse_tx_error_w, c_tse_rx_error_w);
CONSTANT c_tse_err_stat_w : NATURAL := 18;
CONSTANT c_tse_frm_type_w : NATURAL := 4;
CONSTANT c_tse_tx_fifo_depth : NATURAL := 256; -- nof words for Tx FIFO
CONSTANT c_tse_rx_fifo_depth : NATURAL := 256; -- nof words for Rx FIFO
CONSTANT c_tse_promis_en : BOOLEAN := FALSE;
--CONSTANT c_tse_promis_en : BOOLEAN := TRUE;
CONSTANT c_tx_data_type : NATURAL := 1; -- 0 = symbols, 1 = counter
CONSTANT c_tx_ready_latency : NATURAL := 0;
CONSTANT c_nof_tx_not_valid : NATURAL := 0; -- when > 0 then pull tx valid low for c_nof_tx_not_valid beats during tx
CONSTANT c_eth_dst_mac : STD_LOGIC_VECTOR(47 DOWNTO 0) := X"10FA01020300";
CONSTANT c_eth_src_mac : STD_LOGIC_VECTOR(47 DOWNTO 0) := X"123456789ABC"; -- = 12-34-56-78-9A-BC
CONSTANT c_eth_ethertype : STD_LOGIC_VECTOR(15 DOWNTO 0) := X"10FA";
TYPE t_mm_bus IS RECORD
-- Master In Slave Out
waitreq : STD_LOGIC;
rddata : STD_LOGIC_VECTOR(c_tse_data_w-1 DOWNTO 0);
-- Master Out Slave In
address : STD_LOGIC_VECTOR(c_tse_byte_addr_w-1 DOWNTO 0);
wrdata : STD_LOGIC_VECTOR(c_tse_data_w-1 DOWNTO 0);
wr : STD_LOGIC;
rd : STD_LOGIC;
END RECORD;
PROCEDURE proc_dbg_mm_bus(SIGNAL mm_miso : IN t_mm_bus;
SIGNAL mm_mosi : IN t_mm_bus;
SIGNAL dbg_mm : OUT t_mm_bus) IS
BEGIN
dbg_mm.waitreq <= mm_miso.waitreq;
dbg_mm.rddata <= mm_miso.rddata;
dbg_mm.address <= mm_mosi.address;
dbg_mm.wrdata <= mm_mosi.wrdata;
dbg_mm.wr <= mm_mosi.wr;
dbg_mm.rd <= mm_mosi.rd;
END proc_dbg_mm_bus;
-- Wait for MM access (either read or write) finished
PROCEDURE proc_mm_access(SIGNAL mm_clk : IN STD_LOGIC;
SIGNAL mm_waitreq : IN STD_LOGIC;
SIGNAL mm_access : OUT STD_LOGIC) IS
BEGIN
mm_access <= '1';
WAIT UNTIL rising_edge(mm_clk);
WHILE mm_waitreq='1' LOOP
WAIT UNTIL rising_edge(mm_clk);
END LOOP;
mm_access <= '0';
END proc_mm_access;
-- Use word addressing for MAC registers according to table 4.8, 4.9
PROCEDURE proc_wr_mac(CONSTANT mac_addr : IN NATURAL;
CONSTANT mac_data : IN NATURAL;
SIGNAL mm_clk : IN STD_LOGIC;
SIGNAL mm_miso : IN t_mm_bus;
SIGNAL mm_mosi : OUT t_mm_bus) IS
BEGIN
mm_mosi.address <= STD_LOGIC_VECTOR(TO_UNSIGNED(mac_addr, c_tse_byte_addr_w));
mm_mosi.wrdata <= STD_LOGIC_VECTOR(TO_UNSIGNED(mac_data, c_tse_data_w));
proc_mm_access(mm_clk, mm_miso.waitreq, mm_mosi.wr);
END proc_wr_mac;
PROCEDURE proc_rd_mac(CONSTANT mac_addr : IN NATURAL;
SIGNAL mac_data : OUT NATURAL;
SIGNAL mm_clk : IN STD_LOGIC;
SIGNAL mm_miso : IN t_mm_bus;
SIGNAL mm_mosi : OUT t_mm_bus) IS
BEGIN
mm_mosi.address <= STD_LOGIC_VECTOR(TO_UNSIGNED(mac_addr, c_tse_byte_addr_w));
proc_mm_access(mm_clk, mm_miso.waitreq, mm_mosi.rd);
MAC_data <= TO_INTEGER(UNSIGNED(mm_miso.rddata));
END proc_rd_mac;
-- Use halfword addressing for PCS register to match table 4.17
PROCEDURE proc_wr_pcs(CONSTANT pcs_addr : IN NATURAL;
CONSTANT pcs_data : IN NATURAL;
SIGNAL mm_clk : IN STD_LOGIC;
SIGNAL mm_miso : IN t_mm_bus;
SIGNAL mm_mosi : OUT t_mm_bus) IS
BEGIN
mm_mosi.address <= STD_LOGIC_VECTOR(TO_UNSIGNED(pcs_addr*2 + c_tse_byte_addr_pcs_offset, c_tse_byte_addr_w));
mm_mosi.wrdata <= (OTHERS=>'0');
mm_mosi.wrdata(c_tse_pcs_data_w-1 DOWNTO 0) <= STD_LOGIC_VECTOR(TO_UNSIGNED(pcs_data, c_tse_pcs_data_w));
proc_mm_access(mm_clk, mm_miso.waitreq, mm_mosi.wr);
END proc_wr_pcs;
PROCEDURE proc_rd_pcs(CONSTANT pcs_addr : IN NATURAL;
SIGNAL pcs_data : OUT NATURAL;
SIGNAL mm_clk : IN STD_LOGIC;
SIGNAL mm_miso : IN t_mm_bus;
SIGNAL mm_mosi : OUT t_mm_bus) IS
BEGIN
mm_mosi.address <= STD_LOGIC_VECTOR(TO_UNSIGNED(pcs_addr*2 + c_tse_byte_addr_pcs_offset, c_tse_byte_addr_w));
proc_mm_access(mm_clk, mm_miso.waitreq, mm_mosi.rd);
pcs_data <= TO_INTEGER(UNSIGNED(mm_miso.rddata(c_tse_pcs_data_w-1 DOWNTO 0)));
END proc_rd_pcs;
TYPE t_tse_stream IS RECORD
-- Source In or Sink Out
ready : STD_LOGIC;
-- Source Out or Sink In
data : STD_LOGIC_VECTOR(c_tse_data_w-1 DOWNTO 0);
valid : STD_LOGIC;
sop : STD_LOGIC;
eop : STD_LOGIC;
empty : STD_LOGIC_VECTOR(c_tse_empty_w-1 DOWNTO 0);
err : STD_LOGIC_VECTOR(c_tse_error_w-1 DOWNTO 0);
END RECORD;
PROCEDURE proc_dbg_tse_stream_src(SIGNAL src_in : IN t_tse_stream;
SIGNAL src_out : IN t_tse_stream;
SIGNAL dbg_src : OUT t_tse_stream) IS
BEGIN
dbg_src.ready <= src_in.ready;
dbg_src.data <= src_out.data;
dbg_src.valid <= src_out.valid;
dbg_src.sop <= src_out.sop;
dbg_src.eop <= src_out.eop;
dbg_src.empty <= src_out.empty;
dbg_src.err <= src_out.err;
END proc_dbg_tse_stream_src;
PROCEDURE proc_dbg_tse_stream_snk(SIGNAL snk_in : IN t_tse_stream;
SIGNAL snk_out : IN t_tse_stream;
SIGNAL dbg_snk : OUT t_tse_stream) IS
BEGIN
dbg_snk.ready <= snk_out.ready;
dbg_snk.data <= snk_in.data;
dbg_snk.valid <= snk_in.valid;
dbg_snk.sop <= snk_in.sop;
dbg_snk.eop <= snk_in.eop;
dbg_snk.empty <= snk_in.empty;
dbg_snk.err <= snk_in.err;
END proc_dbg_tse_stream_snk;
-- Handle TX ready
-- Only support tx_ready_latency=0 or 1, corresponding to TX_ALMOST_FULL=3 or 4
-- Support for tx_ready_latency>1 requires keeping previous ready information
-- in a STD_LOGIC_VECTOR(tx_ready_latency-1 DOWNTO 0).
PROCEDURE proc_ready_latency(CONSTANT c_latency : IN NATURAL;
SIGNAL clk : IN STD_LOGIC;
SIGNAL ready : IN STD_LOGIC;
CONSTANT c_valid : IN STD_LOGIC;
CONSTANT c_sop : IN STD_LOGIC;
CONSTANT c_eop : IN STD_LOGIC;
SIGNAL out_valid : OUT STD_LOGIC;
SIGNAL out_sop : OUT STD_LOGIC;
SIGNAL out_eop : OUT STD_LOGIC) IS
BEGIN
IF c_latency=0 THEN
out_valid <= c_valid;
out_sop <= c_sop;
out_eop <= c_eop;
WAIT UNTIL rising_edge(clk);
WHILE ready /= '1' LOOP
WAIT UNTIL rising_edge(clk);
END LOOP;
END IF;
IF c_latency=1 THEN
WHILE ready /= '1' LOOP
out_valid <= '0';
out_sop <= '0';
out_eop <= '0';
WAIT UNTIL rising_edge(clk);
END LOOP;
out_valid <= c_valid;
out_sop <= c_sop;
out_eop <= c_eop;
WAIT UNTIL rising_edge(clk);
END IF;
END proc_ready_latency;
-- Transmit user packet
-- . Use word aligned payload data, so with half word inserted before the 14 byte header
-- . Packets can be send immediately after eachother so new sop directly after last eop
-- . The word rate is controlled by respecting ready from the MAC
PROCEDURE proc_tx_packet(CONSTANT dst_mac_addr : IN STD_LOGIC_VECTOR(c_eth_dst_mac'RANGE);
CONSTANT src_mac_addr : IN STD_LOGIC_VECTOR(c_eth_src_mac'RANGE);
CONSTANT ethertype : IN STD_LOGIC_VECTOR(c_eth_ethertype'RANGE);
CONSTANT data_len : IN NATURAL; -- in symbols = octets = bytes
SIGNAL dp_clk : IN STD_LOGIC;
SIGNAL dp_src_in : IN t_tse_stream;
SIGNAL dp_src_out : OUT t_tse_stream) IS
CONSTANT c_mod : NATURAL := data_len MOD c_tse_symbols_per_beat;
CONSTANT c_nof_data_beats : NATURAL := data_len / c_tse_symbols_per_beat + sel_a_b(c_mod, 1, 0);
CONSTANT c_empty : NATURAL := sel_a_b(c_mod, c_tse_symbols_per_beat - c_mod, 0);
VARIABLE v_sym : UNSIGNED(c_tse_symbol_w-1 DOWNTO 0) := (OTHERS=>'0');
VARIABLE v_num : UNSIGNED(c_tse_data_w-1 DOWNTO 0) := (OTHERS=>'0');
BEGIN
-- DST MAC
dp_src_out.empty <= STD_LOGIC_VECTOR(TO_UNSIGNED(0, c_tse_empty_w));
dp_src_out.data <= (OTHERS=>'0');
dp_src_out.data(15 DOWNTO 0) <= hton(dst_mac_addr(15 DOWNTO 0)); -- send to itself
proc_ready_latency(c_tx_ready_latency, dp_clk, dp_src_in.ready, '1', '1', '0', dp_src_out.valid, dp_src_out.sop, dp_src_out.eop);
dp_src_out.data <= hton(dst_mac_addr(47 DOWNTO 16));
proc_ready_latency(c_tx_ready_latency, dp_clk, dp_src_in.ready, '1', '0', '0', dp_src_out.valid, dp_src_out.sop, dp_src_out.eop);
-- SRC MAC
dp_src_out.data <= hton(src_mac_addr(31 DOWNTO 0));
proc_ready_latency(c_tx_ready_latency, dp_clk, dp_src_in.ready, '1', '0', '0', dp_src_out.valid, dp_src_out.sop, dp_src_out.eop);
-- SRC MAC & ETHERTYPE
dp_src_out.data <= hton(src_mac_addr(47 DOWNTO 32)) & hton(c_eth_ethertype);
-- DATA
FOR I IN 0 TO c_nof_data_beats-1 LOOP
proc_ready_latency(c_tx_ready_latency, dp_clk, dp_src_in.ready, '1', '0', '0', dp_src_out.valid, dp_src_out.sop, dp_src_out.eop);
IF c_tx_data_type=0 THEN
-- data : X"01020304", X"05060708", X"090A0B0C", etc
FOR J IN c_tse_symbols_per_beat-1 DOWNTO 0 LOOP
v_sym := v_sym + 1;
dp_src_out.data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) <= STD_LOGIC_VECTOR(v_sym);
END LOOP;
ELSE
-- data : X"00000001", X"00000002", X"00000003", etc
v_num := v_num + 1;
dp_src_out.data <= STD_LOGIC_VECTOR(v_num);
END IF;
-- tb : pull valid low for some time during the middle of the payload
IF c_nof_tx_not_valid > 0 AND I=c_nof_data_beats/2 THEN
dp_src_out.valid <= '0';
FOR I IN 0 TO c_nof_tx_not_valid LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP;
dp_src_out.valid <= '1';
END IF;
END LOOP;
IF c_empty > 0 THEN
dp_src_out.empty <= STD_LOGIC_VECTOR(TO_UNSIGNED(c_empty, c_tse_empty_w));
FOR J IN c_empty-1 DOWNTO 0 LOOP
dp_src_out.data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) <= (OTHERS=>'0');
END LOOP;
END IF;
proc_ready_latency(c_tx_ready_latency, dp_clk, dp_src_in.ready, '1', '0', '1', dp_src_out.valid, dp_src_out.sop, dp_src_out.eop);
dp_src_out.data <= (OTHERS=>'0');
dp_src_out.valid <= '0';
dp_src_out.eop <= '0';
dp_src_out.empty <= STD_LOGIC_VECTOR(TO_UNSIGNED(0, c_tse_empty_w));
END proc_tx_packet;
PROCEDURE proc_valid_sop(SIGNAL clk : IN STD_LOGIC;
SIGNAL in_valid : IN STD_LOGIC;
SIGNAL in_sop : IN STD_LOGIC) IS
BEGIN
WAIT UNTIL rising_edge(clk);
WHILE in_valid /= '1' AND in_sop /= '1' LOOP
WAIT UNTIL rising_edge(clk);
END LOOP;
END proc_valid_sop;
PROCEDURE proc_valid(SIGNAL clk : IN STD_LOGIC;
SIGNAL in_valid : IN STD_LOGIC) IS
BEGIN
WAIT UNTIL rising_edge(clk);
WHILE in_valid /= '1' LOOP
WAIT UNTIL rising_edge(clk);
END LOOP;
END proc_valid;
-- Receive packet
-- . Use word aligned payload data, so with half word inserted before the 14 byte header
-- . Packets can be always be received, assume the user application is always ready
-- . The CRC32 is also passed on to the user at eop.
-- . Note that when empty/=0 then the CRC32 is not word aligned, so therefore use prev_data to be able
-- to handle part of last data word in case empty/=0 at eop
PROCEDURE proc_rx_packet(CONSTANT dst_mac_addr : IN STD_LOGIC_VECTOR(c_eth_dst_mac'RANGE);
CONSTANT src_mac_addr : IN STD_LOGIC_VECTOR(c_eth_src_mac'RANGE);
CONSTANT ethertype : IN STD_LOGIC_VECTOR(c_eth_ethertype'RANGE);
SIGNAL dp_clk : IN STD_LOGIC;
SIGNAL dp_snk_in : IN t_tse_stream;
SIGNAL dp_snk_out : OUT t_tse_stream) IS
VARIABLE v_sym : UNSIGNED(c_tse_symbol_w-1 DOWNTO 0) := (OTHERS=>'0');
VARIABLE v_num : UNSIGNED(c_tse_data_w-1 DOWNTO 0) := (OTHERS=>'0');
VARIABLE v_empty : NATURAL;
VARIABLE v_first : BOOLEAN := TRUE;
VARIABLE v_data : STD_LOGIC_VECTOR(c_tse_data_w-1 DOWNTO 0);
VARIABLE v_prev_data : STD_LOGIC_VECTOR(c_tse_data_w-1 DOWNTO 0);
BEGIN
-- Keep ff_rx_snk_out.ready='1' all the time
dp_snk_out.ready <= '1';
-- Verify DST MAC
proc_valid_sop(dp_clk, dp_snk_in.valid, dp_snk_in.sop);
ASSERT dp_snk_in.data(31 DOWNTO 16) = X"0000" REPORT "RX: Alignment half word not zero" SEVERITY ERROR;
ASSERT dp_snk_in.data(15 DOWNTO 0) = hton(dst_mac_addr(15 DOWNTO 0)) REPORT "RX: Wrong dst_mac_addr(15 DOWNTO 0)" SEVERITY ERROR;
proc_valid(dp_clk, dp_snk_in.valid);
ASSERT dp_snk_in.data(31 DOWNTO 0) = hton(dst_mac_addr(47 DOWNTO 16)) REPORT "RX: Wrong dst_mac_addr(47 DOWNTO 16)" SEVERITY ERROR;
-- Verify SRC MAC
proc_valid(dp_clk, dp_snk_in.valid);
ASSERT dp_snk_in.data(31 DOWNTO 0) = hton(src_mac_addr(31 DOWNTO 0)) REPORT "RX: Wrong src_mac_addr(31 DOWNTO 0)" SEVERITY ERROR;
-- Verify SRC MAC & ETHERTYPE
proc_valid(dp_clk, dp_snk_in.valid);
ASSERT dp_snk_in.data(31 DOWNTO 16) = hton(src_mac_addr(47 DOWNTO 32)) REPORT "RX: Wrong src_mac_addr(47 DOWNTO 32)" SEVERITY ERROR;
ASSERT dp_snk_in.data(15 DOWNTO 0) = hton(c_eth_ethertype) REPORT "RX: Wrong ethertype" SEVERITY ERROR;
-- Verify DATA
v_first := TRUE;
proc_valid(dp_clk, dp_snk_in.valid);
WHILE dp_snk_in.eop /= '1' LOOP
v_prev_data := v_data;
v_data := dp_snk_in.data;
IF v_first = FALSE THEN
IF c_tx_data_type=0 THEN
-- data : X"01020304", X"05060708", X"090A0B0C", etc
FOR J IN c_tse_symbols_per_beat-1 DOWNTO 0 LOOP
v_sym := v_sym + 1;
ASSERT UNSIGNED(v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w)) = v_sym REPORT "RX: Wrong data symbol" SEVERITY ERROR;
END LOOP;
ELSE
-- data : X"00000001", X"00000002", X"00000003", etc
v_num := v_num + 1;
ASSERT UNSIGNED(v_prev_data) = v_num REPORT "RX: Wrong data word" SEVERITY ERROR;
END IF;
END IF;
v_first := FALSE;
proc_valid(dp_clk, dp_snk_in.valid);
END LOOP;
-- Verify last DATA and CRC32 if empty /=0 else the last word is only the CRC32
v_prev_data := v_data;
v_data := dp_snk_in.data;
v_empty := TO_INTEGER(UNSIGNED(dp_snk_in.empty));
IF v_empty > 0 THEN
FOR J IN v_empty-1 DOWNTO 0 LOOP
v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) := (OTHERS=>'0');
END LOOP;
IF c_tx_data_type=0 THEN
-- data : X"01020304", X"05060708", X"090A0B0C", etc
FOR J IN c_tse_symbols_per_beat-1 DOWNTO v_empty LOOP -- ignore CRC32 symbols in last data word
v_sym := v_sym + 1;
ASSERT UNSIGNED(v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w)) = v_sym REPORT "RX: Wrong empty data symbol" SEVERITY ERROR;
END LOOP;
ELSE
-- data : X"00000001", X"00000002", X"00000003", etc
v_num := v_num + 1;
FOR J IN v_empty-1 DOWNTO 0 LOOP
v_num((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) := (OTHERS=>'0'); -- force CRC32 symbols in last data word to 0
END LOOP;
ASSERT UNSIGNED(v_prev_data) = v_num REPORT "RX: Wrong empty data word" SEVERITY ERROR;
END IF;
ELSE
-- No verify on CRC32 word
END IF;
END proc_rx_packet;
-- Clocks and reset
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL eth_clk : STD_LOGIC := '0';
SIGNAL sys_clk : STD_LOGIC := '0';
SIGNAL dp_clk : STD_LOGIC;
SIGNAL mm_clk : STD_LOGIC;
SIGNAL mm_rst : STD_LOGIC;
-- TSE MAC control interface
SIGNAL mm_init : STD_LOGIC := '1';
SIGNAL mm_miso : t_mm_bus; -- master in slave out
SIGNAL mm_mosi : t_mm_bus; -- master out slave in
SIGNAL pcs_rddata : NATURAL; -- [c_tse_pcs_data_w-1:0]
SIGNAL tse_led_an : STD_LOGIC;
SIGNAL tse_led_link : STD_LOGIC;
-- TSE MAC transmit interface
-- . Avalon ST source
SIGNAL ff_tx_src_in : t_tse_stream;
SIGNAL ff_tx_src_out : t_tse_stream;
-- . MAC specific
SIGNAL ff_tx_crc_fwd : STD_LOGIC;
SIGNAL ff_tx_septy : STD_LOGIC;
SIGNAL ff_tx_a_full : STD_LOGIC;
SIGNAL ff_tx_a_empty : STD_LOGIC;
SIGNAL ff_tx_uflow : STD_LOGIC;
-- TSE MAC receive interface
-- . Avalon ST sink
SIGNAL ff_rx_snk_in : t_tse_stream;
SIGNAL ff_rx_snk_out : t_tse_stream;
-- . MAC specific
SIGNAL ff_rx_ethertype: STD_LOGIC_VECTOR(c_tse_err_stat_w-1 DOWNTO 0);
SIGNAL ff_rx_frm_type : STD_LOGIC_VECTOR(c_tse_frm_type_w-1 DOWNTO 0);
SIGNAL ff_rx_dsav : STD_LOGIC;
SIGNAL ff_rx_a_full : STD_LOGIC;
SIGNAL ff_rx_a_empty : STD_LOGIC;
-- TSE PHY interface
SIGNAL eth_txp : STD_LOGIC;
SIGNAL eth_rxp : STD_LOGIC;
-- Verification
SIGNAL tx_pkt_cnt : NATURAL := 0;
SIGNAL rx_pkt_cnt : NATURAL := 0;
-- Debug signals to combine valid in and out of records
SIGNAL dbg_mm : t_mm_bus;
SIGNAL dbg_ff_tx : t_tse_stream;
SIGNAL dbg_ff_rx : t_tse_stream;
BEGIN
eth_clk <= NOT eth_clk AFTER eth_clk_period/2; -- TSE reference clock
sys_clk <= NOT sys_clk AFTER sys_clk_period/2; -- System clock
mm_clk <= sys_clk;
dp_clk <= sys_clk;
-- Debug signals to combine valid in and out of records
proc_dbg_mm_bus( mm_miso, mm_mosi, dbg_mm);
proc_dbg_tse_stream_src(ff_tx_src_in, ff_tx_src_out, dbg_ff_tx);
proc_dbg_tse_stream_snk(ff_rx_snk_in, ff_rx_snk_out, dbg_ff_rx);
-- run 1 us
p_mm_stimuli : PROCESS
BEGIN
mm_init <= '1';
mm_mosi.wr <= '0';
mm_mosi.rd <= '0';
-- reset release
mm_rst <= '1';
FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
mm_rst <= '0';
FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
-- PSC control
proc_rd_pcs(16#22#, pcs_rddata, mm_clk, mm_miso, mm_mosi); -- REV --> 0x0901
proc_wr_pcs(16#28#, 16#0008#, mm_clk, mm_miso, mm_mosi); -- IF_MODE <-- Force 1GbE, no autonegatiation
proc_rd_pcs(16#00#, pcs_rddata, mm_clk, mm_miso, mm_mosi); -- CONTROL --> 0x1140
proc_rd_pcs(16#02#, pcs_rddata, mm_clk, mm_miso, mm_mosi); -- STATUS --> 0x000D
proc_wr_pcs(16#00#, 16#0140#, mm_clk, mm_miso, mm_mosi); -- CONTROL <-- Auto negotiate disable
-- MAC control
proc_rd_mac(16#000#, pcs_rddata, mm_clk, mm_miso, mm_mosi); -- REV --> CUST_VERSION & 0x0901
IF c_tse_promis_en=FALSE THEN
proc_wr_mac(16#008#, 16#0100004B#, mm_clk, mm_miso, mm_mosi);
ELSE
proc_wr_mac(16#008#, 16#0100005B#, mm_clk, mm_miso, mm_mosi);
END IF;
-- COMMAND_CONFIG <--
-- Only the bits relevant to UniBoard are explained here, others are 0
-- [ 0] = TX_ENA = 1, enable tx datapath
-- [ 1] = RX_ENA = 1, enable rx datapath
-- [ 2] = XON_GEN = 0
-- [ 3] = ETH_SPEED = 1, enable 1GbE operation
-- [ 4] = PROMIS_EN = 0, when 1 then receive all frames
-- [ 5] = PAD_EN = 0, when 1 enable receive padding removal (requires ethertype=payload length)
-- [ 6] = CRC_FWD = 1, enable receive CRC forward
-- [ 7] = PAUSE_FWD = 0
-- [ 8] = PAUSE_IGNORE = 0
-- [ 9] = TX_ADDR_INS = 0, when 1 then MAX overwrites tx SRC MAC with mac_0,1 or one of the supplemental mac
-- [ 10] = HD_ENA = 0
-- [ 11] = EXCESS_COL = 0
-- [ 12] = LATE_COL = 0
-- [ 13] = SW_RESET = 0, when 1 MAC disables tx and rx, clear statistics and flushes receive FIFO
-- [ 14] = MHAS_SEL = 0, select multicast address resolutions hash-code mode
-- [ 15] = LOOP_ENA = 0
-- [18-16] = TX_ADDR_SEL[2:0] = 000, TX_ADDR_INS insert mac_0,1 or one of the supplemental mac
-- [ 19] = MAGIC_EN = 0
-- [ 20] = SLEEP = 0
-- [ 21] = WAKEUP = 0
-- [ 22] = XOFF_GEN = 0
-- [ 23] = CNT_FRM_ENA = 0
-- [ 24] = NO_LGTH_CHECK = 1, when 0 then check payload length of received frames (requires ethertype=payload length)
-- [ 25] = ENA_10 = 0
-- [ 26] = RX_ERR_DISC = 0, when 1 then discard erroneous frames (requires store and forward mode, so rx_section_full=0)
-- when 0 then pass on with rx_err[0]=1
-- [ 27] = DISABLE_RD_TIMEOUT = 0
-- [30-28] = RSVD = 000
-- [ 31] = CNT_RESET = 0, when 1 clear statistics
proc_wr_mac(16#00C#, 16#56789ABC#, mm_clk, mm_miso, mm_mosi); -- MAC_0
proc_wr_mac(16#010#, 16#00001234#, mm_clk, mm_miso, mm_mosi); -- MAC_1 <-- SRC_MAC = 12-34-56-78-9A-BC
proc_wr_mac(16#05C#, 16#0000000C#, mm_clk, mm_miso, mm_mosi); -- TX_IPG_LENGTH <-- interpacket gap = 12
proc_wr_mac(16#014#, 16#000005EE#, mm_clk, mm_miso, mm_mosi); -- FRM_LENGTH <-- receive max frame length = 1518
-- FIFO legenda:
-- . Tx section full = There is enough data in the FIFO to start reading it, when 0 then store and forward.
-- . Rx section full = There is enough data in the FIFO to start reading it, when 0 then store and forward.
-- . Tx section empty = There is not much empty space anymore in the FIFO, warn user via ff_tx_septy
-- . Rx section empty = There is not much empty space anymore in the FIFO, inform remote device via XOFF flow control
-- . Tx almost full = Assert ff_tx_a_full and deassert ff_tx_rdy. Furthermore TX_ALMOST_FULL = c_tx_ready_latency+3,
-- so choose 3 for zero tx ready latency
-- . Rx almost full = Assert ff_rx_a_full and if the user is not ready ff_rx_rdy then:
-- --> break off the reception with an error to avoid FIFO overflow
-- . Tx almost empty = Assert ff_tx_a_empty and if the FIFO does not contain a eop yet then:
-- --> break off the transmission with an error to avoid FIFO underflow
-- . Rx almost empty = Assert ff_rx_a_empty
-- Typical FIFO values:
-- . TX_SECTION_FULL = 16 > 8 = TX_ALMOST_EMPTY
-- . RX_SECTION_FULL = 16 > 8 = RX_ALMOST_EMPTY
-- . TX_SECTION_EMPTY = D-16 < D-3 = Tx FIFO depth - TX_ALMOST_FULL
-- . RX_SECTION_EMPTY = D-16 < D-8 = Rx FIFO depth - RX_ALMOST_FULL
-- . c_tse_tx_fifo_depth = 1 M9K = 256*32b = 1k * 8b is sufficient when the Tx user respects ff_tx_rdy, to store a complete
-- ETH packet would require 1518 byte, so 2 M9K = 2k * 8b
-- . c_tse_rx_fifo_depth = 1 M9K = 256*32b = 1k * 8b is sufficient when the Rx user ff_rx_rdy is sufficiently active
proc_wr_mac(16#01C#, c_tse_rx_fifo_depth-16, mm_clk, mm_miso, mm_mosi); -- RX_SECTION_EMPTY <-- default FIFO depth - 16, >3
proc_wr_mac(16#020#, 16, mm_clk, mm_miso, mm_mosi); -- RX_SECTION_FULL <-- default 16
proc_wr_mac(16#024#, c_tse_tx_fifo_depth-16, mm_clk, mm_miso, mm_mosi); -- TX_SECTION_EMPTY <-- default FIFO depth - 16, >3
proc_wr_mac(16#028#, 16, mm_clk, mm_miso, mm_mosi); -- TX_SECTION_FULL <-- default 16, >~ 8 otherwise no tx
proc_wr_mac(16#02C#, 8, mm_clk, mm_miso, mm_mosi); -- RX_ALMOST_EMPTY <-- default 8
proc_wr_mac(16#030#, 8, mm_clk, mm_miso, mm_mosi); -- RX_ALMOST_FULL <-- default 8
proc_wr_mac(16#034#, 8, mm_clk, mm_miso, mm_mosi); -- TX_ALMOST_EMPTY <-- default 8
proc_wr_mac(16#038#, c_tx_ready_latency+3, mm_clk, mm_miso, mm_mosi); -- TX_ALMOST_FULL <-- default 3
proc_rd_mac(16#0E8#, pcs_rddata, mm_clk, mm_miso, mm_mosi); -- TX_CMD_STAT --> 0x00040000 : [18]=1 TX_SHIFT16, [17]=0 OMIT_CRC
proc_rd_mac(16#0EC#, pcs_rddata, mm_clk, mm_miso, mm_mosi); -- RX_CMD_STAT --> 0x02000000 : [25]=1 RX_SHIFT16
WAIT UNTIL rising_edge(mm_clk);
mm_init <= '0';
WAIT;
END PROCESS;
p_tx_frame : PROCESS
BEGIN
-- . Avalon ST
ff_tx_src_out.data <= (OTHERS=>'0');
ff_tx_src_out.valid <= '0';
ff_tx_src_out.sop <= '0';
ff_tx_src_out.eop <= '0';
ff_tx_src_out.empty <= (OTHERS=>'0');
ff_tx_src_out.err <= (OTHERS=>'0');
-- . MAC specific
ff_tx_crc_fwd <= '0';
WHILE mm_init/='0' LOOP
WAIT UNTIL rising_edge(dp_clk);
END LOOP;
FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP;
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, "0000000000010000", 16, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 16, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 16, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1499, dp_clk, ff_tx_src_in, ff_tx_src_out); -- verify st empty
proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
-- proc_tx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, 1500, dp_clk, ff_tx_src_in, ff_tx_src_out);
FOR I IN 0 TO 1500 * 2 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP;
tb_end <= '1';
WAIT;
END PROCESS;
p_rx_frame : PROCESS
BEGIN
-- . Avalon ST
ff_rx_snk_out.ready <= '0';
WHILE mm_init/='0' LOOP
WAIT UNTIL rising_edge(dp_clk);
END LOOP;
-- Receive forever
WHILE TRUE LOOP
proc_rx_packet(c_eth_src_mac, c_eth_src_mac, c_eth_ethertype, dp_clk, ff_rx_snk_in, ff_rx_snk_out);
END LOOP;
WAIT;
END PROCESS;
dut : ENTITY work.ip_arria10_tse_sgmii_lvds
-- The ip_arria10_tse_sgmii_lvds needs to be regenerated if its parameters are changed.
-- . ENABLE_SHIFT16 = 1 : Align packet headers to 32 bit, useful for Nios data handling
-- . ENABLE_SUP_ADDR = 0 : An extra MAC addresses can e.g. be used as service MAC for tests
-- . ENA_HASH = 0 : A multi cast hash table can be used to address all nodes at once
-- . STAT_CNT_ENA = 0 : PHY statistics counts are useful for monitoring, but not realy needed
-- . EG_FIFO = 256 : Tx FIFO depth in nof 32 bit words (256 --> 1 M9K)
-- . ING_FIFO = 256 : Rx FIFO depth in nof 32 bit words (256 --> 1 M9K)
PORT MAP (
-- MAC transmit interface
-- . Avalon ST
ff_tx_clk => dp_clk,
ff_tx_rdy => ff_tx_src_in.ready,
ff_tx_data => ff_tx_src_out.data,
ff_tx_wren => ff_tx_src_out.valid,
ff_tx_sop => ff_tx_src_out.sop,
ff_tx_eop => ff_tx_src_out.eop,
ff_tx_mod => ff_tx_src_out.empty,
ff_tx_err => ff_tx_src_out.err(0),
-- . MAC specific
ff_tx_crc_fwd => ff_tx_crc_fwd, -- when '0' MAC inserts CRC32 after eop
ff_tx_septy => ff_tx_septy, -- when '0' then tx FIFO goes above section-empty threshold
ff_tx_a_full => ff_tx_a_full, -- when '1' then tx FIFO goes above almost-full threshold
ff_tx_a_empty => ff_tx_a_empty, -- when '1' then tx FIFO goes below almost-empty threshold
tx_ff_uflow => ff_tx_uflow, -- when '1' then tx FIFO underflow
-- MAC receive interface
-- . Avalon STs
ff_rx_clk => dp_clk,
ff_rx_rdy => ff_rx_snk_out.ready,
ff_rx_data => ff_rx_snk_in.data,
ff_rx_dval => ff_rx_snk_in.valid,
ff_rx_sop => ff_rx_snk_in.sop,
ff_rx_eop => ff_rx_snk_in.eop,
ff_rx_mod => ff_rx_snk_in.empty,
rx_err => ff_rx_snk_in.err, -- [5] collision error (can only occur in half duplex mode)
-- [4] PHY error on GMII
-- [3] receive frame truncated due to FIFO overflow
-- [2] CRC-32 error
-- [1] invalid length
-- [0] = OR of [1:5]
-- . MAC specific
rx_err_stat => ff_rx_ethertype, -- [17,16] VLAN info, [15:0] Ethernet lentgh/type field
rx_frm_type => ff_rx_frm_type, -- [3]=VLAN, [2]=Broadcast, [1]=Multicast, [0]=Unicast
ff_rx_dsav => ff_rx_dsav, -- rx frame available, but not necessarily a complete frame
ff_rx_a_full => ff_rx_a_full, -- when '1' then rx FIFO goes above almost-full threshold
ff_rx_a_empty => ff_rx_a_empty, -- when '1' sthen rx FIFO goes below almost-empty threshold
-- Reset
reset => mm_rst, -- asynchronous reset (choose synchronous to mm_clk)
-- MM control interface
clk => mm_clk,
address => mm_mosi.address(c_tse_byte_addr_w-1 DOWNTO 2),
readdata => mm_miso.rddata,
read => mm_mosi.rd,
writedata => mm_mosi.wrdata,
write => mm_mosi.wr,
waitrequest => mm_miso.waitreq,
-- Status LEDs
led_an => tse_led_an, -- '1' = autonegation completed
led_link => tse_led_link, -- '1' = successful link synchronisation
led_disp_err => OPEN, -- TBI character error
led_char_err => OPEN, -- TBI disparity errorreceived
-- Serial 1.25 Gbps
ref_clk => eth_clk,
txp => eth_txp,
rxp => eth_rxp
);
-- Loopback
eth_rxp <= eth_txp;
-- Verification
tx_pkt_cnt <= tx_pkt_cnt + 1 WHEN ff_tx_src_out.sop='1' AND rising_edge(dp_clk);
rx_pkt_cnt <= rx_pkt_cnt + 1 WHEN ff_rx_snk_in.eop='1' AND rising_edge(dp_clk);
p_tb_end : PROCESS
BEGIN
WAIT UNTIL tb_end='1';
-- Verify that all transmitted packets have been received
IF tx_pkt_cnt=0 THEN
REPORT "No packets were transmitted." SEVERITY ERROR;
ELSIF rx_pkt_cnt=0 THEN
REPORT "No packets were received." SEVERITY ERROR;
ELSIF tx_pkt_cnt/=rx_pkt_cnt THEN
REPORT "Not all transmitted packets were received." SEVERITY ERROR;
END IF;
-- Stop the simulation
ASSERT FALSE REPORT "Simulation finished." SEVERITY FAILURE;
WAIT;
END PROCESS;
END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment