diff --git a/rtl/axis_baser_rx_64.v b/rtl/axis_baser_rx_64.v new file mode 100644 index 000000000..8bc7072da --- /dev/null +++ b/rtl/axis_baser_rx_64.v @@ -0,0 +1,574 @@ +/* + +Copyright (c) 2019 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 10GBASE-R frame receiver (10GBASE-R in, AXI out) + */ +module axis_baser_rx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded input + */ + input wire [DATA_WIDTH-1:0] encoded_rx_data, + input wire [HDR_WIDTH-1:0] encoded_rx_hdr, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire start_packet_0, + output wire start_packet_4, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +localparam [3:0] + INPUT_TYPE_IDLE = 4'd0, + INPUT_TYPE_ERROR = 4'd1, + INPUT_TYPE_START_0 = 4'd2, + INPUT_TYPE_START_4 = 4'd3, + INPUT_TYPE_DATA = 4'd4, + INPUT_TYPE_TERM_0 = 4'd8, + INPUT_TYPE_TERM_1 = 4'd9, + INPUT_TYPE_TERM_2 = 4'd10, + INPUT_TYPE_TERM_3 = 4'd11, + INPUT_TYPE_TERM_4 = 4'd12, + INPUT_TYPE_TERM_5 = 4'd13, + INPUT_TYPE_TERM_6 = 4'd14, + INPUT_TYPE_TERM_7 = 4'd15; + +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 lanes_swapped = 1'b0; +reg [31:0] swap_data = 32'd0; + +reg delay_type_valid = 1'b0; +reg [3:0] delay_type = INPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] input_data_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] input_data_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] input_data_crc = {DATA_WIDTH{1'b0}}; + +reg [3:0] input_type_d0 = INPUT_TYPE_IDLE; +reg [3:0] input_type_d1 = INPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, m_axis_tkeep_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg start_packet_0_reg = 1'b0; +reg start_packet_4_reg = 1'b0; +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 m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +assign start_packet_0 = start_packet_0_reg; +assign start_packet_4 = start_packet_4_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(input_data_crc[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(input_data_crc[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(input_data_crc[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(input_data_crc[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(input_data_d0[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'd0; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_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 (input_type_d1 == INPUT_TYPE_START_0) begin + // start condition + reset_crc = 1'b0; + update_crc = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'hff; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + if (input_type_d0 == INPUT_TYPE_DATA) begin + state_next = STATE_PAYLOAD; + end else if (input_type_d0[3]) begin + // INPUT_TYPE_TERM_* + if (input_type_d0 <= INPUT_TYPE_TERM_4) begin + // end this cycle + reset_crc = 1'b1; + case (input_type_d0) + INPUT_TYPE_TERM_0: m_axis_tkeep_next = 8'b00001111; + INPUT_TYPE_TERM_1: m_axis_tkeep_next = 8'b00011111; + INPUT_TYPE_TERM_2: m_axis_tkeep_next = 8'b00111111; + INPUT_TYPE_TERM_3: m_axis_tkeep_next = 8'b01111111; + INPUT_TYPE_TERM_4: m_axis_tkeep_next = 8'b11111111; + endcase + m_axis_tlast_next = 1'b1; + if ((input_type_d0 == INPUT_TYPE_TERM_0 && crc_valid7_save) || + (input_type_d0 == INPUT_TYPE_TERM_1 && crc_valid0) || + (input_type_d0 == INPUT_TYPE_TERM_2 && crc_valid1) || + (input_type_d0 == INPUT_TYPE_TERM_3 && crc_valid2) || + (input_type_d0 == INPUT_TYPE_TERM_4 && crc_valid3)) begin + // CRC valid + end else begin + m_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 + state_next = STATE_LAST; + end + end else begin + // control or error characters in packet + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_LAST: begin + // last cycle of packet + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'hff; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + case (input_type_d1) + INPUT_TYPE_TERM_5: m_axis_tkeep_next = 8'b00000001; + INPUT_TYPE_TERM_6: m_axis_tkeep_next = 8'b00000011; + INPUT_TYPE_TERM_7: m_axis_tkeep_next = 8'b00000111; + endcase + + if ((input_type_d1 == INPUT_TYPE_TERM_5 && crc_valid0) || + (input_type_d1 == INPUT_TYPE_TERM_6 && crc_valid1) || + (input_type_d1 == INPUT_TYPE_TERM_7 && crc_valid2)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_0_reg <= 1'b0; + start_packet_4_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; + + input_type_d0 <= INPUT_TYPE_IDLE; + input_type_d1 <= INPUT_TYPE_IDLE; + + lanes_swapped <= 1'b0; + + delay_type_valid <= 1'b0; + delay_type <= INPUT_TYPE_IDLE; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_0_reg <= 1'b0; + start_packet_4_reg <= 1'b0; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + delay_type_valid <= 1'b0; + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + lanes_swapped <= 1'b0; + start_packet_0_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_START_0; + end else if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_4)begin + lanes_swapped <= 1'b1; + start_packet_4_reg <= 1'b1; + delay_type_valid <= 1'b1; + if (delay_type_valid) begin + input_type_d0 <= delay_type; + end else begin + input_type_d0 <= INPUT_TYPE_IDLE; + end + end else if (lanes_swapped) begin + if (delay_type_valid) begin + input_type_d0 <= delay_type; + end else if (encoded_rx_hdr == SYNC_DATA) begin + input_type_d0 <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_TERM_0: input_type_d0 <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_1: input_type_d0 <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_2: input_type_d0 <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_3: input_type_d0 <= INPUT_TYPE_TERM_7; + BLOCK_TYPE_TERM_4: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_5: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_6: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_7: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + default: input_type_d0 <= INPUT_TYPE_ERROR; + endcase + end else begin + input_type_d0 <= INPUT_TYPE_ERROR; + end + end else begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_type_d0 <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_TERM_0: input_type_d0 <= INPUT_TYPE_TERM_0; + BLOCK_TYPE_TERM_1: input_type_d0 <= INPUT_TYPE_TERM_1; + BLOCK_TYPE_TERM_2: input_type_d0 <= INPUT_TYPE_TERM_2; + BLOCK_TYPE_TERM_3: input_type_d0 <= INPUT_TYPE_TERM_3; + BLOCK_TYPE_TERM_4: input_type_d0 <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_5: input_type_d0 <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_6: input_type_d0 <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_7: input_type_d0 <= INPUT_TYPE_TERM_7; + default: input_type_d0 <= INPUT_TYPE_ERROR; + endcase + end else begin + input_type_d0 <= INPUT_TYPE_ERROR; + end + end + + input_type_d1 <= input_type_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 + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tkeep_reg <= m_axis_tkeep_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + if (encoded_rx_hdr == SYNC_DATA) begin + swap_data <= encoded_rx_data[63:32]; + end else begin + swap_data <= {8'd0, encoded_rx_data[63:40]}; + end + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + input_data_d0 <= encoded_rx_data; + input_data_crc <= encoded_rx_data; + end else if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_4)begin + input_data_d0 <= {encoded_rx_data[31:0], swap_data}; + input_data_crc <= {encoded_rx_data[31:0], swap_data}; + end else if (lanes_swapped) begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_data_d0 <= {encoded_rx_data[31:0], swap_data}; + input_data_crc <= {encoded_rx_data[31:0], swap_data}; + end else begin + input_data_d0 <= {encoded_rx_data[39:8], swap_data}; + input_data_crc <= {encoded_rx_data[39:8], swap_data}; + end + end else begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_data_d0 <= encoded_rx_data; + input_data_crc <= encoded_rx_data; + end else begin + input_data_d0 <= {8'd0, encoded_rx_data[63:8]}; + input_data_crc <= {8'd0, encoded_rx_data[63:8]}; + end + end + + if (state_next == STATE_LAST) begin + input_data_crc[31:0] <= input_data_crc[63:32]; + end + + input_data_d1 <= input_data_d0; + + if (encoded_rx_hdr == SYNC_DATA) begin + delay_type <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_START_4: delay_type <= INPUT_TYPE_START_0; + BLOCK_TYPE_TERM_0: delay_type <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_1: delay_type <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_2: delay_type <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_3: delay_type <= INPUT_TYPE_TERM_7; + BLOCK_TYPE_TERM_4: delay_type <= INPUT_TYPE_TERM_0; + BLOCK_TYPE_TERM_5: delay_type <= INPUT_TYPE_TERM_1; + BLOCK_TYPE_TERM_6: delay_type <= INPUT_TYPE_TERM_2; + BLOCK_TYPE_TERM_7: delay_type <= INPUT_TYPE_TERM_3; + default: delay_type <= INPUT_TYPE_ERROR; + endcase + end else begin + delay_type <= INPUT_TYPE_ERROR; + end +end + +endmodule diff --git a/rtl/axis_baser_tx_64.v b/rtl/axis_baser_tx_64.v new file mode 100644 index 000000000..2a4d987cc --- /dev/null +++ b/rtl/axis_baser_tx_64.v @@ -0,0 +1,848 @@ +/* + +Copyright (c) 2019 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 10GBASE-R frame transmitter (AXI in, 10GBASE-R out) + */ +module axis_baser_tx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * 10GBASE-R encoded interface + */ + output wire [DATA_WIDTH-1:0] encoded_tx_data, + output wire [HDR_WIDTH-1:0] encoded_tx_hdr, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire start_packet_0, + output wire start_packet_4 +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +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 [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +localparam [3:0] + OUTPUT_TYPE_IDLE = 4'd0, + OUTPUT_TYPE_ERROR = 4'd1, + OUTPUT_TYPE_START_0 = 4'd2, + OUTPUT_TYPE_START_4 = 4'd3, + OUTPUT_TYPE_DATA = 4'd4, + OUTPUT_TYPE_TERM_0 = 4'd8, + OUTPUT_TYPE_TERM_1 = 4'd9, + OUTPUT_TYPE_TERM_2 = 4'd10, + OUTPUT_TYPE_TERM_3 = 4'd11, + OUTPUT_TYPE_TERM_4 = 4'd12, + OUTPUT_TYPE_TERM_5 = 4'd13, + OUTPUT_TYPE_TERM_6 = 4'd14, + OUTPUT_TYPE_TERM_7 = 4'd15; + +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_data = 32'd0; + +reg delay_type_valid = 1'b0; +reg [3:0] delay_type = OUTPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] s_tdata_reg = {DATA_WIDTH{1'b0}}, s_tdata_next; +reg [7:0] s_tkeep_reg = 8'd0, s_tkeep_next; + +reg [DATA_WIDTH-1:0] fcs_output_data_0; +reg [DATA_WIDTH-1:0] fcs_output_data_1; +reg [3:0] fcs_output_type_0; +reg [3:0] fcs_output_type_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 s_axis_tready_reg = 1'b0, s_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 [DATA_WIDTH-1:0] encoded_tx_data_reg = {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; +reg [HDR_WIDTH-1:0] encoded_tx_hdr_reg = SYNC_CTRL; + +reg [DATA_WIDTH-1:0] output_data_reg = {DATA_WIDTH{1'b0}}, output_data_next; +reg [3:0] output_type_reg = OUTPUT_TYPE_IDLE, output_type_next; + +reg start_packet_0_reg = 1'b0, start_packet_0_next; +reg start_packet_4_reg = 1'b0, start_packet_4_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign encoded_tx_data = encoded_tx_data_reg; +assign encoded_tx_hdr = encoded_tx_hdr_reg; + +assign start_packet_0 = start_packet_0_reg; +assign start_packet_4 = start_packet_4_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(s_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(s_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(s_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(s_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(s_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(s_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(s_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(s_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 + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (s_tkeep_reg) + 8'bzzzzzz01: begin + fcs_output_data_0 = {24'd0, ~crc_next0[31:0], s_tdata_reg[7:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_5; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 8'bzzzzz011: begin + fcs_output_data_0 = {16'd0, ~crc_next1[31:0], s_tdata_reg[15:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_6; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 8'bzzzz0111: begin + fcs_output_data_0 = {8'd0, ~crc_next2[31:0], s_tdata_reg[23:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_7; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 8'bzzz01111: begin + fcs_output_data_0 = {~crc_next3[31:0], s_tdata_reg[31:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_0; + ifg_offset = 8'd8; + extra_cycle = 1'b1; + end + 8'bzz011111: begin + fcs_output_data_0 = {~crc_next4[23:0], s_tdata_reg[39:0]}; + fcs_output_data_1 = {56'd0, ~crc_next4[31:24]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_1; + ifg_offset = 8'd7; + extra_cycle = 1'b1; + end + 8'bz0111111: begin + fcs_output_data_0 = {~crc_next5[15:0], s_tdata_reg[47:0]}; + fcs_output_data_1 = {48'd0, ~crc_next5[31:16]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_2; + ifg_offset = 8'd6; + extra_cycle = 1'b1; + end + 8'b01111111: begin + fcs_output_data_0 = {~crc_next6[7:0], s_tdata_reg[55:0]}; + fcs_output_data_1 = {40'd0, ~crc_next6[31:8]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_3; + ifg_offset = 8'd5; + extra_cycle = 1'b1; + end + 8'b11111111: begin + fcs_output_data_0 = s_tdata_reg; + fcs_output_data_1 = {32'd0, ~crc_next7[31:0]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_4; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_data_0 = 64'd0; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_ERROR; + fcs_output_type_1 = OUTPUT_TYPE_ERROR; + 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; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + s_tkeep_next = s_tkeep_reg; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_IDLE; + + start_packet_0_next = 1'b0; + start_packet_4_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd8; + reset_crc = 1'b1; + s_axis_tready_next = 1'b1; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_IDLE; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_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; + start_packet_4_next = 1'b1; + end else begin + // no more idles - unswap + unswap_lanes = 1'b1; + start_packet_0_next = 1'b1; + end + output_data_next = {ETH_SFD, {7{ETH_PRE}}}; + output_type_next = OUTPUT_TYPE_START_0; + s_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; + s_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd8; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_DATA; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + s_axis_tready_next = 1'b0; + if (s_axis_tuser) begin + output_type_next = OUTPUT_TYPE_ERROR; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(s_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + s_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 + s_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 framec + output_type_next = OUTPUT_TYPE_ERROR; + 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 + s_axis_tready_next = 1'b0; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_DATA; + + s_tdata_next = 64'd0; + s_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 + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + s_axis_tready_next = 1'b0; + + output_data_next = fcs_output_data_0; + output_type_next = fcs_output_type_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 + s_axis_tready_next = 1'b0; + + output_data_next = fcs_output_data_1; + output_type_next = fcs_output_type_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 + s_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 + s_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 + s_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 + s_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 (s_axis_tvalid) begin + if (s_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 + s_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 + s_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; + + s_axis_tready_reg <= 1'b0; + + encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + + output_data_reg <= {DATA_WIDTH{1'b0}}; + output_type_reg <= OUTPUT_TYPE_IDLE; + + start_packet_0_reg <= 1'b0; + start_packet_4_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + lanes_swapped <= 1'b0; + + delay_type_valid <= 1'b0; + delay_type <= OUTPUT_TYPE_IDLE; + 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; + + s_axis_tready_reg <= s_axis_tready_next; + + start_packet_0_reg <= start_packet_0_next; + start_packet_4_reg <= start_packet_4_next; + + delay_type_valid <= 1'b0; + + if (swap_lanes || (lanes_swapped && !unswap_lanes)) begin + lanes_swapped <= 1'b1; + output_data_reg <= {output_data_next[31:0], swap_data}; + if (delay_type_valid) begin + output_type_reg <= delay_type; + end else if (output_type_next == OUTPUT_TYPE_START_0) begin + output_type_reg <= OUTPUT_TYPE_START_4; + end else if (output_type_next[3]) begin + // OUTPUT_TYPE_TERM_* + if (output_type_next[2]) begin + delay_type_valid <= 1'b1; + output_type_reg <= OUTPUT_TYPE_DATA; + end else begin + output_type_reg <= output_type_next ^ 4'd4; + end + end else begin + output_type_reg <= output_type_next; + end + end else begin + lanes_swapped <= 1'b0; + output_data_reg <= output_data_next; + output_type_reg <= output_type_next; + end + + case (output_type_reg) + OUTPUT_TYPE_IDLE: begin + encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_ERROR: begin + encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_START_0: begin + encoded_tx_data_reg <= {output_data_reg[63:8], BLOCK_TYPE_START_0}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_START_4: begin + encoded_tx_data_reg <= {output_data_reg[63:40], 4'd0, {4{CTRL_IDLE}}, BLOCK_TYPE_START_4}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_DATA: begin + encoded_tx_data_reg <= output_data_reg; + encoded_tx_hdr_reg <= SYNC_DATA; + end + OUTPUT_TYPE_TERM_0: begin + encoded_tx_data_reg <= {{7{CTRL_IDLE}}, 7'd0, BLOCK_TYPE_TERM_0}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_1: begin + encoded_tx_data_reg <= {{6{CTRL_IDLE}}, 6'd0, output_data_reg[7:0], BLOCK_TYPE_TERM_1}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_2: begin + encoded_tx_data_reg <= {{5{CTRL_IDLE}}, 5'd0, output_data_reg[15:0], BLOCK_TYPE_TERM_2}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_3: begin + encoded_tx_data_reg <= {{4{CTRL_IDLE}}, 4'd0, output_data_reg[23:0], BLOCK_TYPE_TERM_3}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_4: begin + encoded_tx_data_reg <= {{3{CTRL_IDLE}}, 3'd0, output_data_reg[31:0], BLOCK_TYPE_TERM_4}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_5: begin + encoded_tx_data_reg <= {{2{CTRL_IDLE}}, 2'd0, output_data_reg[39:0], BLOCK_TYPE_TERM_5}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_6: begin + encoded_tx_data_reg <= {{1{CTRL_IDLE}}, 1'd0, output_data_reg[47:0], BLOCK_TYPE_TERM_6}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_7: begin + encoded_tx_data_reg <= {output_data_reg[55:0], BLOCK_TYPE_TERM_7}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + default: begin + encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + endcase + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + s_tdata_reg <= s_tdata_next; + s_tkeep_reg <= s_tkeep_next; + + swap_data <= output_data_next[63:32]; + + delay_type <= output_type_next ^ 4'd4; +end + +endmodule diff --git a/rtl/eth_mac_phy_10g.v b/rtl/eth_mac_phy_10g.v new file mode 100644 index 000000000..672f09a9d --- /dev/null +++ b/rtl/eth_mac_phy_10g.v @@ -0,0 +1,154 @@ +/* + +Copyright (c) 2019 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 + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire tx_start_packet_0, + output wire tx_start_packet_4, + output wire rx_start_packet_0, + output wire rx_start_packet_4, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +eth_mac_phy_10g_rx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_mac_phy_10g_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_start_packet_0(rx_start_packet_0), + .rx_start_packet_4(rx_start_packet_4), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber) +); + +eth_mac_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE) +) +eth_mac_phy_10g_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .ifg_delay(ifg_delay), + .tx_start_packet_0(tx_start_packet_0), + .tx_start_packet_4(tx_start_packet_4) +); + +endmodule diff --git a/rtl/eth_mac_phy_10g_fifo.v b/rtl/eth_mac_phy_10g_fifo.v new file mode 100644 index 000000000..e19f89817 --- /dev/null +++ b/rtl/eth_mac_phy_10g_fifo.v @@ -0,0 +1,295 @@ +/* + +Copyright (c) 2019 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 + +/* + * 10G Ethernet MAC/PHY combination with TX and RX FIFOs + */ +module eth_mac_phy_10g_fifo # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4, + parameter TX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH), + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH), + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_block_lock, + output wire rx_high_ber, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [DATA_WIDTH-1:0] tx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] tx_fifo_axis_tkeep; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] rx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_fifo_axis_tkeep; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [3:0] rx_sync_reg_1 = 4'd0; +reg [3:0] rx_sync_reg_2 = 4'd0; +reg [3:0] rx_sync_reg_3 = 4'd0; +reg [3:0] rx_sync_reg_4 = 4'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; +assign rx_block_lock = rx_sync_reg_3[2] ^ rx_sync_reg_4[2]; +assign rx_high_ber = rx_sync_reg_3[3] ^ rx_sync_reg_4[3]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 4'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_high_ber_int, rx_block_lock_int, rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 4'd0; + rx_sync_reg_3 <= 4'd0; + rx_sync_reg_4 <= 4'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +eth_mac_phy_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_mac_phy_10g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tkeep(tx_fifo_axis_tkeep), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tkeep(rx_fifo_axis_tkeep), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .rx_block_lock(rx_block_lock_int), + .rx_high_ber(rx_high_ber_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo #( + .ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(1), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // Common reset + .async_rst(logic_rst | tx_rst), + // AXI input + .s_clk(logic_clk), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(tx_fifo_axis_tkeep), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo #( + .ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(1), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(RX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(RX_DROP_WHEN_FULL) +) +rx_fifo ( + // Common reset + .async_rst(rx_rst | logic_rst), + // AXI input + .s_clk(rx_clk), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(rx_fifo_axis_tkeep), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/rtl/eth_mac_phy_10g_rx.v b/rtl/eth_mac_phy_10g_rx.v new file mode 100644 index 000000000..d6f48cfb4 --- /dev/null +++ b/rtl/eth_mac_phy_10g_rx.v @@ -0,0 +1,187 @@ +/* + +Copyright (c) 2019 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 + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g_rx # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * SERDES interface + */ + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire rx_start_packet_0, + output wire rx_start_packet_4, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_block_lock, + output wire rx_high_ber +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH * 32 != DATA_WIDTH) begin + $error("Error: HDR_WIDTH must be equal to DATA_WIDTH/32"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] serdes_rx_data_int; +wire [HDR_WIDTH-1:0] serdes_rx_hdr_int; + +generate + genvar n; + + if (BIT_REVERSE) begin + for (n = 0; n < DATA_WIDTH; n = n + 1) begin + assign serdes_rx_data_int[n] = serdes_rx_data[DATA_WIDTH-n-1]; + end + + for (n = 0; n < HDR_WIDTH; n = n + 1) begin + assign serdes_rx_hdr_int[n] = serdes_rx_hdr[HDR_WIDTH-n-1]; + end + end else begin + assign serdes_rx_data_int = serdes_rx_data; + assign serdes_rx_hdr_int = serdes_rx_hdr; + end +endgenerate + +wire [DATA_WIDTH-1:0] descrambled_rx_data; + +reg [DATA_WIDTH-1:0] encoded_rx_data_reg = {DATA_WIDTH{1'b0}}; +reg [HDR_WIDTH-1:0] encoded_rx_hdr_reg = {HDR_WIDTH{1'b0}}; + +reg [57:0] scrambler_state_reg = {58{1'b1}}; +wire [57:0] scrambler_state; + +lfsr #( + .LFSR_WIDTH(58), + .LFSR_POLY(58'h8000000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(1), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH), + .STYLE("AUTO") +) +descrambler_inst ( + .data_in(serdes_rx_data_int), + .state_in(scrambler_state_reg), + .data_out(descrambled_rx_data), + .state_out(scrambler_state) +); + +always @(posedge clk) begin + scrambler_state_reg <= scrambler_state; + + encoded_rx_data_reg <= SCRAMBLER_DISABLE ? serdes_rx_data_int : descrambled_rx_data; + encoded_rx_hdr_reg <= serdes_rx_hdr_int; +end + +axis_baser_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +axis_baser_rx_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data_reg), + .encoded_rx_hdr(encoded_rx_hdr_reg), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .start_packet_0(rx_start_packet_0), + .start_packet_4(rx_start_packet_4), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +eth_phy_10g_rx_frame_sync #( + .HDR_WIDTH(HDR_WIDTH), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH) +) +eth_phy_10g_rx_frame_sync_inst ( + .clk(clk), + .rst(rst), + .serdes_rx_hdr(serdes_rx_hdr_int), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_block_lock(rx_block_lock) +); + +eth_phy_10g_rx_ber_mon #( + .HDR_WIDTH(HDR_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_ber_mon_inst ( + .clk(clk), + .rst(rst), + .serdes_rx_hdr(serdes_rx_hdr_int), + .rx_high_ber(rx_high_ber) +); + +endmodule diff --git a/rtl/eth_mac_phy_10g_tx.v b/rtl/eth_mac_phy_10g_tx.v new file mode 100644 index 000000000..7c4d2032e --- /dev/null +++ b/rtl/eth_mac_phy_10g_tx.v @@ -0,0 +1,168 @@ +/* + +Copyright (c) 2019 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 + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g_tx # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire tx_start_packet_0, + output wire tx_start_packet_4 +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH * 32 != DATA_WIDTH) begin + $error("Error: HDR_WIDTH must be equal to DATA_WIDTH/32"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; + +axis_baser_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +axis_baser_tx_inst ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .ifg_delay(ifg_delay), + .start_packet_0(tx_start_packet_0), + .start_packet_4(tx_start_packet_4) +); + +reg [57:0] tx_scrambler_state_reg = {58{1'b1}}; +wire [57:0] tx_scrambler_state; +wire [DATA_WIDTH-1:0] scrambled_data; + +reg [DATA_WIDTH-1:0] serdes_tx_data_reg = {DATA_WIDTH{1'b0}}; +reg [HDR_WIDTH-1:0] serdes_tx_hdr_reg = {HDR_WIDTH{1'b0}}; + +generate + genvar n; + + if (BIT_REVERSE) begin + for (n = 0; n < DATA_WIDTH; n = n + 1) begin + assign serdes_tx_data[n] = serdes_tx_data_reg[DATA_WIDTH-n-1]; + end + + for (n = 0; n < HDR_WIDTH; n = n + 1) begin + assign serdes_tx_hdr[n] = serdes_tx_hdr_reg[HDR_WIDTH-n-1]; + end + end else begin + assign serdes_tx_data = serdes_tx_data_reg; + assign serdes_tx_hdr = serdes_tx_hdr_reg; + end +endgenerate + +lfsr #( + .LFSR_WIDTH(58), + .LFSR_POLY(58'h8000000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH), + .STYLE("AUTO") +) +scrambler_inst ( + .data_in(encoded_tx_data), + .state_in(tx_scrambler_state_reg), + .data_out(scrambled_data), + .state_out(tx_scrambler_state) +); + +always @(posedge clk) begin + tx_scrambler_state_reg <= tx_scrambler_state; + + serdes_tx_data_reg <= SCRAMBLER_DISABLE ? encoded_tx_data : scrambled_data; + serdes_tx_hdr_reg <= encoded_tx_hdr; +end + +endmodule diff --git a/tb/test_axis_baser_rx_64.py b/tb/test_axis_baser_rx_64.py new file mode 100755 index 000000000..c1a06fded --- /dev/null +++ b/tb/test_axis_baser_rx_64.py @@ -0,0 +1,438 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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 baser_serdes_ep +import xgmii_ep + +module = 'axis_baser_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 + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + encoded_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + encoded_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + m_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + m_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + m_axis_tvalid = Signal(bool(0)) + m_axis_tlast = Signal(bool(0)) + m_axis_tuser = Signal(bool(0)) + start_packet_0 = Signal(bool(0)) + start_packet_4 = Signal(bool(0)) + error_bad_frame = Signal(bool(0)) + error_bad_fcs = Signal(bool(0)) + + # sources and sinks + source = baser_serdes_ep.BaseRSerdesSource() + + source_logic = source.create_logic( + clk, + tx_data=encoded_rx_data, + tx_header=encoded_rx_hdr, + scramble=False, + name='source' + ) + + sink = axis_ep.AXIStreamSink() + + sink_logic = sink.create_logic( + clk, + rst, + tdata=m_axis_tdata, + tkeep=m_axis_tkeep, + tvalid=m_axis_tvalid, + tlast=m_axis_tlast, + tuser=m_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, + encoded_rx_data=encoded_rx_data, + encoded_rx_hdr=encoded_rx_hdr, + m_axis_tdata=m_axis_tdata, + m_axis_tkeep=m_axis_tkeep, + m_axis_tvalid=m_axis_tvalid, + m_axis_tlast=m_axis_tlast, + m_axis_tuser=m_axis_tuser, + start_packet_0=start_packet_0, + start_packet_4=start_packet_4, + 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) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 6: test stream with zero IFG, length %d" % payload_len) + current_test.next = 6 + + source.ifg = 0 + + 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 + + source.ifg = 12 + + yield delay(100) + + for payload_len in list(range(46,54)): + yield clk.posedge + print("test 6: test stream with zero IFG and offset start, length %d" % payload_len) + current_test.next = 6 + + source.ifg = 0 + source.force_offset_start = True + + 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 + + source.ifg = 12 + source.force_offset_start = False + + yield delay(100) + + 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_baser_rx_64.v b/tb/test_axis_baser_rx_64.v new file mode 100644 index 000000000..cf644a276 --- /dev/null +++ b/tb/test_axis_baser_rx_64.v @@ -0,0 +1,105 @@ +/* + +Copyright (c) 2019 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_baser_rx_64 + */ +module test_axis_baser_rx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] encoded_rx_data = 0; +reg [HDR_WIDTH-1:0] encoded_rx_hdr = 1; + +// Outputs +wire [DATA_WIDTH-1:0] m_axis_tdata; +wire [KEEP_WIDTH-1:0] m_axis_tkeep; +wire m_axis_tvalid; +wire m_axis_tlast; +wire m_axis_tuser; +wire start_packet_0; +wire start_packet_4; +wire error_bad_frame; +wire error_bad_fcs; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + encoded_rx_data, + encoded_rx_hdr + ); + $to_myhdl( + m_axis_tdata, + m_axis_tkeep, + m_axis_tvalid, + m_axis_tlast, + m_axis_tuser, + start_packet_0, + start_packet_4, + error_bad_frame, + error_bad_fcs + ); + + // dump file + $dumpfile("test_axis_baser_rx_64.lxt"); + $dumpvars(0, test_axis_baser_rx_64); +end + +axis_baser_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .start_packet_0(start_packet_0), + .start_packet_4(start_packet_4), + .error_bad_frame(error_bad_frame), + .error_bad_fcs(error_bad_fcs) +); + +endmodule diff --git a/tb/test_axis_baser_tx_64.py b/tb/test_axis_baser_tx_64.py new file mode 100755 index 000000000..e64be4945 --- /dev/null +++ b/tb/test_axis_baser_tx_64.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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 baser_serdes_ep + +module = 'axis_baser_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 + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + s_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + s_axis_tvalid = Signal(bool(0)) + s_axis_tlast = Signal(bool(0)) + s_axis_tuser = Signal(bool(0)) + ifg_delay = Signal(intbv(0)[8:]) + + # Outputs + s_axis_tready = Signal(bool(0)) + encoded_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + encoded_tx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + start_packet_0 = Signal(bool(0)) + start_packet_4 = Signal(bool(0)) + + # sources and sinks + source_pause = Signal(bool(0)) + + source = axis_ep.AXIStreamSource() + + source_logic = source.create_logic( + clk, + rst, + tdata=s_axis_tdata, + tkeep=s_axis_tkeep, + tvalid=s_axis_tvalid, + tready=s_axis_tready, + tlast=s_axis_tlast, + tuser=s_axis_tuser, + pause=source_pause, + name='source' + ) + + sink = baser_serdes_ep.BaseRSerdesSink() + + sink_logic = sink.create_logic( + clk, + rx_data=encoded_tx_data, + rx_header=encoded_tx_hdr, + scramble=False, + 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, + s_axis_tdata=s_axis_tdata, + s_axis_tkeep=s_axis_tkeep, + s_axis_tvalid=s_axis_tvalid, + s_axis_tready=s_axis_tready, + s_axis_tlast=s_axis_tlast, + s_axis_tuser=s_axis_tuser, + encoded_tx_data=encoded_tx_data, + encoded_tx_hdr=encoded_tx_hdr, + ifg_delay=ifg_delay, + start_packet_0=start_packet_0, + start_packet_4=start_packet_4, + ) + + @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_baser_tx_64.v b/tb/test_axis_baser_tx_64.v new file mode 100644 index 000000000..efa195259 --- /dev/null +++ b/tb/test_axis_baser_tx_64.v @@ -0,0 +1,111 @@ +/* + +Copyright (c) 2019 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_baser_tx_64 + */ +module test_axis_baser_tx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] s_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] s_axis_tkeep = 0; +reg s_axis_tvalid = 0; +reg s_axis_tlast = 0; +reg s_axis_tuser = 0; +reg [7:0] ifg_delay = 0; + +// Outputs +wire s_axis_tready; +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; +wire start_packet_0; +wire start_packet_4; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_tdata, + s_axis_tkeep, + s_axis_tvalid, + s_axis_tlast, + s_axis_tuser, + ifg_delay + ); + $to_myhdl( + s_axis_tready, + encoded_tx_data, + encoded_tx_hdr, + start_packet_0, + start_packet_4 + ); + + // dump file + $dumpfile("test_axis_baser_tx_64.lxt"); + $dumpvars(0, test_axis_baser_tx_64); +end + +axis_baser_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .ifg_delay(ifg_delay), + .start_packet_0(start_packet_0), + .start_packet_4(start_packet_4) +); + +endmodule diff --git a/tb/test_eth_mac_phy_10g.py b/tb/test_eth_mac_phy_10g.py new file mode 100755 index 000000000..0eda4e24e --- /dev/null +++ b/tb/test_eth_mac_phy_10g.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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 +import baser_serdes_ep + +module = 'eth_mac_phy_10g' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_baser_tx_64.v") +srcs.append("../rtl/axis_baser_rx_64.v") +srcs.append("../rtl/eth_mac_phy_10g_rx.v") +srcs.append("../rtl/eth_mac_phy_10g_tx.v") +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +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 + DATA_WIDTH = 64 + KEEP_WIDTH = (DATA_WIDTH/8) + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = (DATA_WIDTH/32) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + SLIP_COUNT_WIDTH = 3 + COUNT_125US = 125000/6.4 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + tx_start_packet_0 = Signal(bool(0)) + tx_start_packet_4 = Signal(bool(0)) + rx_start_packet_0 = Signal(bool(0)) + rx_start_packet_4 = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + serdes_source = baser_serdes_ep.BaseRSerdesSource() + + serdes_source_logic = serdes_source.create_logic( + rx_clk, + tx_data=serdes_rx_data_int, + tx_header=serdes_rx_hdr_int, + name='serdes_source' + ) + + serdes_sink = baser_serdes_ep.BaseRSerdesSink() + + serdes_sink_logic = serdes_sink.create_logic( + tx_clk, + rx_data=serdes_tx_data, + rx_header=serdes_tx_hdr, + name='serdes_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + tx_clk, + tx_rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + rx_clk, + rx_rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + tx_axis_tvalid=tx_axis_tvalid, + tx_axis_tready=tx_axis_tready, + tx_axis_tlast=tx_axis_tlast, + tx_axis_tuser=tx_axis_tuser, + rx_axis_tdata=rx_axis_tdata, + rx_axis_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + serdes_tx_data=serdes_tx_data, + serdes_tx_hdr=serdes_tx_hdr, + serdes_rx_data=serdes_rx_data, + serdes_rx_hdr=serdes_rx_hdr, + serdes_rx_bitslip=serdes_rx_bitslip, + tx_start_packet_0=tx_start_packet_0, + tx_start_packet_4=tx_start_packet_4, + rx_start_packet_0=rx_start_packet_0, + rx_start_packet_4=rx_start_packet_4, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber, + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + + load_bit_offset = [] + + @instance + def shift_bits(): + bit_offset = 0 + last_data = 0 + + while True: + yield clk.posedge + + if load_bit_offset: + bit_offset = load_bit_offset.pop(0) + + if serdes_rx_bitslip: + bit_offset += 1 + + bit_offset = bit_offset % 66 + + data = int(serdes_rx_data_int) << 2 | int(serdes_rx_hdr_int) + + out_data = ((last_data | data << 66) >> 66-bit_offset) & 0x3ffffffffffffffff + + last_data = data + + serdes_rx_data.next = out_data >> 2 + serdes_rx_hdr.next = out_data & 3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + serdes_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield serdes_sink.wait() + rx_frame = serdes_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_eth_mac_phy_10g.v b/tb/test_eth_mac_phy_10g.v new file mode 100644 index 000000000..2937762bc --- /dev/null +++ b/tb/test_eth_mac_phy_10g.v @@ -0,0 +1,173 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Testbench for eth_mac_phy_10g + */ +module test_eth_mac_phy_10g; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = (DATA_WIDTH/32); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter SLIP_COUNT_WIDTH = 3; +parameter COUNT_125US = 125000/6.4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; +wire serdes_rx_bitslip; +wire tx_start_packet_0; +wire tx_start_packet_4; +wire rx_start_packet_0; +wire rx_start_packet_4; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_block_lock; +wire rx_high_ber; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + serdes_rx_data, + serdes_rx_hdr, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + serdes_tx_data, + serdes_tx_hdr, + serdes_rx_bitslip, + tx_start_packet_0, + tx_start_packet_4, + rx_start_packet_0, + rx_start_packet_4, + rx_error_bad_frame, + rx_error_bad_fcs, + rx_block_lock, + rx_high_ber + ); + + // dump file + $dumpfile("test_eth_mac_phy_10g.lxt"); + $dumpvars(0, test_eth_mac_phy_10g); +end + +eth_mac_phy_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .tx_start_packet_0(tx_start_packet_0), + .tx_start_packet_4(tx_start_packet_4), + .rx_start_packet_0(rx_start_packet_0), + .rx_start_packet_4(rx_start_packet_4), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/tb/test_eth_mac_phy_10g_fifo.py b/tb/test_eth_mac_phy_10g_fifo.py new file mode 100755 index 000000000..df34ef405 --- /dev/null +++ b/tb/test_eth_mac_phy_10g_fifo.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2019 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 +import baser_serdes_ep + +module = 'eth_mac_phy_10g_fifo' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/axis_baser_tx_64.v") +srcs.append("../rtl/axis_baser_rx_64.v") +srcs.append("../rtl/eth_mac_phy_10g.v") +srcs.append("../rtl/eth_mac_phy_10g_rx.v") +srcs.append("../rtl/eth_mac_phy_10g_tx.v") +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +srcs.append("../rtl/lfsr.v") +srcs.append("../lib/axis/rtl/axis_async_fifo.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + DATA_WIDTH = 64 + KEEP_WIDTH = int(DATA_WIDTH/8) + CTRL_WIDTH = int(DATA_WIDTH/8) + HDR_WIDTH = int(DATA_WIDTH/32) + ENABLE_PADDING = 1 + ENABLE_DIC = 1 + MIN_FRAME_LENGTH = 64 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + SLIP_COUNT_WIDTH = 3 + COUNT_125US = 125000/6.4 + TX_FIFO_ADDR_WIDTH = 12-(KEEP_WIDTH-1).bit_length() + TX_FRAME_FIFO = 1 + TX_DROP_BAD_FRAME = TX_FRAME_FIFO + TX_DROP_WHEN_FULL = 0 + RX_FIFO_ADDR_WIDTH = 12-(KEEP_WIDTH-1).bit_length() + RX_FRAME_FIFO = 1 + RX_DROP_BAD_FRAME = RX_FRAME_FIFO + RX_DROP_WHEN_FULL = RX_FRAME_FIFO + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + rx_clk = Signal(bool(0)) + rx_rst = Signal(bool(0)) + tx_clk = Signal(bool(0)) + tx_rst = Signal(bool(0)) + logic_clk = Signal(bool(0)) + logic_rst = Signal(bool(0)) + tx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + tx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + tx_axis_tvalid = Signal(bool(0)) + tx_axis_tlast = Signal(bool(0)) + tx_axis_tuser = Signal(bool(0)) + rx_axis_tready = Signal(bool(0)) + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + ifg_delay = Signal(intbv(0)[8:]) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + tx_axis_tready = Signal(bool(0)) + rx_axis_tdata = Signal(intbv(0)[DATA_WIDTH:]) + rx_axis_tkeep = Signal(intbv(0)[KEEP_WIDTH:]) + rx_axis_tvalid = Signal(bool(0)) + rx_axis_tlast = Signal(bool(0)) + rx_axis_tuser = Signal(bool(0)) + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + tx_fifo_overflow = Signal(bool(0)) + tx_fifo_bad_frame = Signal(bool(0)) + tx_fifo_good_frame = Signal(bool(0)) + rx_error_bad_frame = Signal(bool(0)) + rx_error_bad_fcs = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + rx_fifo_overflow = Signal(bool(0)) + rx_fifo_bad_frame = Signal(bool(0)) + rx_fifo_good_frame = Signal(bool(0)) + + # sources and sinks + axis_source_pause = Signal(bool(0)) + + serdes_source = baser_serdes_ep.BaseRSerdesSource() + + serdes_source_logic = serdes_source.create_logic( + rx_clk, + tx_data=serdes_rx_data_int, + tx_header=serdes_rx_hdr_int, + name='serdes_source' + ) + + serdes_sink = baser_serdes_ep.BaseRSerdesSink() + + serdes_sink_logic = serdes_sink.create_logic( + tx_clk, + rx_data=serdes_tx_data, + rx_header=serdes_tx_hdr, + name='serdes_sink' + ) + + axis_source = axis_ep.AXIStreamSource() + + axis_source_logic = axis_source.create_logic( + logic_clk, + logic_rst, + tdata=tx_axis_tdata, + tkeep=tx_axis_tkeep, + tvalid=tx_axis_tvalid, + tready=tx_axis_tready, + tlast=tx_axis_tlast, + tuser=tx_axis_tuser, + pause=axis_source_pause, + name='axis_source' + ) + + axis_sink = axis_ep.AXIStreamSink() + + axis_sink_logic = axis_sink.create_logic( + logic_clk, + logic_rst, + tdata=rx_axis_tdata, + tkeep=rx_axis_tkeep, + tvalid=rx_axis_tvalid, + tready=rx_axis_tready, + tlast=rx_axis_tlast, + tuser=rx_axis_tuser, + name='axis_sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + rx_clk=rx_clk, + rx_rst=rx_rst, + tx_clk=tx_clk, + tx_rst=tx_rst, + logic_clk=logic_clk, + logic_rst=logic_rst, + tx_axis_tdata=tx_axis_tdata, + tx_axis_tkeep=tx_axis_tkeep, + tx_axis_tvalid=tx_axis_tvalid, + tx_axis_tready=tx_axis_tready, + tx_axis_tlast=tx_axis_tlast, + tx_axis_tuser=tx_axis_tuser, + rx_axis_tdata=rx_axis_tdata, + rx_axis_tkeep=rx_axis_tkeep, + rx_axis_tvalid=rx_axis_tvalid, + rx_axis_tready=rx_axis_tready, + rx_axis_tlast=rx_axis_tlast, + rx_axis_tuser=rx_axis_tuser, + serdes_tx_data=serdes_tx_data, + serdes_tx_hdr=serdes_tx_hdr, + serdes_rx_data=serdes_rx_data, + serdes_rx_hdr=serdes_rx_hdr, + serdes_rx_bitslip=serdes_rx_bitslip, + tx_fifo_overflow=tx_fifo_overflow, + tx_fifo_bad_frame=tx_fifo_bad_frame, + tx_fifo_good_frame=tx_fifo_good_frame, + rx_error_bad_frame=rx_error_bad_frame, + rx_error_bad_fcs=rx_error_bad_fcs, + rx_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber, + rx_fifo_overflow=rx_fifo_overflow, + rx_fifo_bad_frame=rx_fifo_bad_frame, + rx_fifo_good_frame=rx_fifo_good_frame, + ifg_delay=ifg_delay + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + tx_clk.next = not tx_clk + rx_clk.next = not rx_clk + logic_clk.next = not logic_clk + + load_bit_offset = [] + + @instance + def shift_bits(): + bit_offset = 0 + last_data = 0 + + while True: + yield clk.posedge + + if load_bit_offset: + bit_offset = load_bit_offset.pop(0) + + if serdes_rx_bitslip: + bit_offset += 1 + + bit_offset = bit_offset % 66 + + data = int(serdes_rx_data_int) << 2 | int(serdes_rx_hdr_int) + + out_data = ((last_data | data << 66) >> 66-bit_offset) & 0x3ffffffffffffffff + + last_data = data + + serdes_rx_data.next = out_data >> 2 + serdes_rx_hdr.next = out_data & 3 + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + tx_rst.next = 1 + rx_rst.next = 1 + logic_rst.next = 1 + yield clk.posedge + rst.next = 0 + tx_rst.next = 0 + rx_rst.next = 0 + logic_rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + ifg_delay.next = 12 + + # testbench stimulus + + yield clk.posedge + print("test 1: test rx packet") + current_test.next = 1 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis_fcs() + + serdes_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame)) + + yield axis_sink.wait() + rx_frame = axis_sink.recv() + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis(rx_frame) + eth_frame.update_fcs() + + assert eth_frame == test_frame + + yield delay(100) + + yield clk.posedge + print("test 2: test tx packet") + current_test.next = 2 + + test_frame = eth_ep.EthFrame() + test_frame.eth_dest_mac = 0xDAD1D2D3D4D5 + test_frame.eth_src_mac = 0x5A5152535455 + test_frame.eth_type = 0x8000 + test_frame.payload = bytearray(range(32)) + test_frame.update_fcs() + + axis_frame = test_frame.build_axis() + + axis_source.send(axis_frame) + + yield serdes_sink.wait() + rx_frame = serdes_sink.recv() + + assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5') + + eth_frame = eth_ep.EthFrame() + eth_frame.parse_axis_fcs(rx_frame.data[8:]) + + print(hex(eth_frame.eth_fcs)) + print(hex(eth_frame.calc_fcs())) + + assert len(eth_frame.payload.data) == 46 + assert eth_frame.eth_fcs == eth_frame.calc_fcs() + assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac + assert eth_frame.eth_src_mac == test_frame.eth_src_mac + assert eth_frame.eth_type == test_frame.eth_type + assert eth_frame.payload.data.index(test_frame.payload.data) == 0 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_eth_mac_phy_10g_fifo.v b/tb/test_eth_mac_phy_10g_fifo.v new file mode 100644 index 000000000..c838c79b4 --- /dev/null +++ b/tb/test_eth_mac_phy_10g_fifo.v @@ -0,0 +1,204 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Testbench for eth_mac_phy_10g_fifo + */ +module test_eth_mac_phy_10g_fifo; + +// Parameters +parameter DATA_WIDTH = 64; +parameter KEEP_WIDTH = (DATA_WIDTH/8); +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = (DATA_WIDTH/32); +parameter ENABLE_PADDING = 1; +parameter ENABLE_DIC = 1; +parameter MIN_FRAME_LENGTH = 64; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter SLIP_COUNT_WIDTH = 3; +parameter COUNT_125US = 125000/6.4; +parameter TX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH); +parameter TX_FRAME_FIFO = 1; +parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO; +parameter TX_DROP_WHEN_FULL = 0; +parameter RX_FIFO_ADDR_WIDTH = 12-$clog2(KEEP_WIDTH); +parameter RX_FRAME_FIFO = 1; +parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO; +parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg rx_clk = 0; +reg rx_rst = 0; +reg tx_clk = 0; +reg tx_rst = 0; +reg logic_clk = 0; +reg logic_rst = 0; +reg [DATA_WIDTH-1:0] tx_axis_tdata = 0; +reg [KEEP_WIDTH-1:0] tx_axis_tkeep = 0; +reg tx_axis_tvalid = 0; +reg tx_axis_tlast = 0; +reg tx_axis_tuser = 0; +reg rx_axis_tready = 0; +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; +reg [7:0] ifg_delay = 0; + +// Outputs +wire tx_axis_tready; +wire [DATA_WIDTH-1:0] rx_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_axis_tkeep; +wire rx_axis_tvalid; +wire rx_axis_tlast; +wire rx_axis_tuser; +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; +wire serdes_rx_bitslip; +wire tx_fifo_overflow; +wire tx_fifo_bad_frame; +wire tx_fifo_good_frame; +wire rx_error_bad_frame; +wire rx_error_bad_fcs; +wire rx_block_lock; +wire rx_high_ber; +wire rx_fifo_overflow; +wire rx_fifo_bad_frame; +wire rx_fifo_good_frame; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + rx_clk, + rx_rst, + tx_clk, + tx_rst, + logic_clk, + logic_rst, + tx_axis_tdata, + tx_axis_tkeep, + tx_axis_tvalid, + tx_axis_tlast, + tx_axis_tuser, + rx_axis_tready, + serdes_rx_data, + serdes_rx_hdr, + ifg_delay + ); + $to_myhdl( + tx_axis_tready, + rx_axis_tdata, + rx_axis_tkeep, + rx_axis_tvalid, + rx_axis_tlast, + rx_axis_tuser, + serdes_tx_data, + serdes_tx_hdr, + serdes_rx_bitslip, + tx_fifo_overflow, + tx_fifo_bad_frame, + tx_fifo_good_frame, + rx_error_bad_frame, + rx_error_bad_fcs, + rx_block_lock, + rx_high_ber, + rx_fifo_overflow, + rx_fifo_bad_frame, + rx_fifo_good_frame + ); + + // dump file + $dumpfile("test_eth_mac_phy_10g_fifo.lxt"); + $dumpvars(0, test_eth_mac_phy_10g_fifo); +end + +eth_mac_phy_10g_fifo #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US), + .TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH), + .TX_FRAME_FIFO(TX_FRAME_FIFO), + .TX_DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .TX_DROP_WHEN_FULL(TX_DROP_WHEN_FULL), + .RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH), + .RX_FRAME_FIFO(RX_FRAME_FIFO), + .RX_DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .RX_DROP_WHEN_FULL(RX_DROP_WHEN_FULL) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .logic_clk(logic_clk), + .logic_rst(logic_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tkeep(tx_axis_tkeep), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tkeep(rx_axis_tkeep), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tready(rx_axis_tready), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .tx_fifo_overflow(tx_fifo_overflow), + .tx_fifo_bad_frame(tx_fifo_bad_frame), + .tx_fifo_good_frame(tx_fifo_good_frame), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_fifo_overflow(rx_fifo_overflow), + .rx_fifo_bad_frame(rx_fifo_bad_frame), + .rx_fifo_good_frame(rx_fifo_good_frame), + .ifg_delay(ifg_delay) +); + +endmodule