From de699758722a30c534d0ce2107a2d9340046689a Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 23 Oct 2018 23:34:43 -0700 Subject: [PATCH] Add AXI stream XGMII RX and TX modules and testbenches --- rtl/axis_xgmii_rx_32.v | 415 ++++++++++++++++++++++ rtl/axis_xgmii_rx_64.v | 476 +++++++++++++++++++++++++ rtl/axis_xgmii_tx_32.v | 549 ++++++++++++++++++++++++++++ rtl/axis_xgmii_tx_64.v | 691 ++++++++++++++++++++++++++++++++++++ tb/test_axis_xgmii_rx_32.py | 364 +++++++++++++++++++ tb/test_axis_xgmii_rx_32.v | 92 +++++ tb/test_axis_xgmii_rx_64.py | 402 +++++++++++++++++++++ tb/test_axis_xgmii_rx_64.v | 92 +++++ tb/test_axis_xgmii_tx_32.py | 340 ++++++++++++++++++ tb/test_axis_xgmii_tx_32.v | 97 +++++ tb/test_axis_xgmii_tx_64.py | 340 ++++++++++++++++++ tb/test_axis_xgmii_tx_64.v | 97 +++++ 12 files changed, 3955 insertions(+) create mode 100644 rtl/axis_xgmii_rx_32.v create mode 100644 rtl/axis_xgmii_rx_64.v create mode 100644 rtl/axis_xgmii_tx_32.v create mode 100644 rtl/axis_xgmii_tx_64.v create mode 100755 tb/test_axis_xgmii_rx_32.py create mode 100644 tb/test_axis_xgmii_rx_32.v create mode 100755 tb/test_axis_xgmii_rx_64.py create mode 100644 tb/test_axis_xgmii_rx_64.v create mode 100755 tb/test_axis_xgmii_tx_32.py create mode 100644 tb/test_axis_xgmii_tx_32.v create mode 100755 tb/test_axis_xgmii_tx_64.py create mode 100644 tb/test_axis_xgmii_tx_64.v diff --git a/rtl/axis_xgmii_rx_32.v b/rtl/axis_xgmii_rx_32.v new file mode 100644 index 00000000..aa8f4a9c --- /dev/null +++ b/rtl/axis_xgmii_rx_32.v @@ -0,0 +1,415 @@ +/* + +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 XGMII frame receiver (XGMII in, AXI out) + */ +module axis_xgmii_rx_32 +( + input wire clk, + input wire rst, + + /* + * XGMII input + */ + input wire [31:0] xgmii_rxd, + input wire [3:0] xgmii_rxc, + + /* + * AXI output + */ + output wire [31:0] output_axis_tdata, + output wire [3:0] output_axis_tkeep, + output wire output_axis_tvalid, + output wire output_axis_tlast, + output wire output_axis_tuser, + + /* + * Status + */ + output wire error_bad_frame, + output wire error_bad_fcs +); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PREAMBLE = 3'd1, + STATE_PAYLOAD = 3'd2, + STATE_LAST = 3'd3; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [3:0] last_cycle_tkeep_reg = 4'd0, last_cycle_tkeep_next; + +reg [31:0] xgmii_rxd_d0 = 32'd0; +reg [31:0] xgmii_rxd_d1 = 32'd0; +reg [31:0] xgmii_rxd_d2 = 32'd0; + +reg [3:0] xgmii_rxc_d0 = 4'd0; +reg [3:0] xgmii_rxc_d1 = 4'd0; +reg [3:0] xgmii_rxc_d2 = 4'd0; + +reg [31:0] output_axis_tdata_reg = 32'd0, output_axis_tdata_next; +reg [3:0] output_axis_tkeep_reg = 4'd0, output_axis_tkeep_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_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; + +reg crc_valid0_save = 1'b0; +reg crc_valid1_save = 1'b0; +reg crc_valid2_save = 1'b0; +reg crc_valid3_save = 1'b0; + +assign output_axis_tdata = output_axis_tdata_reg; +assign output_axis_tkeep = output_axis_tkeep_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; + +wire last_cycle = state_reg == STATE_LAST; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(xgmii_rxd_d0[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(xgmii_rxd_d0[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(xgmii_rxd_d0[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(xgmii_rxd_d0[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +// detect control characters +reg [3:0] detect_start; +reg [3:0] detect_term; +reg [3:0] detect_error; + +reg [3:0] detect_term_save; + +integer i; + +always @* begin + for (i = 0; i < 4; i = i + 1) begin + detect_start[i] = xgmii_rxc_d0[i] && (xgmii_rxd_d0[i*8 +: 8] == 8'hfb); + detect_term[i] = xgmii_rxc_d0[i] && (xgmii_rxd_d0[i*8 +: 8] == 8'hfd); + detect_error[i] = xgmii_rxc_d0[i] && (xgmii_rxd_d0[i*8 +: 8] == 8'hfe); + end +end + +// mask errors to within packet +reg [3:0] detect_error_masked; +reg [3:0] control_masked; +reg [3:0] tkeep_mask; + +always @* begin + casez (detect_term) + 4'b0000: begin + detect_error_masked = detect_error; + control_masked = xgmii_rxc_d0; + tkeep_mask = 4'b1111; + end + 4'bzzz1: begin + detect_error_masked = 0; + control_masked = 0; + tkeep_mask = 4'b0000; + end + 4'bzz10: begin + detect_error_masked = detect_error[0]; + control_masked = xgmii_rxc_d0[0]; + tkeep_mask = 4'b0001; + end + 4'bz100: begin + detect_error_masked = detect_error[1:0]; + control_masked = xgmii_rxc_d0[1:0]; + tkeep_mask = 4'b0011; + end + 4'b1000: begin + detect_error_masked = detect_error[2:0]; + control_masked = xgmii_rxc_d0[2:0]; + tkeep_mask = 4'b0111; + end + default: begin + detect_error_masked = detect_error; + control_masked = xgmii_rxc_d0; + tkeep_mask = 4'b1111; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + output_axis_tdata_next = 32'd0; + output_axis_tkeep_next = 4'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; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (xgmii_rxc_d2[0] && xgmii_rxd_d2[7:0] == 8'hfb) begin + // start condition + if (detect_error_masked) begin + // error in first data word + output_axis_tdata_next = 32'd0; + output_axis_tkeep_next = 4'h1; + output_axis_tvalid_next = 1'b1; + output_axis_tlast_next = 1'b1; + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + update_crc = 1'b1; + state_next = STATE_PREAMBLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // drop preamble + update_crc = 1'b1; + state_next = STATE_PAYLOAD; + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + output_axis_tdata_next = xgmii_rxd_d2; + output_axis_tkeep_next = ~xgmii_rxc_d2; + output_axis_tvalid_next = 1'b1; + output_axis_tlast_next = 1'b0; + output_axis_tuser_next = 1'b0; + + if (control_masked) begin + // control or error characters in packet + output_axis_tlast_next = 1'b1; + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end else if (detect_term) begin + if (detect_term[0]) begin + // end this cycle + reset_crc = 1'b1; + output_axis_tkeep_next = 4'b1111; + output_axis_tlast_next = 1'b1; + if (detect_term[0] & crc_valid3_save) begin + // CRC valid + end else begin + 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 + // need extra cycle + last_cycle_tkeep_next = tkeep_mask; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle of packet + output_axis_tdata_next = xgmii_rxd_d2; + output_axis_tkeep_next = last_cycle_tkeep_reg; + output_axis_tvalid_next = 1'b1; + output_axis_tlast_next = 1'b1; + output_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + if ((detect_term_save[1] & crc_valid0_save) || + (detect_term_save[2] & crc_valid1_save) || + (detect_term_save[3] & crc_valid2_save)) begin + // CRC valid + end else begin + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + if (xgmii_rxc_d1[0] && xgmii_rxd_d1[7:0] == 8'hfb) begin + // start condition + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + endcase +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; + crc_valid0_save <= 1'b0; + crc_valid1_save <= 1'b0; + crc_valid2_save <= 1'b0; + crc_valid3_save <= 1'b0; + + xgmii_rxc_d0 <= 4'd0; + xgmii_rxc_d1 <= 4'd0; + 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; + + xgmii_rxc_d0 <= xgmii_rxc; + xgmii_rxc_d1 <= xgmii_rxc_d0; + xgmii_rxc_d2 <= xgmii_rxc_d1; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + crc_valid0_save <= 1'b0; + crc_valid1_save <= 1'b0; + crc_valid2_save <= 1'b0; + crc_valid3_save <= 1'b0; + end else if (update_crc) begin + crc_state <= crc_next3; + crc_valid0_save <= crc_valid0; + crc_valid1_save <= crc_valid1; + crc_valid2_save <= crc_valid2; + crc_valid3_save <= crc_valid3; + end + end + + output_axis_tdata_reg <= output_axis_tdata_next; + output_axis_tkeep_reg <= output_axis_tkeep_next; + output_axis_tlast_reg <= output_axis_tlast_next; + output_axis_tuser_reg <= output_axis_tuser_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + + detect_term_save <= detect_term; + + xgmii_rxd_d0 <= xgmii_rxd; + xgmii_rxd_d1 <= xgmii_rxd_d0; + xgmii_rxd_d2 <= xgmii_rxd_d1; +end + +endmodule diff --git a/rtl/axis_xgmii_rx_64.v b/rtl/axis_xgmii_rx_64.v new file mode 100644 index 00000000..dd4e138c --- /dev/null +++ b/rtl/axis_xgmii_rx_64.v @@ -0,0 +1,476 @@ +/* + +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 XGMII frame receiver (XGMII in, AXI out) + */ +module axis_xgmii_rx_64 +( + input wire clk, + input wire rst, + + /* + * XGMII input + */ + input wire [63:0] xgmii_rxd, + input wire [7:0] xgmii_rxc, + + /* + * AXI output + */ + output wire [63:0] output_axis_tdata, + output wire [7:0] output_axis_tkeep, + output wire output_axis_tvalid, + output wire output_axis_tlast, + output wire output_axis_tuser, + + /* + * Status + */ + output wire error_bad_frame, + output wire error_bad_fcs +); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_LAST = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [7:0] last_cycle_tkeep_reg = 8'd0, last_cycle_tkeep_next; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_rxd = 32'd0; +reg [3:0] swap_rxc = 4'd0; + +reg [63:0] xgmii_rxd_d0 = 32'd0; +reg [63:0] xgmii_rxd_d1 = 32'd0; + +reg [7:0] xgmii_rxc_d0 = 8'd0; +reg [7:0] xgmii_rxc_d1 = 8'd0; + +reg [63:0] output_axis_tdata_reg = 64'd0, output_axis_tdata_next; +reg [7:0] output_axis_tkeep_reg = 8'd0, output_axis_tkeep_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; +reg [31:0] crc_state3 = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next7; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; +wire crc_valid7 = crc_next7 == ~32'h2144df1c; + +reg crc_valid7_save = 1'b0; + +assign output_axis_tdata = output_axis_tdata_reg; +assign output_axis_tkeep = output_axis_tkeep_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; + +wire last_cycle = state_reg == STATE_LAST; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(xgmii_rxd_d0[7:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(xgmii_rxd_d0[15:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(xgmii_rxd_d0[23:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(xgmii_rxd_d0[31:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(xgmii_rxd_d0[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +// detect control characters +reg [7:0] detect_start; +reg [7:0] detect_term; +reg [7:0] detect_error; + +reg [7:0] detect_term_save = 8'd0; + +integer i; + +always @* begin + for (i = 0; i < 8; i = i + 1) begin + detect_start[i] = xgmii_rxc_d0[i] && (xgmii_rxd_d0[i*8 +: 8] == 8'hfb); + detect_term[i] = xgmii_rxc_d0[i] && (xgmii_rxd_d0[i*8 +: 8] == 8'hfd); + detect_error[i] = xgmii_rxc_d0[i] && (xgmii_rxd_d0[i*8 +: 8] == 8'hfe); + end +end + +// mask errors to within packet +reg [7:0] detect_error_masked; +reg [7:0] control_masked; +reg [7:0] tkeep_mask; + +always @* begin + casez (detect_term) + 8'b00000000: begin + detect_error_masked = detect_error; + control_masked = xgmii_rxc_d0; + tkeep_mask = 8'b11111111; + end + 8'bzzzzzzz1: begin + detect_error_masked = 0; + control_masked = 0; + tkeep_mask = 8'b00000000; + end + 8'bzzzzzz10: begin + detect_error_masked = detect_error[0]; + control_masked = xgmii_rxc_d0[0]; + tkeep_mask = 8'b00000001; + end + 8'bzzzzz100: begin + detect_error_masked = detect_error[1:0]; + control_masked = xgmii_rxc_d0[1:0]; + tkeep_mask = 8'b00000011; + end + 8'bzzzz1000: begin + detect_error_masked = detect_error[2:0]; + control_masked = xgmii_rxc_d0[2:0]; + tkeep_mask = 8'b00000111; + end + 8'bzzz10000: begin + detect_error_masked = detect_error[3:0]; + control_masked = xgmii_rxc_d0[3:0]; + tkeep_mask = 8'b00001111; + end + 8'bzz100000: begin + detect_error_masked = detect_error[4:0]; + control_masked = xgmii_rxc_d0[4:0]; + tkeep_mask = 8'b00011111; + end + 8'bz1000000: begin + detect_error_masked = detect_error[5:0]; + control_masked = xgmii_rxc_d0[5:0]; + tkeep_mask = 8'b00111111; + end + 8'b10000000: begin + detect_error_masked = detect_error[6:0]; + control_masked = xgmii_rxc_d0[6:0]; + tkeep_mask = 8'b01111111; + end + default: begin + detect_error_masked = detect_error; + control_masked = xgmii_rxc_d0; + tkeep_mask = 8'b11111111; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + output_axis_tdata_next = 64'd0; + output_axis_tkeep_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; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (xgmii_rxc_d1[0] && xgmii_rxd_d1[7:0] == 8'hfb) begin + // start condition + if (detect_error_masked) begin + // error in first data word + output_axis_tdata_next = 64'd0; + output_axis_tkeep_next = 8'h01; + output_axis_tvalid_next = 1'b1; + output_axis_tlast_next = 1'b1; + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + update_crc = 1'b1; + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + output_axis_tdata_next = xgmii_rxd_d1; + output_axis_tkeep_next = ~xgmii_rxc_d1; + output_axis_tvalid_next = 1'b1; + output_axis_tlast_next = 1'b0; + output_axis_tuser_next = 1'b0; + + if (control_masked) begin + // control or error characters in packet + output_axis_tlast_next = 1'b1; + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end else if (detect_term) begin + if (detect_term[4:0]) begin + // end this cycle + reset_crc = 1'b1; + output_axis_tkeep_next = {tkeep_mask[3:0], 4'b1111}; + output_axis_tlast_next = 1'b1; + if ((detect_term[0] & crc_valid7_save) || + (detect_term[1] & crc_valid0) || + (detect_term[2] & crc_valid1) || + (detect_term[3] & crc_valid2) || + (detect_term[4] & crc_valid3)) begin + // CRC valid + end else begin + 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 + // need extra cycle + last_cycle_tkeep_next = {4'b0000, tkeep_mask[7:4]}; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle of packet + output_axis_tdata_next = xgmii_rxd_d1; + output_axis_tkeep_next = last_cycle_tkeep_reg; + output_axis_tvalid_next = 1'b1; + output_axis_tlast_next = 1'b1; + output_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + if ((detect_term_save[5] & crc_valid0) || + (detect_term_save[6] & crc_valid1) || + (detect_term_save[7] & crc_valid2)) begin + // CRC valid + end else begin + output_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + if (xgmii_rxc_d1[0] && xgmii_rxd_d1[7:0] == 8'hfb) begin + // start condition + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + endcase +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; + crc_state3 <= 32'hFFFFFFFF; + crc_valid7_save <= 1'b0; + + xgmii_rxc_d0 <= 8'd0; + xgmii_rxc_d1 <= 8'd0; + + lanes_swapped <= 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; + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == 8'hfb) begin + lanes_swapped <= 1'b0; + xgmii_rxc_d0 <= xgmii_rxc; + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == 8'hfb) begin + lanes_swapped <= 1'b1; + xgmii_rxc_d0 <= {xgmii_rxc[3:0], swap_rxc}; + end else if (lanes_swapped) begin + xgmii_rxc_d0 <= {xgmii_rxc[3:0], swap_rxc}; + end else begin + xgmii_rxc_d0 <= xgmii_rxc; + end + + if (state_next == STATE_LAST) begin + xgmii_rxc_d0[3:0] <= xgmii_rxc_d0[7:4]; + end + + xgmii_rxc_d1 <= xgmii_rxc_d0; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + crc_valid7_save <= 1'b0; + end else if (update_crc) begin + crc_state <= crc_next7; + crc_state3 <= crc_next3; + crc_valid7_save <= crc_valid7; + end + end + + output_axis_tdata_reg <= output_axis_tdata_next; + output_axis_tkeep_reg <= output_axis_tkeep_next; + output_axis_tlast_reg <= output_axis_tlast_next; + output_axis_tuser_reg <= output_axis_tuser_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + + detect_term_save <= detect_term; + + swap_rxd <= xgmii_rxd[63:32]; + swap_rxc <= xgmii_rxc[7:4]; + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == 8'hfb) begin + xgmii_rxd_d0 <= xgmii_rxd; + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == 8'hfb) begin + xgmii_rxd_d0 <= {xgmii_rxd[31:0], swap_rxd}; + end else if (lanes_swapped) begin + xgmii_rxd_d0 <= {xgmii_rxd[31:0], swap_rxd}; + end else begin + xgmii_rxd_d0 <= xgmii_rxd; + end + + if (state_next == STATE_LAST) begin + xgmii_rxd_d0[31:0] <= xgmii_rxd_d0[63:32]; + end + + xgmii_rxd_d1 <= xgmii_rxd_d0; +end + +endmodule diff --git a/rtl/axis_xgmii_tx_32.v b/rtl/axis_xgmii_tx_32.v new file mode 100644 index 00000000..7761b858 --- /dev/null +++ b/rtl/axis_xgmii_tx_32.v @@ -0,0 +1,549 @@ +/* + +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 XGMII frame transmitter (AXI in, XGMII out) + */ +module axis_xgmii_tx_32 # +( + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [31:0] input_axis_tdata, + input wire [3:0] input_axis_tkeep, + input wire input_axis_tvalid, + output wire input_axis_tready, + input wire input_axis_tlast, + input wire input_axis_tuser, + + /* + * XGMII output + */ + output wire [31:0] xgmii_txd, + output wire [3:0] xgmii_txc, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfffc; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0003; + +localparam [3:0] + STATE_IDLE = 4'd0, + STATE_PREAMBLE = 4'd1, + STATE_PAYLOAD = 4'd2, + STATE_PAD = 4'd3, + STATE_FCS_1 = 4'd4, + STATE_FCS_2 = 4'd5, + STATE_FCS_3 = 4'd6, + STATE_IFG = 4'd7, + STATE_WAIT_END = 4'd8; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [31:0] input_axis_tdata_masked; + +reg [31:0] input_tdata_reg = 64'd0, input_tdata_next; +reg [3:0] input_tkeep_reg = 8'd0, input_tkeep_next; + +reg [31:0] fcs_output_txd_0; +reg [31:0] fcs_output_txd_1; +reg [3:0] fcs_output_txc_0; +reg [3:0] fcs_output_txc_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg input_axis_tready_reg = 1'b0, input_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; + +reg [31:0] xgmii_txd_reg = 32'h07070707, xgmii_txd_next; +reg [3:0] xgmii_txc_reg = 4'b1111, xgmii_txc_next; + +assign input_axis_tready = input_axis_tready_reg; + +assign xgmii_txd = xgmii_txd_reg; +assign xgmii_txc = xgmii_txc_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(input_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(input_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(input_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(input_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +function [2:0] keep2count; + input [3:0] k; + casez (k) + 4'bzzz0: keep2count = 3'd0; + 4'bzz01: keep2count = 3'd1; + 4'bz011: keep2count = 3'd2; + 4'b0111: keep2count = 3'd3; + 4'b1111: keep2count = 3'd4; + endcase +endfunction + +function [3:0] count2keep; + input [2:0] k; + case (k) + 3'd0: count2keep = 4'b0000; + 3'd1: count2keep = 4'b0001; + 3'd2: count2keep = 4'b0011; + 3'd3: count2keep = 4'b0111; + 3'd4: count2keep = 4'b1111; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 4; j = j + 1) begin + input_axis_tdata_masked[j*8 +: 8] = input_axis_tkeep[j] ? input_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (input_tkeep_reg) + 4'bzz01: begin + fcs_output_txd_0 = {~crc_next0[23:0], input_tdata_reg[7:0]}; + fcs_output_txd_1 = {24'h0707fd, ~crc_next0[31:24]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1110; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 4'bz011: begin + fcs_output_txd_0 = {~crc_next1[15:0], input_tdata_reg[15:0]}; + fcs_output_txd_1 = {16'h07fd, ~crc_next1[31:16]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1100; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 4'b0111: begin + fcs_output_txd_0 = {~crc_next2[7:0], input_tdata_reg[23:0]}; + fcs_output_txd_1 = {16'hfd, ~crc_next2[31:8]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1000; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 4'b1111: begin + fcs_output_txd_0 = input_tdata_reg; + fcs_output_txd_1 = ~crc_next3; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b0000; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_txd_0 = 32'd0; + fcs_output_txd_1 = 32'd0; + fcs_output_txc_0 = 4'd0; + fcs_output_txc_1 = 4'd0; + ifg_offset = 8'd0; + extra_cycle = 1'b0; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + input_axis_tready_next = 1'b0; + + input_tdata_next = input_tdata_reg; + input_tkeep_next = input_tkeep_reg; + + // XGMII idle + xgmii_txd_next = 32'h07070707; + xgmii_txc_next = 4'b1111; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd4; + reset_crc = 1'b1; + + // XGMII idle + xgmii_txd_next = 32'h07070707; + xgmii_txc_next = 4'b1111; + + input_tdata_next = input_axis_tdata_masked; + input_tkeep_next = input_axis_tkeep; + + if (input_axis_tvalid) begin + // XGMII start and preamble + xgmii_txd_next = 32'h555555fb; + xgmii_txc_next = 4'b0001; + input_axis_tready_next = 1'b1; + state_next = STATE_PREAMBLE; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // send preamble + + input_tdata_next = input_axis_tdata_masked; + input_tkeep_next = input_axis_tkeep; + + xgmii_txd_next = 32'hd5555555; + xgmii_txc_next = 4'b0000; + input_axis_tready_next = 1'b1; + state_next = STATE_PAYLOAD; + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + input_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd4; + + xgmii_txd_next = input_tdata_reg; + xgmii_txc_next = 4'b0000; + + input_tdata_next = input_axis_tdata_masked; + input_tkeep_next = input_axis_tkeep; + + if (input_axis_tvalid) begin + if (input_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(input_axis_tkeep); + input_axis_tready_next = 1'b0; + if (input_axis_tuser) begin + xgmii_txd_next = 32'h07fdfefe; + xgmii_txc_next = 4'b1111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd10; + state_next = STATE_IFG; + end else begin + input_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(input_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + input_tkeep_next = 4'hf; + frame_ptr_next = frame_ptr_reg + 16'd4; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-4)) begin + state_next = STATE_PAD; + end else begin + input_tkeep_next = 4'hf >> ((4-MIN_FL_NOCRC_LS) % 4); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + xgmii_txd_next = 32'h07fdfefe; + xgmii_txc_next = 4'b1111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd10; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + input_axis_tready_next = 1'b0; + + xgmii_txd_next = input_tdata_reg; + xgmii_txc_next = 4'b0000; + + input_tdata_next = 32'd0; + input_tkeep_next = 4'hf; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd4; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-4)) begin + state_next = STATE_PAD; + end else begin + input_tkeep_next = 4'hf >> ((4-MIN_FL_NOCRC_LS) % 4); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + input_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_0; + xgmii_txc_next = fcs_output_txc_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + deficit_idle_count_reg; + state_next = STATE_FCS_2; + end + STATE_FCS_2: begin + // last cycle + input_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_1; + xgmii_txc_next = fcs_output_txc_1; + + frame_ptr_next = 16'd0; + + if (extra_cycle) begin + state_next = STATE_FCS_3; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_3: begin + // last cycle + input_axis_tready_next = 1'b0; + + xgmii_txd_next = 32'h070707fd; + xgmii_txc_next = 4'b1111; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + input_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd4) begin + ifg_count_next = ifg_count_reg - 8'd4; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (input_axis_tvalid) begin + if (input_axis_tlast) begin + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + input_axis_tready_reg <= 1'b0; + + xgmii_txd_reg <= 32'h07070707; + xgmii_txc_reg <= 4'b1111; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + input_axis_tready_reg <= input_axis_tready_next; + + xgmii_txd_reg <= xgmii_txd_next; + xgmii_txc_reg <= xgmii_txc_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next3; + end + end + + input_tdata_reg <= input_tdata_next; + input_tkeep_reg <= input_tkeep_next; +end + +endmodule diff --git a/rtl/axis_xgmii_tx_64.v b/rtl/axis_xgmii_tx_64.v new file mode 100644 index 00000000..ce118dfa --- /dev/null +++ b/rtl/axis_xgmii_tx_64.v @@ -0,0 +1,691 @@ +/* + +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 XGMII frame transmitter (AXI in, XGMII out) + */ +module axis_xgmii_tx_64 # +( + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] input_axis_tdata, + input wire [7:0] input_axis_tkeep, + input wire input_axis_tvalid, + output wire input_axis_tready, + input wire input_axis_tlast, + input wire input_axis_tuser, + + /* + * XGMII output + */ + output wire [63:0] xgmii_txd, + output wire [7:0] xgmii_txc, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfff8; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0007; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_PAD = 3'd2, + STATE_FCS_1 = 3'd3, + STATE_FCS_2 = 3'd4, + STATE_IFG = 3'd5, + STATE_WAIT_END = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg swap_lanes; +reg unswap_lanes; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_txd = 32'd0; +reg [3:0] swap_txc = 4'd0; + +reg [63:0] input_axis_tdata_masked; + +reg [63:0] input_tdata_reg = 64'd0, input_tdata_next; +reg [7:0] input_tkeep_reg = 8'd0, input_tkeep_next; + +reg [63:0] fcs_output_txd_0; +reg [63:0] fcs_output_txd_1; +reg [7:0] fcs_output_txc_0; +reg [7:0] fcs_output_txc_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg input_axis_tready_reg = 1'b0, input_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +reg [63:0] xgmii_txd_reg = 64'h0707070707070707, xgmii_txd_next; +reg [7:0] xgmii_txc_reg = 8'b11111111, xgmii_txc_next; + +assign input_axis_tready = input_axis_tready_reg; + +assign xgmii_txd = xgmii_txd_reg; +assign xgmii_txc = xgmii_txc_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(input_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(input_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(input_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(input_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(input_tdata_reg[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(input_tdata_reg[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(input_tdata_reg[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(input_tdata_reg[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + input_axis_tdata_masked[j*8 +: 8] = input_axis_tkeep[j] ? input_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (input_tkeep_reg) + 8'bzzzzzz01: begin + fcs_output_txd_0 = {24'h0707fd, ~crc_next0[31:0], input_tdata_reg[7:0]}; + fcs_output_txd_1 = {63'h0707070707070707}; + fcs_output_txc_0 = 8'b11100000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 8'bzzzzz011: begin + fcs_output_txd_0 = {16'h07fd, ~crc_next1[31:0], input_tdata_reg[15:0]}; + fcs_output_txd_1 = {63'h0707070707070707}; + fcs_output_txc_0 = 8'b11000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 8'bzzzz0111: begin + fcs_output_txd_0 = {8'hfd, ~crc_next2[31:0], input_tdata_reg[23:0]}; + fcs_output_txd_1 = {63'h0707070707070707}; + fcs_output_txc_0 = 8'b10000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 8'bzzz01111: begin + fcs_output_txd_0 = {~crc_next3[31:0], input_tdata_reg[31:0]}; + fcs_output_txd_1 = {63'h07070707070707fd}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd8; + extra_cycle = 1'b1; + end + 8'bzz011111: begin + fcs_output_txd_0 = {~crc_next4[23:0], input_tdata_reg[39:0]}; + fcs_output_txd_1 = {56'h070707070707fd, ~crc_next4[31:24]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111110; + ifg_offset = 8'd7; + extra_cycle = 1'b1; + end + 8'bz0111111: begin + fcs_output_txd_0 = {~crc_next5[15:0], input_tdata_reg[47:0]}; + fcs_output_txd_1 = {48'h0707070707fd, ~crc_next5[31:16]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111100; + ifg_offset = 8'd6; + extra_cycle = 1'b1; + end + 8'b01111111: begin + fcs_output_txd_0 = {~crc_next6[7:0], input_tdata_reg[55:0]}; + fcs_output_txd_1 = {40'h07070707fd, ~crc_next6[31:8]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111000; + ifg_offset = 8'd5; + extra_cycle = 1'b1; + end + 8'b11111111: begin + fcs_output_txd_0 = input_tdata_reg; + fcs_output_txd_1 = {32'h070707fd, ~crc_next7[31:0]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11110000; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_txd_0 = 64'd0; + fcs_output_txd_1 = 64'd0; + fcs_output_txc_0 = 8'd0; + fcs_output_txc_1 = 8'd0; + ifg_offset = 8'd0; + extra_cycle = 1'b1; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + swap_lanes = 1'b0; + unswap_lanes = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + input_axis_tready_next = 1'b0; + + input_tdata_next = input_tdata_reg; + input_tkeep_next = input_tkeep_reg; + + // XGMII idle + xgmii_txd_next = 64'h0707070707070707; + xgmii_txc_next = 8'b11111111; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd8; + reset_crc = 1'b1; + input_axis_tready_next = 1'b1; + + // XGMII idle + xgmii_txd_next = 64'h0707070707070707; + xgmii_txc_next = 8'b11111111; + + input_tdata_next = input_axis_tdata_masked; + input_tkeep_next = input_axis_tkeep; + + if (input_axis_tvalid) begin + // XGMII start and preamble + if (ifg_count_reg > 8'd0) begin + // need to send more idles - swap lanes + swap_lanes = 1'b1; + end else begin + // no more idles - unswap + unswap_lanes = 1'b1; + end + xgmii_txd_next = 64'hd5555555555555fb; + xgmii_txc_next = 8'b00000001; + input_axis_tready_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + unswap_lanes = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + input_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd8; + + xgmii_txd_next = input_tdata_reg; + xgmii_txc_next = 8'b00000000; + + input_tdata_next = input_axis_tdata_masked; + input_tkeep_next = input_axis_tkeep; + + if (input_axis_tvalid) begin + if (input_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(input_axis_tkeep); + input_axis_tready_next = 1'b0; + if (input_axis_tuser) begin + xgmii_txd_next = 64'h070707fdfefefefe; + xgmii_txc_next = 8'b11111111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + state_next = STATE_IFG; + end else begin + input_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(input_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + input_tkeep_next = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + input_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + xgmii_txd_next = 64'h070707fdfefefefe; + xgmii_txc_next = 8'b11111111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + input_axis_tready_next = 1'b0; + + xgmii_txd_next = input_tdata_reg; + xgmii_txc_next = 8'b00000000; + + input_tdata_next = 64'd0; + input_tkeep_next = 8'hff; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + input_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + input_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_0; + xgmii_txc_next = fcs_output_txc_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + (lanes_swapped ? 8'd4 : 8'd0) + deficit_idle_count_reg; + if (extra_cycle) begin + state_next = STATE_FCS_2; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_2: begin + // last cycle + input_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_1; + xgmii_txc_next = fcs_output_txc_1; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + input_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + input_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + input_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + input_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (input_axis_tvalid) begin + if (input_axis_tlast) begin + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + input_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + input_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + input_axis_tready_reg <= 1'b0; + + xgmii_txd_reg <= 64'h0707070707070707; + xgmii_txc_reg <= 8'b11111111; + + crc_state <= 32'hFFFFFFFF; + + lanes_swapped <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + input_axis_tready_reg <= input_axis_tready_next; + + if (lanes_swapped) begin + if (unswap_lanes) begin + lanes_swapped <= 1'b0; + xgmii_txd_reg <= xgmii_txd_next; + xgmii_txc_reg <= xgmii_txc_next; + end else begin + xgmii_txd_reg <= {xgmii_txd_next[31:0], swap_txd}; + xgmii_txc_reg <= {xgmii_txc_next[3:0], swap_txc}; + end + end else begin + if (swap_lanes) begin + lanes_swapped <= 1'b1; + xgmii_txd_reg <= {xgmii_txd_next[31:0], swap_txd}; + xgmii_txc_reg <= {xgmii_txc_next[3:0], swap_txc}; + end else begin + xgmii_txd_reg <= xgmii_txd_next; + xgmii_txc_reg <= xgmii_txc_next; + end + end + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + input_tdata_reg <= input_tdata_next; + input_tkeep_reg <= input_tkeep_next; + + swap_txd <= xgmii_txd_next[63:32]; + swap_txc <= xgmii_txc_next[7:4]; +end + +endmodule diff --git a/tb/test_axis_xgmii_rx_32.py b/tb/test_axis_xgmii_rx_32.py new file mode 100755 index 00000000..9f340f70 --- /dev/null +++ b/tb/test_axis_xgmii_rx_32.py @@ -0,0 +1,364 @@ +#!/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 xgmii_ep + +module = 'axis_xgmii_rx_32' +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)[4:]) + + xgmii_rxd = Signal(intbv(0x07070707)[32:]) + xgmii_rxc = Signal(intbv(0xf)[4:]) + + # Outputs + output_axis_tdata = Signal(intbv(0)[32:]) + output_axis_tkeep = Signal(intbv(0)[4:]) + 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 = xgmii_ep.XGMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=output_axis_tdata, + tkeep=output_axis_tkeep, + 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, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + output_axis_tdata=output_axis_tdata, + output_axis_tkeep=output_axis_tkeep, + output_axis_tvalid=output_axis_tvalid, + output_axis_tlast=output_axis_tlast, + output_axis_tuser=output_axis_tuser, + + 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 + + @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 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() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(xgmii_frame) + + yield sink.wait() + 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() + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + + yield sink.wait() + 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 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + 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 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert rx_frame.last_cycle_user + + yield sink.wait() + 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) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 5: test stream, length %d" % payload_len) + current_test.next = 5 + + for i in range(10): + 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() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = 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) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_xgmii_rx_32.v b/tb/test_axis_xgmii_rx_32.v new file mode 100644 index 00000000..838fc41a --- /dev/null +++ b/tb/test_axis_xgmii_rx_32.v @@ -0,0 +1,92 @@ +/* + +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_xgmii_rx_32 + */ +module test_axis_xgmii_rx_32; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [3:0] current_test = 0; + +reg [31:0] xgmii_rxd = 32'h07070707; +reg [3:0] xgmii_rxc = 4'hf; + +// Outputs +wire [31:0] output_axis_tdata; +wire [3:0] output_axis_tkeep; +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, + xgmii_rxd, + xgmii_rxc + ); + $to_myhdl( + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tlast, + output_axis_tuser, + error_bad_frame, + error_bad_fcs + ); + + // dump file + $dumpfile("test_axis_xgmii_rx_32.lxt"); + $dumpvars(0, test_axis_xgmii_rx_32); +end + +axis_xgmii_rx_32 +UUT ( + .clk(clk), + .rst(rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .output_axis_tdata(output_axis_tdata), + .output_axis_tkeep(output_axis_tkeep), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tlast(output_axis_tlast), + .output_axis_tuser(output_axis_tuser), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs) +); + +endmodule diff --git a/tb/test_axis_xgmii_rx_64.py b/tb/test_axis_xgmii_rx_64.py new file mode 100755 index 00000000..c62c022f --- /dev/null +++ b/tb/test_axis_xgmii_rx_64.py @@ -0,0 +1,402 @@ +#!/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 xgmii_ep + +module = 'axis_xgmii_rx_64' +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:]) + + xgmii_rxd = Signal(intbv(0x0707070707070707)[64:]) + xgmii_rxc = Signal(intbv(0xff)[8:]) + + # Outputs + output_axis_tdata = Signal(intbv(0)[64:]) + output_axis_tkeep = 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 = xgmii_ep.XGMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=xgmii_rxd, + txc=xgmii_rxc, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=output_axis_tdata, + tkeep=output_axis_tkeep, + 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, + + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + + output_axis_tdata=output_axis_tdata, + output_axis_tkeep=output_axis_tkeep, + output_axis_tvalid=output_axis_tvalid, + output_axis_tlast=output_axis_tlast, + output_axis_tuser=output_axis_tuser, + + 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 + + @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 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() + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(xgmii_frame) + + yield sink.wait() + 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() + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame1 + + yield sink.wait() + 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 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert error_bad_fcs_asserted + + assert rx_frame.user[-1] + + yield sink.wait() + 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 + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame1)) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame2)) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert rx_frame.last_cycle_user + + yield sink.wait() + 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) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 5: test stream, length %d" % payload_len) + current_test.next = 5 + + for i in range(10): + 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() + + source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + for i in range(10): + yield sink.wait() + rx_frame = 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 6: Ensure 0xfb in FCS in lane 4 is not detected as start code in lane 0") + current_test.next = 6 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x806f + test_frame.payload = bytearray(range(60)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + error_bad_frame_asserted.next = 0 + error_bad_fcs_asserted.next = 0 + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert not error_bad_frame_asserted + assert not error_bad_fcs_asserted + + assert not rx_frame.last_cycle_user + + 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) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_xgmii_rx_64.v b/tb/test_axis_xgmii_rx_64.v new file mode 100644 index 00000000..e18adcb6 --- /dev/null +++ b/tb/test_axis_xgmii_rx_64.v @@ -0,0 +1,92 @@ +/* + +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_xgmii_rx_64 + */ +module test_axis_xgmii_rx_64; + +// Parameters + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [63:0] xgmii_rxd = 64'h0707070707070707; +reg [7:0] xgmii_rxc = 8'hff; + +// Outputs +wire [63:0] output_axis_tdata; +wire [7:0] output_axis_tkeep; +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, + xgmii_rxd, + xgmii_rxc + ); + $to_myhdl( + output_axis_tdata, + output_axis_tkeep, + output_axis_tvalid, + output_axis_tlast, + output_axis_tuser, + error_bad_frame, + error_bad_fcs + ); + + // dump file + $dumpfile("test_axis_xgmii_rx_64.lxt"); + $dumpvars(0, test_axis_xgmii_rx_64); +end + +axis_xgmii_rx_64 +UUT ( + .clk(clk), + .rst(rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .output_axis_tdata(output_axis_tdata), + .output_axis_tkeep(output_axis_tkeep), + .output_axis_tvalid(output_axis_tvalid), + .output_axis_tlast(output_axis_tlast), + .output_axis_tuser(output_axis_tuser), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs) +); + +endmodule diff --git a/tb/test_axis_xgmii_tx_32.py b/tb/test_axis_xgmii_tx_32.py new file mode 100755 index 00000000..241b2f6a --- /dev/null +++ b/tb/test_axis_xgmii_tx_32.py @@ -0,0 +1,340 @@ +#!/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 xgmii_ep + +module = 'axis_xgmii_tx_32' +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)[4:]) + + input_axis_tdata = Signal(intbv(0)[32:]) + input_axis_tkeep = Signal(intbv(0)[4:]) + input_axis_tvalid = Signal(bool(0)) + input_axis_tlast = Signal(bool(0)) + input_axis_tuser = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + input_axis_tready = Signal(bool(0)) + xgmii_txd = Signal(intbv(0x07070707)[32:]) + xgmii_txc = Signal(intbv(0xf)[4:]) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=input_axis_tdata, + tkeep=input_axis_tkeep, + tvalid=input_axis_tvalid, + tready=input_axis_tready, + tlast=input_axis_tlast, + tuser=input_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = xgmii_ep.XGMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + 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_tkeep=input_axis_tkeep, + input_axis_tvalid=input_axis_tvalid, + input_axis_tready=input_axis_tready, + input_axis_tlast=input_axis_tlast, + input_axis_tuser=input_axis_tuser, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @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 payload_len in list(range(1,18))+list(range(40,58)): + 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 sink.wait() + 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 sink.wait() + 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 + + yield sink.wait() + 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.last_cycle_user = 1 + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + 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 + + yield sink.wait() + 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) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 4: test stream, length %d" % payload_len) + current_test.next = 4 + + for i in range(10): + 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) + + for i in range(10): + yield sink.wait() + 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:]) + + 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 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_xgmii_tx_32.v b/tb/test_axis_xgmii_tx_32.v new file mode 100644 index 00000000..883a0ed6 --- /dev/null +++ b/tb/test_axis_xgmii_tx_32.v @@ -0,0 +1,97 @@ +/* + +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_xgmii_tx_32 + */ +module test_axis_xgmii_tx_32; + +// Parameters +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [3:0] current_test = 0; + +reg [31:0] input_axis_tdata = 0; +reg [3:0] input_axis_tkeep = 0; +reg input_axis_tvalid = 0; +reg input_axis_tlast = 0; +reg input_axis_tuser = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire input_axis_tready; +wire [31:0] xgmii_txd; +wire [3:0] xgmii_txc; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tlast, + input_axis_tuser, + ifg_delay + ); + $to_myhdl( + input_axis_tready, + xgmii_txd, + xgmii_txc + ); + + // dump file + $dumpfile("test_axis_xgmii_tx_32.lxt"); + $dumpvars(0, test_axis_xgmii_tx_32); +end + +axis_xgmii_tx_32 #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .clk(clk), + .rst(rst), + .input_axis_tdata(input_axis_tdata), + .input_axis_tkeep(input_axis_tkeep), + .input_axis_tvalid(input_axis_tvalid), + .input_axis_tready(input_axis_tready), + .input_axis_tlast(input_axis_tlast), + .input_axis_tuser(input_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/tb/test_axis_xgmii_tx_64.py b/tb/test_axis_xgmii_tx_64.py new file mode 100755 index 00000000..370336fb --- /dev/null +++ b/tb/test_axis_xgmii_tx_64.py @@ -0,0 +1,340 @@ +#!/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 xgmii_ep + +module = 'axis_xgmii_tx_64' +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)[64:]) + input_axis_tkeep = Signal(intbv(0)[8:]) + input_axis_tvalid = Signal(bool(0)) + input_axis_tlast = Signal(bool(0)) + input_axis_tuser = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + input_axis_tready = Signal(bool(0)) + xgmii_txd = Signal(intbv(0x0707070707070707)[64:]) + xgmii_txc = Signal(intbv(0xff)[8:]) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=input_axis_tdata, + tkeep=input_axis_tkeep, + tvalid=input_axis_tvalid, + tready=input_axis_tready, + tlast=input_axis_tlast, + tuser=input_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = xgmii_ep.XGMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=xgmii_txd, + rxc=xgmii_txc, + 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_tkeep=input_axis_tkeep, + input_axis_tvalid=input_axis_tvalid, + input_axis_tready=input_axis_tready, + input_axis_tlast=input_axis_tlast, + input_axis_tuser=input_axis_tuser, + + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @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 payload_len in list(range(1,18))+list(range(40,58)): + 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 sink.wait() + 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 sink.wait() + 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 + + yield sink.wait() + 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.last_cycle_user = 1 + + source.send(axis_frame1) + source.send(axis_frame2) + + yield sink.wait() + 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 + + yield sink.wait() + 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) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 4: test stream, length %d" % payload_len) + current_test.next = 4 + + for i in range(10): + 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) + + for i in range(10): + yield sink.wait() + 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:]) + + 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 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_axis_xgmii_tx_64.v b/tb/test_axis_xgmii_tx_64.v new file mode 100644 index 00000000..e7a48070 --- /dev/null +++ b/tb/test_axis_xgmii_tx_64.v @@ -0,0 +1,97 @@ +/* + +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_xgmii_tx_64 + */ +module test_axis_xgmii_tx_64; + +// Parameters +parameter ENABLE_PADDING = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [63:0] input_axis_tdata = 0; +reg [7:0] input_axis_tkeep = 0; +reg input_axis_tvalid = 0; +reg input_axis_tlast = 0; +reg input_axis_tuser = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire input_axis_tready; +wire [63:0] xgmii_txd; +wire [7:0] xgmii_txc; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + input_axis_tdata, + input_axis_tkeep, + input_axis_tvalid, + input_axis_tlast, + input_axis_tuser, + ifg_delay + ); + $to_myhdl( + input_axis_tready, + xgmii_txd, + xgmii_txc + ); + + // dump file + $dumpfile("test_axis_xgmii_tx_64.lxt"); + $dumpvars(0, test_axis_xgmii_tx_64); +end + +axis_xgmii_tx_64 #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .clk(clk), + .rst(rst), + .input_axis_tdata(input_axis_tdata), + .input_axis_tkeep(input_axis_tkeep), + .input_axis_tvalid(input_axis_tvalid), + .input_axis_tready(input_axis_tready), + .input_axis_tlast(input_axis_tlast), + .input_axis_tuser(input_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ifg_delay(ifg_delay) +); + +endmodule