diff --git a/rtl/eth_mac_1g_gmii.v b/rtl/eth_mac_1g_gmii.v new file mode 100644 index 000000000..5c476b1cd --- /dev/null +++ b/rtl/eth_mac_1g_gmii.v @@ -0,0 +1,252 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC with GMII interface + */ +module eth_mac_1g_gmii # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire gtx_clk, + input wire gtx_rst, + output wire rx_clk, + output wire rx_rst, + output wire tx_clk, + output wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire gmii_rx_clk, + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + input wire mii_tx_clk, + output wire gmii_tx_clk, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Status + */ + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] mac_gmii_rxd; +wire mac_gmii_rx_dv; +wire mac_gmii_rx_er; +wire [7:0] mac_gmii_txd; +wire mac_gmii_tx_en; +wire mac_gmii_tx_er; + +reg [1:0] speed_reg = 2'b10; +wire mii_select; + +reg tx_mii_select_1 = 1'b0; +reg tx_mii_select_2 = 1'b0; +reg tx_mii_select_3 = 1'b0; + +always @(posedge tx_clk) begin + tx_mii_select_1 <= mii_select; + tx_mii_select_2 <= tx_mii_select_1; + tx_mii_select_3 <= tx_mii_select_2; +end + +reg rx_mii_select_1 = 1'b0; +reg rx_mii_select_2 = 1'b0; +reg rx_mii_select_3 = 1'b0; + +always @(posedge rx_clk) begin + rx_mii_select_1 <= mii_select; + rx_mii_select_2 <= rx_mii_select_1; + rx_mii_select_3 <= rx_mii_select_2; +end + +// PHY speed detection +reg [2:0] rx_prescale = 3'd0; + +always @(posedge rx_clk) begin + rx_prescale <= rx_prescale + 3'd1; +end + +reg rx_prescale_sync_1 = 1'b0; +reg rx_prescale_sync_2 = 1'b0; +reg rx_prescale_sync_3 = 1'b0; + +always @(posedge gtx_clk) begin + rx_prescale_sync_1 <= rx_prescale[2]; + rx_prescale_sync_2 <= rx_prescale_sync_1; + rx_prescale_sync_3 <= rx_prescale_sync_2; +end + +reg [6:0] rx_speed_count_1 = 0; +reg [1:0] rx_speed_count_2 = 0; + +always @(posedge gtx_clk) begin + if (gtx_rst) begin + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b10; + end else begin + rx_speed_count_1 <= rx_speed_count_1 + 1; + + if (rx_prescale_sync_2 ^ rx_prescale_sync_3) begin + rx_speed_count_2 <= rx_speed_count_2 + 1; + end + + if (&rx_speed_count_1) begin + // reference count overflow - 10M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b00; + end + + if (&rx_speed_count_2) begin + // prescaled count overflow - 100M or 1000M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + if (rx_speed_count_1[6:5]) begin + // large reference count - 100M + speed_reg <= 2'b01; + end else begin + // small reference count - 1000M + speed_reg <= 2'b10; + end + end + end +end + +assign speed = speed_reg; +assign mii_select = speed != 2'b10; + +gmii_phy_if #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE) +) +gmii_phy_if_inst ( + .clk(gtx_clk), + .rst(gtx_rst), + + .mac_gmii_rx_clk(rx_clk), + .mac_gmii_rx_rst(rx_rst), + .mac_gmii_rxd(mac_gmii_rxd), + .mac_gmii_rx_dv(mac_gmii_rx_dv), + .mac_gmii_rx_er(mac_gmii_rx_er), + .mac_gmii_tx_clk(tx_clk), + .mac_gmii_tx_rst(tx_rst), + .mac_gmii_txd(mac_gmii_txd), + .mac_gmii_tx_en(mac_gmii_tx_en), + .mac_gmii_tx_er(mac_gmii_tx_er), + + .phy_gmii_rx_clk(gmii_rx_clk), + .phy_gmii_rxd(gmii_rxd), + .phy_gmii_rx_dv(gmii_rx_dv), + .phy_gmii_rx_er(gmii_rx_er), + .phy_mii_tx_clk(mii_tx_clk), + .phy_gmii_tx_clk(gmii_tx_clk), + .phy_gmii_txd(gmii_txd), + .phy_gmii_tx_en(gmii_tx_en), + .phy_gmii_tx_er(gmii_tx_er), + + .mii_select(mii_select) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_gmii_rxd), + .gmii_rx_dv(mac_gmii_rx_dv), + .gmii_rx_er(mac_gmii_rx_er), + .gmii_txd(mac_gmii_txd), + .gmii_tx_en(mac_gmii_tx_en), + .gmii_tx_er(mac_gmii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(1'b1), + .rx_mii_select(rx_mii_select_3), + .tx_mii_select(tx_mii_select_3), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/rtl/eth_mac_1g_gmii_fifo.v b/rtl/eth_mac_1g_gmii_fifo.v new file mode 100644 index 000000000..e70690a8c --- /dev/null +++ b/rtl/eth_mac_1g_gmii_fifo.v @@ -0,0 +1,268 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC with GMII interface and TX and RX FIFOs + */ +module eth_mac_1g_gmii_fifo # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_ADDR_WIDTH = 12, + parameter RX_FIFO_ADDR_WIDTH = 12 +) +( + input wire gtx_clk, + input wire gtx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire gmii_rx_clk, + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + input wire mii_tx_clk, + output wire gmii_tx_clk, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Status + */ + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire tx_clk; +wire rx_clk; +wire tx_rst; +wire rx_rst; + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +wire [1:0] speed_int; + +reg [1:0] speed_sync_reg_1 = 2'b10; +reg [1:0] speed_sync_reg_2 = 2'b10; + +assign speed = speed_sync_reg_2; + +always @(posedge logic_clk) begin + speed_sync_reg_1 <= speed_int; + speed_sync_reg_2 <= speed_sync_reg_1; +end + +eth_mac_1g_gmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_gmii_inst ( + .gtx_clk(gtx_clk), + .gtx_rst(gtx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .gmii_rx_clk(gmii_rx_clk), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_tx_clk(gmii_tx_clk), + .mii_tx_clk(mii_tx_clk), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .speed(speed_int), + .ifg_delay(ifg_delay) +); + +axis_async_frame_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .DROP_WHEN_FULL(0) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .input_clk(logic_clk), + .input_axis_tdata(tx_axis_tdata), + .input_axis_tvalid(tx_axis_tvalid), + .input_axis_tready(tx_axis_tready), + .input_axis_tlast(tx_axis_tlast), + .input_axis_tuser(tx_axis_tuser), + // AXI output + .output_clk(tx_clk), + .output_axis_tdata(tx_fifo_axis_tdata), + .output_axis_tvalid(tx_fifo_axis_tvalid), + .output_axis_tready(tx_fifo_axis_tready), + .output_axis_tlast(tx_fifo_axis_tlast), + // Status + .input_status_overflow(tx_fifo_overflow), + .input_status_bad_frame(tx_fifo_bad_frame), + .input_status_good_frame(tx_fifo_good_frame), + .output_status_overflow(), + .output_status_bad_frame(), + .output_status_good_frame() +); + +assign tx_fifo_axis_tuser = 1'b0; + +axis_async_frame_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(8), + .DROP_WHEN_FULL(1) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .input_clk(rx_clk), + .input_axis_tdata(rx_fifo_axis_tdata), + .input_axis_tvalid(rx_fifo_axis_tvalid), + .input_axis_tready(), + .input_axis_tlast(rx_fifo_axis_tlast), + .input_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .output_clk(logic_clk), + .output_axis_tdata(rx_axis_tdata), + .output_axis_tvalid(rx_axis_tvalid), + .output_axis_tready(rx_axis_tready), + .output_axis_tlast(rx_axis_tlast), + // Status + .input_status_overflow(), + .input_status_bad_frame(), + .input_status_good_frame(), + .output_status_overflow(rx_fifo_overflow), + .output_status_bad_frame(rx_fifo_bad_frame), + .output_status_good_frame(rx_fifo_good_frame) +); + +assign rx_axis_tuser = 1'b0; + +endmodule diff --git a/rtl/gmii_phy_if.v b/rtl/gmii_phy_if.v index 6c2efb305..347c20ce1 100644 --- a/rtl/gmii_phy_if.v +++ b/rtl/gmii_phy_if.v @@ -68,10 +68,16 @@ module gmii_phy_if # input wire [7:0] phy_gmii_rxd, input wire phy_gmii_rx_dv, input wire phy_gmii_rx_er, + input wire phy_mii_tx_clk, output wire phy_gmii_tx_clk, output wire [7:0] phy_gmii_txd, output wire phy_gmii_tx_en, - output wire phy_gmii_tx_er + output wire phy_gmii_tx_er, + + /* + * Control + */ + input wire mii_select ); ssio_sdr_in # @@ -94,13 +100,27 @@ ssio_sdr_out # .WIDTH(10) ) tx_ssio_sdr_inst ( - .clk(clk), + .clk(mac_gmii_tx_clk), .input_d({mac_gmii_txd, mac_gmii_tx_en, mac_gmii_tx_er}), .output_clk(phy_gmii_tx_clk), .output_q({phy_gmii_txd, phy_gmii_tx_en, phy_gmii_tx_er}) ); -assign mac_gmii_tx_clk = clk; +generate + +if (TARGET == "XILINX") begin + BUFGMUX + gmii_bufgmux_inst ( + .I0(clk), + .I1(phy_mii_tx_clk), + .S(mii_select), + .O(mac_gmii_tx_clk) + ); +end else begin + assign mac_gmii_tx_clk = mii_select ? phy_mii_tx_clk : clk; +end + +endgenerate // reset sync reg [3:0] tx_rst_reg = 4'hf; diff --git a/tb/test_eth_mac_1g_gmii.py b/tb/test_eth_mac_1g_gmii.py new file mode 100755 index 000000000..2495eeab3 --- /dev/null +++ b/tb/test_eth_mac_1g_gmii.py @@ -0,0 +1,342 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import gmii_ep + +module = 'eth_mac_1g_gmii' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/gmii_phy_if.v") +srcs.append("../rtl/oddr.v") +srcs.append("../rtl/ssio_sdr_in.v") +srcs.append("../rtl/ssio_sdr_out.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + IODDR_STYLE = "IODDR2" + CLOCK_INPUT_STYLE = "BUFIO2" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + gtx_clk = Signal(bool(0)) + gtx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + gmii_rx_clk = Signal(bool(0)) + gmii_rxd = Signal(intbv(0)[8:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + mii_tx_clk = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + gmii_tx_clk = Signal(bool(0)) + gmii_txd = Signal(intbv(0)[8:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + speed = Signal(intbv(0)[2:]) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + mii_select = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + gmii_rx_clk, + rst, + txd=gmii_rxd, + tx_en=gmii_rx_dv, + tx_er=gmii_rx_er, + mii_select=mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + gmii_tx_clk, + rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + mii_select=mii_select, + name='gmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + tx_clk, + tx_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + gmii_rx_clk, + rx_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + gtx_clk=gtx_clk, + gtx_rst=gtx_rst, + + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + + tx_axis_tdata=tx_axis_tdata, + tx_axis_tvalid=tx_axis_tvalid, + tx_axis_tready=tx_axis_tready, + tx_axis_tlast=tx_axis_tlast, + tx_axis_tuser=tx_axis_tuser, + + rx_axis_tdata=rx_axis_tdata, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + gmii_rx_clk=gmii_rx_clk, + gmii_rxd=gmii_rxd, + gmii_rx_dv=gmii_rx_dv, + gmii_rx_er=gmii_rx_er, + + gmii_tx_clk=gmii_tx_clk, + mii_tx_clk=mii_tx_clk, + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + speed=speed, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + gtx_clk.next = not clk + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + gmii_rx_clk.next = not gmii_rx_clk + mii_tx_clk.next = not gmii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + gtx_rst.next = 1 + yield clk.posedge + rst.next = 0 + gtx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(4, 0), (20, 1), (200, 1)]: + rx_clk_hp.next = rate + mii_select.next = mii + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + while not (gmii_rx_dv or gmii_tx_en): + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + while gmii_rx_dv or gmii_tx_en or tx_axis_tvalid or rx_axis_tvalid: + yield gmii_rx_clk.posedge + + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + while not (gmii_rx_dv or gmii_tx_en): + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + while gmii_rx_dv or gmii_tx_en or tx_axis_tvalid or rx_axis_tvalid: + yield gmii_rx_clk.posedge + + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + rx_frame = gmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return dut, monitor, axis_source_logic, axis_sink_logic, gmii_source_logic, gmii_sink_logic, clkgen, rx_clk_gen, check + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_eth_mac_1g_gmii.v b/tb/test_eth_mac_1g_gmii.v new file mode 100644 index 000000000..22ec338c4 --- /dev/null +++ b/tb/test_eth_mac_1g_gmii.v @@ -0,0 +1,158 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Testbench for eth_mac_1g_gmii + */ +module test_eth_mac_1g_gmii; + +// Parameters +parameter TARGET = "SIM"; +parameter IODDR_STYLE = "IODDR2"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg gtx_clk = 0; +reg gtx_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg gmii_rx_clk = 0; +reg [7:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; +reg mii_tx_clk = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire rx_clk; +wire rx_rst; +wire tx_clk; +wire tx_rst; +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire gmii_tx_clk; +wire [7:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire [1:0] speed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gtx_clk, + gtx_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + gmii_rx_clk, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + mii_tx_clk, + ifg_delay + ); + $to_myhdl( + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + gmii_tx_clk, + gmii_txd, + gmii_tx_en, + gmii_tx_er, + rx_error_bad_frame, + rx_error_bad_fcs, + speed + ); + + // dump file + $dumpfile("test_eth_mac_1g_gmii.lxt"); + $dumpvars(0, test_eth_mac_1g_gmii); +end + +eth_mac_1g_gmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .gtx_clk(gtx_clk), + .gtx_rst(gtx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rx_clk(gmii_rx_clk), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_tx_clk(gmii_tx_clk), + .mii_tx_clk(mii_tx_clk), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .speed(speed), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/tb/test_eth_mac_1g_gmii_fifo.py b/tb/test_eth_mac_1g_gmii_fifo.py new file mode 100755 index 000000000..1f6af9b29 --- /dev/null +++ b/tb/test_eth_mac_1g_gmii_fifo.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +""" + +from myhdl import * +import os + +import axis_ep +import eth_ep +import gmii_ep + +module = 'eth_mac_1g_gmii_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("../rtl/axis_gmii_rx.v") +srcs.append("../rtl/axis_gmii_tx.v") +srcs.append("../rtl/eth_mac_1g.v") +srcs.append("../rtl/eth_mac_1g_gmii.v") +srcs.append("../rtl/gmii_phy_if.v") +srcs.append("../rtl/oddr.v") +srcs.append("../rtl/ssio_sdr_in.v") +srcs.append("../rtl/ssio_sdr_out.v") +srcs.append("../lib/axis/rtl/axis_async_frame_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + TARGET = "SIM" + IODDR_STYLE = "IODDR2" + CLOCK_INPUT_STYLE = "BUFIO2" + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + TX_FIFO_ADDR_WIDTH = 9 + RX_FIFO_ADDR_WIDTH = 9 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + gtx_clk = Signal(bool(0)) + gtx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[8:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + gmii_rx_clk = Signal(bool(0)) + gmii_rxd = Signal(intbv(0)[8:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + mii_tx_clk = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[8:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + gmii_tx_clk = Signal(bool(0)) + gmii_txd = Signal(intbv(0)[8:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + speed = Signal(intbv(0)[1:0]) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + axis_sink_pause = Signal(bool(0)) + + mii_select = Signal(bool(0)) + + gmii_source = gmii_ep.GMIISource() + + gmii_source_logic = gmii_source.create_logic( + gmii_rx_clk, + rst, + txd=gmii_rxd, + tx_en=gmii_rx_dv, + tx_er=gmii_rx_er, + mii_select=mii_select, + name='gmii_source' + ) + + gmii_sink = gmii_ep.GMIISink() + + gmii_sink_logic = gmii_sink.create_logic( + gmii_tx_clk, + rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + mii_select=mii_select, + name='gmii_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + pause=axis_sink_pause, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + + gtx_clk=gtx_clk, + gtx_rst=gtx_rst, + logic_clk=logic_clk, + logic_rst=logic_rst, + + tx_axis_tdata=tx_axis_tdata, + tx_axis_tvalid=tx_axis_tvalid, + tx_axis_tready=tx_axis_tready, + tx_axis_tlast=tx_axis_tlast, + tx_axis_tuser=tx_axis_tuser, + + rx_axis_tdata=rx_axis_tdata, + rx_axis_tready=rx_axis_tready, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + + gmii_rx_clk=gmii_rx_clk, + gmii_rxd=gmii_rxd, + gmii_rx_dv=gmii_rx_dv, + gmii_rx_er=gmii_rx_er, + + gmii_tx_clk=gmii_tx_clk, + mii_tx_clk=mii_tx_clk, + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + speed=speed, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + gtx_clk.next = not clk + logic_clk.next = not clk + + rx_clk_hp = Signal(int(4)) + + @instance + def rx_clk_gen(): + while True: + yield delay(int(rx_clk_hp)) + gmii_rx_clk.next = not gmii_rx_clk + mii_tx_clk.next = not gmii_rx_clk + + rx_error_bad_frame_asserted = Signal(bool(0)) + rx_error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (rx_error_bad_frame): + rx_error_bad_frame_asserted.next = 1 + if (rx_error_bad_fcs): + rx_error_bad_fcs_asserted.next = 1 + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + gtx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + gtx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(4, 0), (20, 1), (200, 1)]: + rx_clk_hp.next = rate + mii_select.next = mii + + yield delay(1000) + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + gmii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + while gmii_rx_dv: + yield gmii_rx_clk.posedge + + for k in range(10): + yield gmii_rx_clk.posedge + + while rx_axis_tvalid: + yield gmii_rx_clk.posedge + + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + while tx_axis_tvalid: + yield gmii_rx_clk.posedge + + for k in range(10): + yield gmii_rx_clk.posedge + + while gmii_tx_en: + yield gmii_rx_clk.posedge + + yield gmii_rx_clk.posedge + yield gmii_rx_clk.posedge + + rx_frame = gmii_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return dut, monitor, axis_source_logic, axis_sink_logic, gmii_source_logic, gmii_sink_logic, clkgen, rx_clk_gen, check + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_eth_mac_1g_gmii_fifo.v b/tb/test_eth_mac_1g_gmii_fifo.v new file mode 100644 index 000000000..49ceaf86a --- /dev/null +++ b/tb/test_eth_mac_1g_gmii_fifo.v @@ -0,0 +1,177 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Testbench for eth_mac_1g_gmii_fifo + */ +module test_eth_mac_1g_gmii_fifo; + +// Parameters +parameter TARGET = "SIM"; +parameter IODDR_STYLE = "IODDR2"; +parameter CLOCK_INPUT_STYLE = "BUFIO2"; +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter TX_FIFO_ADDR_WIDTH = 9; +parameter RX_FIFO_ADDR_WIDTH = 9; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg gtx_clk = 0; +reg gtx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [7:0] tx_axis_tdata = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg gmii_rx_clk = 0; +reg [7:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; +reg mii_tx_clk = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [7:0] rx_axis_tdata; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire gmii_tx_clk; +wire [7:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; +wire [1:0] speed; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gtx_clk, + gtx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + gmii_rx_clk, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + mii_tx_clk, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + gmii_tx_clk, + gmii_txd, + gmii_tx_en, + gmii_tx_er, + tx_fifo_overflow, + tx_fifo_bad_frame, + tx_fifo_good_frame, + rx_error_bad_frame, + rx_error_bad_fcs, + rx_fifo_overflow, + rx_fifo_bad_frame, + rx_fifo_good_frame, + speed + ); + + // dump file + $dumpfile("test_eth_mac_1g_gmii_fifo.lxt"); + $dumpvars(0, test_eth_mac_1g_gmii_fifo); +end + +eth_mac_1g_gmii_fifo #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH) +) +UUT ( + .gtx_clk(gtx_clk), + .gtx_rst(gtx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rx_clk(gmii_rx_clk), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_tx_clk(gmii_tx_clk), + .mii_tx_clk(mii_tx_clk), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .speed(speed), + .ifg_delay(ifg_delay) +); + +endmodule