From dbbbc28059a19a052e659bccb00e6b076c5c8cfb Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Wed, 16 Jan 2019 18:00:56 -0800 Subject: [PATCH] Add 10G Ethernet PHY modules and testbenches --- rtl/eth_phy_10g.v | 109 +++++++++++++ rtl/eth_phy_10g_rx.v | 173 ++++++++++++++++++++ rtl/eth_phy_10g_rx_ber_mon.v | 120 ++++++++++++++ rtl/eth_phy_10g_rx_frame_sync.v | 133 ++++++++++++++++ rtl/eth_phy_10g_tx.v | 139 +++++++++++++++++ tb/test_eth_phy_10g_64.py | 254 ++++++++++++++++++++++++++++++ tb/test_eth_phy_10g_64.v | 121 ++++++++++++++ tb/test_eth_phy_10g_rx_64.py | 269 ++++++++++++++++++++++++++++++++ tb/test_eth_phy_10g_rx_64.v | 99 ++++++++++++ tb/test_eth_phy_10g_tx_64.py | 207 ++++++++++++++++++++++++ tb/test_eth_phy_10g_tx_64.v | 88 +++++++++++ 11 files changed, 1712 insertions(+) create mode 100644 rtl/eth_phy_10g.v create mode 100644 rtl/eth_phy_10g_rx.v create mode 100644 rtl/eth_phy_10g_rx_ber_mon.v create mode 100644 rtl/eth_phy_10g_rx_frame_sync.v create mode 100644 rtl/eth_phy_10g_tx.v create mode 100755 tb/test_eth_phy_10g_64.py create mode 100644 tb/test_eth_phy_10g_64.v create mode 100755 tb/test_eth_phy_10g_rx_64.py create mode 100644 tb/test_eth_phy_10g_rx_64.v create mode 100755 tb/test_eth_phy_10g_tx_64.py create mode 100644 tb/test_eth_phy_10g_tx_64.v diff --git a/rtl/eth_phy_10g.v b/rtl/eth_phy_10g.v new file mode 100644 index 000000000..4d4e21751 --- /dev/null +++ b/rtl/eth_phy_10g.v @@ -0,0 +1,109 @@ +/* + +Copyright (c) 2018 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 PHY + */ +module eth_phy_10g # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + 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, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * 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 rx_block_lock, + output wire rx_high_ber +); + +eth_phy_10g_rx #( + .DATA_WIDTH(DATA_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_phy_10g_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber) +); + +eth_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE) +) +eth_phy_10g_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr) +); + +endmodule diff --git a/rtl/eth_phy_10g_rx.v b/rtl/eth_phy_10g_rx.v new file mode 100644 index 000000000..0348d0d1c --- /dev/null +++ b/rtl/eth_phy_10g_rx.v @@ -0,0 +1,173 @@ +/* + +Copyright (c) 2018 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 PHY + */ +module eth_phy_10g_rx # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + 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, + + /* + * XGMII interface + */ + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * 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_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 (CTRL_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 + +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 <= descrambled_rx_data; + encoded_rx_hdr_reg <= serdes_rx_hdr_int; +end + +xgmii_baser_dec_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +xgmii_baser_dec_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(SCRAMBLER_DISABLE ? serdes_rx_data_int : encoded_rx_data_reg), + .encoded_rx_hdr(SCRAMBLER_DISABLE ? serdes_rx_hdr_int : encoded_rx_hdr_reg), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .rx_bad_block() +); + +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_phy_10g_rx_ber_mon.v b/rtl/eth_phy_10g_rx_ber_mon.v new file mode 100644 index 000000000..a1b5d0b37 --- /dev/null +++ b/rtl/eth_phy_10g_rx_ber_mon.v @@ -0,0 +1,120 @@ +/* + +Copyright (c) 2018 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 PHY BER monitor + */ +module eth_phy_10g_rx_ber_mon # +( + parameter HDR_WIDTH = 2, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * SERDES interface + */ + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + + /* + * Status + */ + output wire rx_high_ber +); + +// bus width assertions +initial begin + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +parameter COUNT_WIDTH = $clog2(COUNT_125US); + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +reg [COUNT_WIDTH-1:0] time_count_reg = 0, time_count_next; +reg [3:0] ber_count_reg = 4'd0, ber_count_next; + +reg rx_high_ber_reg = 1'b0, rx_high_ber_next; + +assign rx_high_ber = rx_high_ber_reg; + +always @* begin + if (time_count_reg > 0) begin + time_count_next = time_count_reg-1; + end else begin + time_count_next = time_count_reg; + end + ber_count_next = ber_count_reg; + + rx_high_ber_next = rx_high_ber_reg; + + if (serdes_rx_hdr == SYNC_CTRL || serdes_rx_hdr == SYNC_DATA) begin + // valid header + if (ber_count_reg != 4'd15) begin + if (time_count_reg == 0) begin + rx_high_ber_next = 1'b0; + end + end + end else begin + // invalid header + if (ber_count_reg == 4'd15) begin + rx_high_ber_next = 1'b1; + end else begin + ber_count_next = ber_count_reg + 1; + if (time_count_reg == 0) begin + rx_high_ber_next = 1'b0; + end + end + end + if (time_count_reg == 0) begin + // 125 us timer expired + ber_count_next = 4'd0; + time_count_next = COUNT_125US; + end +end + +always @(posedge clk) begin + if (rst) begin + time_count_reg <= COUNT_125US; + ber_count_reg <= 4'd0; + rx_high_ber_reg <= 1'b0; + end else begin + time_count_reg <= time_count_next; + ber_count_reg <= ber_count_next; + rx_high_ber_reg <= rx_high_ber_next; + end +end + +endmodule diff --git a/rtl/eth_phy_10g_rx_frame_sync.v b/rtl/eth_phy_10g_rx_frame_sync.v new file mode 100644 index 000000000..14fdf72b5 --- /dev/null +++ b/rtl/eth_phy_10g_rx_frame_sync.v @@ -0,0 +1,133 @@ +/* + +Copyright (c) 2018 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 PHY frame sync + */ +module eth_phy_10g_rx_frame_sync # +( + parameter HDR_WIDTH = 2, + parameter SLIP_COUNT_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * SERDES interface + */ + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire rx_block_lock +); + +// bus width assertions +initial begin + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +reg [5:0] sh_count_reg = 6'd0, sh_count_next; +reg [3:0] sh_invalid_count_reg = 4'd0, sh_invalid_count_next; +reg [SLIP_COUNT_WIDTH-1:0] slip_count_reg = 0, slip_count_next; + +reg serdes_rx_bitslip_reg = 1'b0, serdes_rx_bitslip_next; + +reg rx_block_lock_reg = 1'b0, rx_block_lock_next; + +assign serdes_rx_bitslip = serdes_rx_bitslip_reg; +assign rx_block_lock = rx_block_lock_reg; + +always @* begin + sh_count_next = sh_count_reg; + sh_invalid_count_next = sh_invalid_count_reg; + slip_count_next = slip_count_reg; + + serdes_rx_bitslip_next = 1'b0; + + rx_block_lock_next = rx_block_lock_reg; + + if (slip_count_reg) begin + slip_count_next = slip_count_reg-1; + end else if (serdes_rx_hdr == SYNC_CTRL || serdes_rx_hdr == SYNC_DATA) begin + // valid header + sh_count_next = sh_count_reg + 1; + if (&sh_count_reg) begin + // valid count overflow, reset + sh_count_next = 0; + sh_invalid_count_next = 0; + if (!sh_invalid_count_reg) begin + rx_block_lock_next = 1'b1; + end + end + end else begin + // invalid header + sh_count_next = sh_count_reg + 1; + sh_invalid_count_next = sh_invalid_count_reg + 1; + if (!rx_block_lock_reg || &sh_invalid_count_reg) begin + // invalid count overflow, lost block lock + sh_count_next = 0; + sh_invalid_count_next = 0; + rx_block_lock_next = 1'b0; + serdes_rx_bitslip_next = 1'b1; + slip_count_next = {SLIP_COUNT_WIDTH{1'b1}}; + end else if (&sh_count_reg) begin + // valid count overflow, reset + sh_count_next = 0; + sh_invalid_count_next = 0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + sh_count_reg <= 6'd0; + sh_invalid_count_reg <= 4'd0; + slip_count_reg <= 0; + rx_block_lock_reg <= 1'b0; + end else begin + sh_count_reg <= sh_count_next; + sh_invalid_count_reg <= sh_invalid_count_next; + slip_count_reg <= slip_count_next; + rx_block_lock_reg <= rx_block_lock_next; + end + + serdes_rx_bitslip_reg <= serdes_rx_bitslip_next; +end + +endmodule diff --git a/rtl/eth_phy_10g_tx.v b/rtl/eth_phy_10g_tx.v new file mode 100644 index 000000000..96de4a584 --- /dev/null +++ b/rtl/eth_phy_10g_tx.v @@ -0,0 +1,139 @@ +/* + +Copyright (c) 2018 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 PHY + */ +module eth_phy_10g_tx # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_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 + +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; + +xgmii_baser_enc_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +xgmii_baser_enc_inst ( + .clk(clk), + .rst(rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr) +); + +reg [57:0] scrambler_state_reg = {58{1'b1}}; +wire [57:0] 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(scrambler_state_reg), + .data_out(scrambled_data), + .state_out(scrambler_state) +); + +always @(posedge clk) begin + scrambler_state_reg <= 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_eth_phy_10g_64.py b/tb/test_eth_phy_10g_64.py new file mode 100755 index 000000000..e24ae570b --- /dev/null +++ b/tb/test_eth_phy_10g_64.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2018 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_phy_10g' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/eth_phy_10g_rx.v") +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +srcs.append("../rtl/eth_phy_10g_tx.v") +srcs.append("../rtl/xgmii_baser_dec_64.v") +srcs.append("../rtl/xgmii_baser_enc_64.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 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + COUNT_125US = 1250/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)) + xgmii_txd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0)[CTRL_WIDTH:]) + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + xgmii_rxd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0)[CTRL_WIDTH:]) + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(0)[HDR_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + + # sources and sinks + xgmii_source = xgmii_ep.XGMIISource() + + xgmii_source_logic = xgmii_source.create_logic( + tx_clk, + tx_rst, + txd=xgmii_txd, + txc=xgmii_txc, + name='xgmii_source' + ) + + xgmii_sink = xgmii_ep.XGMIISink() + + xgmii_sink_logic = xgmii_sink.create_logic( + rx_clk, + rx_rst, + rxd=xgmii_rxd, + rxc=xgmii_rxc, + name='xgmii_sink' + ) + + 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' + ) + + # 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, + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + 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_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + rx_clk.next = not rx_clk + tx_clk.next = not tx_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 + + # testbench stimulus + + yield clk.posedge + print("test 1: test RX packet") + current_test.next = 1 + + test_frame = bytearray(range(128)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + xgmii_source.send(xgmii_frame) + + yield serdes_sink.wait() + rx_frame = serdes_sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert xgmii_sink.empty() + assert serdes_sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 2: test TX packet") + current_test.next = 2 + + test_frame = bytearray(range(128)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + serdes_source.send(xgmii_frame) + + yield xgmii_sink.wait() + rx_frame = xgmii_sink.recv() + + assert rx_frame.data == xgmii_frame.data + + assert xgmii_sink.empty() + assert serdes_sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_eth_phy_10g_64.v b/tb/test_eth_phy_10g_64.v new file mode 100644 index 000000000..61a78d7af --- /dev/null +++ b/tb/test_eth_phy_10g_64.v @@ -0,0 +1,121 @@ +/* + +Copyright (c) 2018 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_phy_10g + */ +module test_eth_phy_10g_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +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] xgmii_txd = 0; +reg [CTRL_WIDTH-1:0] xgmii_txc = 0; +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; + +// Outputs +wire [DATA_WIDTH-1:0] xgmii_rxd; +wire [CTRL_WIDTH-1:0] xgmii_rxc; +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; +wire serdes_rx_bitslip; +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, + xgmii_txd, + xgmii_txc, + serdes_rx_data, + serdes_rx_hdr + ); + $to_myhdl( + xgmii_rxd, + xgmii_rxc, + serdes_tx_data, + serdes_tx_hdr, + serdes_rx_bitslip, + rx_block_lock, + rx_high_ber + ); + + // dump file + $dumpfile("test_eth_phy_10g_64.lxt"); + $dumpvars(0, test_eth_phy_10g_64); +end + +eth_phy_10g #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .COUNT_125US(COUNT_125US) +) +UUT ( + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .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_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber) +); + +endmodule diff --git a/tb/test_eth_phy_10g_rx_64.py b/tb/test_eth_phy_10g_rx_64.py new file mode 100755 index 000000000..1effa27e8 --- /dev/null +++ b/tb/test_eth_phy_10g_rx_64.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2018 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_phy_10g_rx' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/eth_phy_10g_rx_ber_mon.v") +srcs.append("../rtl/eth_phy_10g_rx_frame_sync.v") +srcs.append("../rtl/xgmii_baser_dec_64.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 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + COUNT_125US = 1250/6.4 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + serdes_rx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr = Signal(intbv(1)[HDR_WIDTH:]) + + serdes_rx_data_int = Signal(intbv(0)[DATA_WIDTH:]) + serdes_rx_hdr_int = Signal(intbv(1)[HDR_WIDTH:]) + + # Outputs + xgmii_rxd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_rxc = Signal(intbv(0)[CTRL_WIDTH:]) + serdes_rx_bitslip = Signal(bool(0)) + rx_block_lock = Signal(bool(0)) + rx_high_ber = Signal(bool(0)) + + # sources and sinks + source = baser_serdes_ep.BaseRSerdesSource() + + source_logic = source.create_logic( + clk, + tx_data=serdes_rx_data_int, + tx_header=serdes_rx_hdr_int, + name='source' + ) + + sink = xgmii_ep.XGMIISink() + + sink_logic = sink.create_logic( + clk, + rst, + rxd=xgmii_rxd, + rxc=xgmii_rxc, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + xgmii_rxd=xgmii_rxd, + xgmii_rxc=xgmii_rxc, + serdes_rx_data=serdes_rx_data, + serdes_rx_hdr=serdes_rx_hdr, + serdes_rx_bitslip=serdes_rx_bitslip, + rx_block_lock=rx_block_lock, + rx_high_ber=rx_high_ber + ) + + @always(delay(4)) + def clkgen(): + clk.next = not 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 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + for payload_len in list(range(16,34)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = bytearray(range(payload_len)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame.data + + 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 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: errored frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + #assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 4: test frame sync") + current_test.next = 4 + + assert rx_block_lock + + load_bit_offset.append(33) + + yield delay(600) + + assert not rx_block_lock + assert rx_high_ber + + yield delay(3000) + + assert rx_block_lock + + yield delay(2000) + + assert not rx_high_ber + + 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_phy_10g_rx_64.v b/tb/test_eth_phy_10g_rx_64.v new file mode 100644 index 000000000..221ea84ec --- /dev/null +++ b/tb/test_eth_phy_10g_rx_64.v @@ -0,0 +1,99 @@ +/* + +Copyright (c) 2018 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_phy_10g_rx + */ +module test_eth_phy_10g_rx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; +parameter COUNT_125US = 1250/6.4; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] serdes_rx_data = 0; +reg [HDR_WIDTH-1:0] serdes_rx_hdr = 1; + +// Outputs +wire [DATA_WIDTH-1:0] xgmii_rxd; +wire [CTRL_WIDTH-1:0] xgmii_rxc; +wire serdes_rx_bitslip; +wire rx_block_lock; +wire rx_high_ber; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + serdes_rx_data, + serdes_rx_hdr + ); + $to_myhdl( + xgmii_rxd, + xgmii_rxc, + serdes_rx_bitslip, + rx_block_lock, + rx_high_ber + ); + + // dump file + $dumpfile("test_eth_phy_10g_rx_64.lxt"); + $dumpvars(0, test_eth_phy_10g_rx_64); +end + +eth_phy_10g_rx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .COUNT_125US(COUNT_125US) +) +UUT ( + .clk(clk), + .rst(rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber) +); + +endmodule diff --git a/tb/test_eth_phy_10g_tx_64.py b/tb/test_eth_phy_10g_tx_64.py new file mode 100755 index 000000000..dffd88854 --- /dev/null +++ b/tb/test_eth_phy_10g_tx_64.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +""" + +Copyright (c) 2018 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_phy_10g_tx' +testbench = 'test_%s_64' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/xgmii_baser_enc_64.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 + CTRL_WIDTH = (DATA_WIDTH/8) + HDR_WIDTH = 2 + BIT_REVERSE = 0 + SCRAMBLER_DISABLE = 0 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + xgmii_txd = Signal(intbv(0)[DATA_WIDTH:]) + xgmii_txc = Signal(intbv(0)[CTRL_WIDTH:]) + + # Outputs + serdes_tx_data = Signal(intbv(0)[DATA_WIDTH:]) + serdes_tx_hdr = Signal(intbv(0)[HDR_WIDTH:]) + + # sources and sinks + source = xgmii_ep.XGMIISource() + + source_logic = source.create_logic( + clk, + rst, + txd=xgmii_txd, + txc=xgmii_txc, + name='source' + ) + + sink = baser_serdes_ep.BaseRSerdesSink() + + sink_logic = sink.create_logic( + clk, + rx_data=serdes_tx_data, + rx_header=serdes_tx_hdr, + name='sink' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + xgmii_txd=xgmii_txd, + xgmii_txc=xgmii_txc, + serdes_tx_data=serdes_tx_data, + serdes_tx_hdr=serdes_tx_hdr + ) + + @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 + + # testbench stimulus + + for payload_len in list(range(16,34)): + yield clk.posedge + print("test 1: test packet, length %d" % payload_len) + current_test.next = 1 + + test_frame = bytearray(range(payload_len)) + + xgmii_frame = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame) + + source.send(xgmii_frame) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame.data + + 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 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + yield clk.posedge + print("test 3: errored frame, length %d" % payload_len) + current_test.next = 3 + + test_frame1 = bytearray(range(payload_len)) + test_frame2 = bytearray(range(payload_len)) + + xgmii_frame1 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame1) + xgmii_frame2 = xgmii_ep.XGMIIFrame(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+test_frame2) + + xgmii_frame1.error = 1 + + source.send(xgmii_frame1) + source.send(xgmii_frame2) + + yield sink.wait() + rx_frame = sink.recv() + + #assert rx_frame.data == xgmii_frame1.data + + yield sink.wait() + rx_frame = sink.recv() + + assert rx_frame.data == xgmii_frame2.data + + assert sink.empty() + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/tb/test_eth_phy_10g_tx_64.v b/tb/test_eth_phy_10g_tx_64.v new file mode 100644 index 000000000..af5a5fd36 --- /dev/null +++ b/tb/test_eth_phy_10g_tx_64.v @@ -0,0 +1,88 @@ +/* + +Copyright (c) 2018 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_phy_10g_tx + */ +module test_eth_phy_10g_tx_64; + +// Parameters +parameter DATA_WIDTH = 64; +parameter CTRL_WIDTH = (DATA_WIDTH/8); +parameter HDR_WIDTH = 2; +parameter BIT_REVERSE = 0; +parameter SCRAMBLER_DISABLE = 0; + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [DATA_WIDTH-1:0] xgmii_txd = 0; +reg [CTRL_WIDTH-1:0] xgmii_txc = 0; + +// Outputs +wire [DATA_WIDTH-1:0] serdes_tx_data; +wire [HDR_WIDTH-1:0] serdes_tx_hdr; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + xgmii_txd, + xgmii_txc + ); + $to_myhdl( + serdes_tx_data, + serdes_tx_hdr + ); + + // dump file + $dumpfile("test_eth_phy_10g_tx_64.lxt"); + $dumpvars(0, test_eth_phy_10g_tx_64); +end + +eth_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE) +) +UUT ( + .clk(clk), + .rst(rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr) +); + +endmodule