From 817e7c2667c4713e31effe693243e934e79a799e Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Wed, 31 May 2017 16:11:20 -0700 Subject: [PATCH] Add AXI stream GMII RX and TX modules and testbenches --- rtl/axis_gmii_rx.v | 313 +++++++++++++++++++++++++++++ rtl/axis_gmii_tx.v | 376 +++++++++++++++++++++++++++++++++++ tb/test_axis_gmii_rx.py | 430 ++++++++++++++++++++++++++++++++++++++++ tb/test_axis_gmii_rx.v | 99 +++++++++ tb/test_axis_gmii_tx.py | 382 +++++++++++++++++++++++++++++++++++ tb/test_axis_gmii_tx.v | 103 ++++++++++ 6 files changed, 1703 insertions(+) create mode 100644 rtl/axis_gmii_rx.v create mode 100644 rtl/axis_gmii_tx.v create mode 100755 tb/test_axis_gmii_rx.py create mode 100644 tb/test_axis_gmii_rx.v create mode 100755 tb/test_axis_gmii_tx.py create mode 100644 tb/test_axis_gmii_tx.v diff --git a/rtl/axis_gmii_rx.v b/rtl/axis_gmii_rx.v new file mode 100644 index 00000000..7c4662b7 --- /dev/null +++ b/rtl/axis_gmii_rx.v @@ -0,0 +1,313 @@ +/* + +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 + +/* + * AXI4-Stream GMII frame receiver (GMII in, AXI out) + */ +module axis_gmii_rx +( + input wire clk, + input wire rst, + + /* + * GMII input + */ + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + + /* + * AXI output + */ + output wire [7:0] output_axis_tdata, + output wire output_axis_tvalid, + output wire output_axis_tlast, + output wire output_axis_tuser, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Status + */ + output wire error_bad_frame, + output wire error_bad_fcs +); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_WAIT_LAST = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg mii_odd = 1'b0; +reg mii_locked = 1'b0; + +reg [7:0] gmii_rxd_d0 = 8'd0; +reg [7:0] gmii_rxd_d1 = 8'd0; +reg [7:0] gmii_rxd_d2 = 8'd0; +reg [7:0] gmii_rxd_d3 = 8'd0; +reg [7:0] gmii_rxd_d4 = 8'd0; + +reg gmii_rx_dv_d0 = 1'b0; +reg gmii_rx_dv_d1 = 1'b0; +reg gmii_rx_dv_d2 = 1'b0; +reg gmii_rx_dv_d3 = 1'b0; +reg gmii_rx_dv_d4 = 1'b0; + +reg gmii_rx_er_d0 = 1'b0; +reg gmii_rx_er_d1 = 1'b0; +reg gmii_rx_er_d2 = 1'b0; +reg gmii_rx_er_d3 = 1'b0; +reg gmii_rx_er_d4 = 1'b0; + +reg [7:0] output_axis_tdata_reg = 8'd0, output_axis_tdata_next; +reg output_axis_tvalid_reg = 1'b0, output_axis_tvalid_next; +reg output_axis_tlast_reg = 1'b0, output_axis_tlast_next; +reg output_axis_tuser_reg = 1'b0, output_axis_tuser_next; + +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign output_axis_tdata = output_axis_tdata_reg; +assign output_axis_tvalid = output_axis_tvalid_reg; +assign output_axis_tlast = output_axis_tlast_reg; +assign output_axis_tuser = output_axis_tuser_reg; + +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .REVERSE(1), + .DATA_WIDTH(8), + .OUTPUT_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(gmii_rxd_d4), + .lfsr_in(crc_state), + .lfsr_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + output_axis_tdata_next = 8'd0; + output_axis_tvalid_next = 1'b0; + output_axis_tlast_next = 1'b0; + output_axis_tuser_next = 1'b0; + + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + if (!clk_enable) begin + // clock disabled - hold state + state_next = state_reg; + end else if (mii_select & ~mii_odd) begin + // MII even cycle - hold state + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (gmii_rx_dv_d4 && ~gmii_rx_er_d4 && gmii_rxd_d4 == 8'hD5) begin + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + output_axis_tdata_next = gmii_rxd_d4; + output_axis_tvalid_next = 1'b1; + + if (gmii_rx_dv_d4 & gmii_rx_er_d4) begin + // error + output_axis_tlast_next = 1'b1; + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else if (~gmii_rx_dv) begin + // end of packet + output_axis_tlast_next = 1'b1; + if (gmii_rx_er_d0 | gmii_rx_er_d1 | gmii_rx_er_d2 | gmii_rx_er_d3) begin + // error received in FCS bytes + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + end else if ({gmii_rxd_d0, gmii_rxd_d1, gmii_rxd_d2, gmii_rxd_d3} == ~crc_next) begin + // FCS good + output_axis_tuser_next = 1'b0; + end else begin + // FCS bad + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_WAIT_LAST: begin + // wait for end of packet + + if (~gmii_rx_dv) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + output_axis_tvalid_reg <= 1'b0; + + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + mii_locked <= 1'b0; + mii_odd <= 1'b0; + + gmii_rx_dv_d0 <= 1'b0; + gmii_rx_dv_d1 <= 1'b0; + gmii_rx_dv_d2 <= 1'b0; + gmii_rx_dv_d3 <= 1'b0; + gmii_rx_dv_d4 <= 1'b0; + end else begin + state_reg <= state_next; + + output_axis_tvalid_reg <= output_axis_tvalid_next; + + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + if (clk_enable) begin + if (mii_select) begin + mii_odd <= ~mii_odd; + + if (mii_locked) begin + mii_locked <= gmii_rx_dv; + end else if (gmii_rx_dv & {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == 8'hD5) begin + mii_locked <= 1'b1; + mii_odd <= 1'b1; + end + + if (mii_odd) begin + gmii_rx_dv_d0 <= gmii_rx_dv & gmii_rx_dv_d0; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + end else begin + gmii_rx_dv_d0 <= gmii_rx_dv; + end + end else begin + gmii_rx_dv_d0 <= gmii_rx_dv; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + end + end + end + + output_axis_tdata_reg <= output_axis_tdata_next; + output_axis_tlast_reg <= output_axis_tlast_next; + output_axis_tuser_reg <= output_axis_tuser_next; + + // delay input + if (clk_enable) begin + if (mii_select) begin + gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]}; + + if (mii_odd) begin + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end else begin + gmii_rx_er_d0 <= gmii_rx_er; + end + end else begin + gmii_rxd_d0 <= gmii_rxd; + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_er_d0 <= gmii_rx_er; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end + end +end + +endmodule diff --git a/rtl/axis_gmii_tx.v b/rtl/axis_gmii_tx.v new file mode 100644 index 00000000..680cc8f8 --- /dev/null +++ b/rtl/axis_gmii_tx.v @@ -0,0 +1,376 @@ +/* + +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 + +/* + * AXI4-Stream GMII frame transmitter (AXI in, GMII out) + */ +module axis_gmii_tx # +( + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] input_axis_tdata, + input wire input_axis_tvalid, + output wire input_axis_tready, + input wire input_axis_tlast, + input wire input_axis_tuser, + + /* + * GMII output + */ + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PREAMBLE = 3'd1, + STATE_PAYLOAD = 3'd2, + STATE_LAST = 3'd3, + STATE_PAD = 3'd4, + STATE_FCS = 3'd5, + STATE_WAIT_END = 3'd6, + STATE_IFG = 3'd7; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [7:0] input_tdata_reg = 8'd0, input_tdata_next; + +reg mii_odd_reg = 1'b0, mii_odd_next; +reg [3:0] mii_msn_reg = 4'b0, mii_msn_next; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] gmii_txd_reg = 8'd0, gmii_txd_next; +reg gmii_tx_en_reg = 1'b0, gmii_tx_en_next; +reg gmii_tx_er_reg = 1'b0, gmii_tx_er_next; + +reg input_axis_tready_reg = 1'b0, input_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign input_axis_tready = input_axis_tready_reg; + +assign gmii_txd = gmii_txd_reg; +assign gmii_tx_en = gmii_tx_en_reg; +assign gmii_tx_er = gmii_tx_er_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .REVERSE(1), + .DATA_WIDTH(8), + .OUTPUT_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(input_tdata_reg), + .lfsr_in(crc_state), + .lfsr_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + mii_odd_next = mii_odd_reg; + mii_msn_next = mii_msn_reg; + + frame_ptr_next = frame_ptr_reg; + + input_axis_tready_next = 1'b0; + + input_tdata_next = input_tdata_reg; + + gmii_txd_next = 8'd0; + gmii_tx_en_next = 1'b0; + gmii_tx_er_next = 1'b0; + + if (!clk_enable) begin + // clock disabled - hold state and outputs + gmii_txd_next = gmii_txd_reg; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else if (mii_select & mii_odd_reg) begin + // MII odd cycle - hold state, output MSN + mii_odd_next = 1'b0; + gmii_txd_next = {4'd0, mii_msn_reg}; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + mii_odd_next = 1'b0; + + if (input_axis_tvalid) begin + mii_odd_next = 1'b1; + frame_ptr_next = 16'd1; + gmii_txd_next = 8'h55; // Preamble + gmii_tx_en_next = 1'b1; + state_next = STATE_PREAMBLE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // send preamble + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = 8'h55; // Preamble + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg == 16'd6) begin + input_axis_tready_next = 1'b1; + input_tdata_next = input_axis_tdata; + state_next = STATE_PREAMBLE; + end else if (frame_ptr_reg == 16'd7) begin + // end of preamble; start payload + frame_ptr_next = 16'd0; + if (input_axis_tready_reg) begin + input_axis_tready_next = 1'b1; + input_tdata_next = input_axis_tdata; + end + gmii_txd_next = 8'hD5; // SFD + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_PREAMBLE; + end + end + STATE_PAYLOAD: begin + // send payload + + update_crc = 1'b1; + input_axis_tready_next = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = input_tdata_reg; + gmii_tx_en_next = 1'b1; + + input_tdata_next = input_axis_tdata; + + if (input_axis_tvalid) begin + if (input_axis_tlast) begin + input_axis_tready_next = ~input_axis_tready_reg; + if (input_axis_tuser) begin + gmii_tx_er_next = 1'b1; + frame_ptr_next = 1'b0; + state_next = STATE_IFG; + end else begin + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + gmii_tx_er_next = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_WAIT_END; + end + end + STATE_LAST: begin + // last payload word + + update_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = input_tdata_reg; + gmii_tx_en_next = 1'b1; + + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + input_tdata_next = 8'd0; + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_PAD: begin + // send padding + + update_crc = 1'b1; + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = 8'd0; + gmii_tx_en_next = 1'b1; + + input_tdata_next = 8'd0; + + if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_FCS: begin + // send FCS + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + case (frame_ptr_reg) + 2'd0: gmii_txd_next = ~crc_state[7:0]; + 2'd1: gmii_txd_next = ~crc_state[15:8]; + 2'd2: gmii_txd_next = ~crc_state[23:16]; + 2'd3: gmii_txd_next = ~crc_state[31:24]; + endcase + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg < 3) begin + state_next = STATE_FCS; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_IFG; + end + end + STATE_WAIT_END: begin + // wait for end of frame + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + input_axis_tready_next = 1'b1; + + if (input_axis_tvalid) begin + if (input_axis_tlast) begin + input_axis_tready_next = 1'b0; + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + STATE_IFG: begin + // send IFG + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + endcase + + if (mii_select) begin + mii_msn_next = gmii_txd_next[7:4]; + gmii_txd_next[7:4] = 4'd0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + input_axis_tready_reg <= 1'b0; + + gmii_tx_en_reg <= 1'b0; + gmii_tx_er_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + input_axis_tready_reg <= input_axis_tready_next; + + gmii_tx_en_reg <= gmii_tx_en_next; + gmii_tx_er_reg <= gmii_tx_er_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + end + + mii_odd_reg <= mii_odd_next; + mii_msn_reg <= mii_msn_next; + + input_tdata_reg <= input_tdata_next; + + gmii_txd_reg <= gmii_txd_next; +end + +endmodule diff --git a/tb/test_axis_gmii_rx.py b/tb/test_axis_gmii_rx.py new file mode 100755 index 00000000..bdfc3078 --- /dev/null +++ b/tb/test_axis_gmii_rx.py @@ -0,0 +1,430 @@ +#!/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 = 'axis_gmii_rx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + gmii_rxd = Signal(intbv(0)[8:]) + gmii_rx_dv = Signal(bool(0)) + gmii_rx_er = Signal(bool(0)) + clk_enable = Signal(bool(1)) + mii_select = Signal(bool(0)) + + # Outputs + output_axis_tdata = Signal(intbv(0)[8:]) + output_axis_tvalid = Signal(bool(0)) + output_axis_tlast = Signal(bool(0)) + output_axis_tuser = Signal(bool(0)) + error_bad_frame = Signal(bool(0)) + error_bad_fcs = Signal(bool(0)) + + # sources and sinks + source = gmii_ep.GMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=gmii_rxd, + tx_en=gmii_rx_dv, + tx_er=gmii_rx_er, + clk_enable=clk_enable, + mii_select=mii_select, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=output_axis_tdata, + tvalid=output_axis_tvalid, + tlast=output_axis_tlast, + tuser=output_axis_tuser, + name='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, + + gmii_rxd=gmii_rxd, + gmii_rx_dv=gmii_rx_dv, + gmii_rx_er=gmii_rx_er, + + output_axis_tdata=output_axis_tdata, + output_axis_tvalid=output_axis_tvalid, + output_axis_tlast=output_axis_tlast, + output_axis_tuser=output_axis_tuser, + + clk_enable=clk_enable, + mii_select=mii_select, + + error_bad_frame=error_bad_frame, + error_bad_fcs=error_bad_fcs + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + error_bad_frame_asserted = Signal(bool(0)) + error_bad_fcs_asserted = Signal(bool(0)) + + @always(clk.posedge) + def monitor(): + if (error_bad_frame): + error_bad_frame_asserted.next = 1 + if (error_bad_fcs): + error_bad_fcs_asserted.next = 1 + + clk_enable_rate = Signal(int(0)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + clk_enable.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + clk_enable.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for rate, mii in [(1, 0), (10, 0), (5, 1)]: + clk_enable_rate.next = rate + mii_select.next = mii + + yield delay(100) + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + 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(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + gmii_frame = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(gmii_frame) + yield clk.posedge + yield clk.posedge + + while not clk_enable or not gmii_rx_dv: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_rx_dv or output_axis_tvalid: + yield clk.posedge + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + gmii_frame1 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + gmii_frame2 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(gmii_frame1) + source.send(gmii_frame2) + yield clk.posedge + yield clk.posedge + + while not clk_enable or not gmii_rx_dv: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_rx_dv or output_axis_tvalid: + yield clk.posedge + + yield clk.posedge + + while not clk_enable or not gmii_rx_dv: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_rx_dv or output_axis_tvalid: + yield clk.posedge + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: truncated frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + axis_frame1.data = axis_frame1.data[:-1] + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + gmii_frame1 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + gmii_frame2 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(gmii_frame1) + source.send(gmii_frame2) + yield clk.posedge + yield clk.posedge + + while not clk_enable or not gmii_rx_dv: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_rx_dv or output_axis_tvalid: + yield clk.posedge + + yield clk.posedge + + while not clk_enable or not gmii_rx_dv: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_rx_dv or output_axis_tvalid: + yield clk.posedge + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert error_bad_frame_asserted + assert error_bad_fcs_asserted + + rx_frame = sink.recv() + + assert rx_frame.user[-1] + + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: errored frame, length %d" % payload_len) + current_test.next = 4 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis_fcs() + axis_frame2 = test_frame2.build_axis_fcs() + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + gmii_frame1 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + gmii_frame2 = gmii_ep.GMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + gmii_frame1.error = 1 + + source.send(gmii_frame1) + source.send(gmii_frame2) + yield clk.posedge + yield clk.posedge + + while not clk_enable or not gmii_rx_dv: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_rx_dv or output_axis_tvalid: + yield clk.posedge + + yield clk.posedge + + while not clk_enable or not gmii_rx_dv: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_rx_dv or output_axis_tvalid: + yield clk.posedge + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + assert error_bad_frame_asserted + assert not error_bad_fcs_asserted + + rx_frame = sink.recv() + + assert rx_frame.user[-1] + + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame2 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return dut, monitor, source_logic, sink_logic, clkgen, clk_enable_gen, check + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_gmii_rx.v b/tb/test_axis_gmii_rx.v new file mode 100644 index 00000000..5a15721f --- /dev/null +++ b/tb/test_axis_gmii_rx.v @@ -0,0 +1,99 @@ +/* + +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 axis_gmii_rx + */ +module test_axis_gmii_rx; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] gmii_rxd = 0; +reg gmii_rx_dv = 0; +reg gmii_rx_er = 0; + +reg clk_enable = 1; +reg mii_select = 0; + +// Outputs +wire [7:0] output_axis_tdata; +wire output_axis_tvalid; +wire output_axis_tlast; +wire output_axis_tuser; +wire error_bad_frame; +wire error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + gmii_rxd, + gmii_rx_dv, + gmii_rx_er, + clk_enable, + mii_select + ); + $to_myhdl( + output_axis_tdata, + output_axis_tvalid, + output_axis_tlast, + output_axis_tuser, + error_bad_frame, + error_bad_fcs + ); + + // dump file + $dumpfile("test_axis_gmii_rx.lxt"); + $dumpvars(0, test_axis_gmii_rx); +end + +axis_gmii_rx +UUT ( + .clk(clk), + .rst(rst), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .output_axis_tdata(output_axis_tdata), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tlast(output_axis_tlast), + .output_axis_tuser(output_axis_tuser), + .clk_enable(clk_enable), + .mii_select(mii_select), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs) +); + +endmodule diff --git a/tb/test_axis_gmii_tx.py b/tb/test_axis_gmii_tx.py new file mode 100755 index 00000000..d27c6c7c --- /dev/null +++ b/tb/test_axis_gmii_tx.py @@ -0,0 +1,382 @@ +#!/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 = 'axis_gmii_tx' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/lfsr.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ENABLE_PADDING = 1 + MIN_FRAME_LENGTH = 64 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + input_axis_tdata = Signal(intbv(0)[8:]) + input_axis_tvalid = Signal(bool(0)) + input_axis_tlast = Signal(bool(0)) + input_axis_tuser = Signal(bool(0)) + clk_enable = Signal(bool(1)) + mii_select = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + input_axis_tready = Signal(bool(0)) + gmii_txd = Signal(intbv(0)[8:]) + gmii_tx_en = Signal(bool(0)) + gmii_tx_er = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=input_axis_tdata, + tvalid=input_axis_tvalid, + tready=input_axis_tready, + tlast=input_axis_tlast, + tuser=input_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = gmii_ep.GMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=gmii_txd, + rx_dv=gmii_tx_en, + rx_er=gmii_tx_er, + clk_enable=clk_enable, + mii_select=mii_select, + name='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, + + input_axis_tdata=input_axis_tdata, + input_axis_tvalid=input_axis_tvalid, + input_axis_tready=input_axis_tready, + input_axis_tlast=input_axis_tlast, + input_axis_tuser=input_axis_tuser, + + gmii_txd=gmii_txd, + gmii_tx_en=gmii_tx_en, + gmii_tx_er=gmii_tx_er, + + clk_enable=clk_enable, + mii_select=mii_select, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + clk_enable_rate = Signal(int(1)) + clk_enable_div = Signal(int(0)) + + @always(clk.posedge) + def clk_enable_gen(): + if clk_enable_div.next > 0: + clk_enable.next = 0 + clk_enable_div.next = clk_enable_div - 1 + else: + clk_enable.next = 1 + clk_enable_div.next = clk_enable_rate - 1 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + for rate, mii in [(1, 0), (10, 0), (5, 1)]: + clk_enable_rate.next = rate + mii_select.next = mii + + yield delay(100) + + for payload_len in list(range(1,18))+list(range(64,82)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + 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(payload_len)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + source.send(axis_frame) + yield clk.posedge + yield clk.posedge + + while not clk_enable: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_tx_en or input_axis_tvalid: + yield clk.posedge + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = 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) == max(payload_len, 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 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: back-to-back packets, length %d" % payload_len) + current_test.next = 2 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + while not clk_enable: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_tx_en or input_axis_tvalid: + yield clk.posedge + + yield clk.posedge + + while not clk_enable: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_tx_en or input_axis_tvalid: + yield clk.posedge + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = 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) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame1.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame1.eth_src_mac + assert eth_frame.eth_type == test_frame1.eth_type + assert eth_frame.payload.data.index(test_frame1.payload.data) == 0 + + rx_frame = 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) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: tuser assert, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = eth_ep.EthFrame() + test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame1.eth_src_mac = 0x5A5152535455 + test_frame1.eth_type = 0x8000 + test_frame1.payload = bytearray(range(payload_len)) + test_frame1.update_fcs() + test_frame2 = eth_ep.EthFrame() + test_frame2.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame2.eth_src_mac = 0x5A5152535455 + test_frame2.eth_type = 0x8000 + test_frame2.payload = bytearray(range(payload_len)) + test_frame2.update_fcs() + + axis_frame1 = test_frame1.build_axis() + axis_frame2 = test_frame2.build_axis() + + axis_frame1.user = 1 + + source.send(axis_frame1) + source.send(axis_frame2) + yield clk.posedge + yield clk.posedge + + while not clk_enable: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_tx_en or input_axis_tvalid: + yield clk.posedge + + yield clk.posedge + + while not clk_enable: + yield clk.posedge + yield clk.posedge + + while not clk_enable or gmii_tx_en or input_axis_tvalid: + yield clk.posedge + + yield clk.posedge + yield clk.posedge + yield clk.posedge + + rx_frame = sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + assert rx_frame.error[-1] + + # bad packet + + rx_frame = 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) == max(payload_len, 46) + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame2.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame2.eth_src_mac + assert eth_frame.eth_type == test_frame2.eth_type + assert eth_frame.payload.data.index(test_frame2.payload.data) == 0 + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return dut, source_logic, sink_logic, clkgen, clk_enable_gen, check + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_gmii_tx.v b/tb/test_axis_gmii_tx.v new file mode 100644 index 00000000..b1dba252 --- /dev/null +++ b/tb/test_axis_gmii_tx.v @@ -0,0 +1,103 @@ +/* + +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 axis_gmii_tx + */ +module test_axis_gmii_tx; + +// Parameters +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [7:0] input_axis_tdata = 0; +reg input_axis_tvalid = 0; +reg input_axis_tlast = 0; +reg input_axis_tuser = 0; +reg clk_enable = 1; +reg mii_select = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire input_axis_tready; +wire [7:0] gmii_txd; +wire gmii_tx_en; +wire gmii_tx_er; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + input_axis_tdata, + input_axis_tvalid, + input_axis_tlast, + input_axis_tuser, + clk_enable, + mii_select, + ifg_delay + ); + $to_myhdl( + input_axis_tready, + gmii_txd, + gmii_tx_en, + gmii_tx_er + ); + + // dump file + $dumpfile("test_axis_gmii_tx.lxt"); + $dumpvars(0, test_axis_gmii_tx); +end + +axis_gmii_tx #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .clk(clk), + .rst(rst), + .input_axis_tdata(input_axis_tdata), + .input_axis_tvalid(input_axis_tvalid), + .input_axis_tready(input_axis_tready), + .input_axis_tlast(input_axis_tlast), + .input_axis_tuser(input_axis_tuser), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .clk_enable(clk_enable), + .mii_select(mii_select), + .ifg_delay(ifg_delay) +); + +endmodule