eth: Add support for USXGMII to 10GBASE-R MAC+PCS

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-07-13 16:31:27 -07:00
parent caaa2c3281
commit 5549f2be9e
17 changed files with 1364 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
taxi_eth_mac_phy_10g.sv
taxi_eth_mac_phy_10g_rx.f
taxi_eth_mac_phy_10g_tx.f
taxi_eth_phy_10g_usxgmii_an.sv
taxi_eth_mac_stats.f
taxi_mac_ctrl_tx.sv
taxi_mac_ctrl_rx.sv

View File

@@ -21,6 +21,7 @@ module taxi_eth_mac_phy_10g #
parameter HDR_W = (DATA_W/32),
parameter logic TX_GBX_IF_EN = 1'b0,
parameter logic RX_GBX_IF_EN = TX_GBX_IF_EN,
parameter logic USXGMII_EN = 1'b0,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TD_EN = PTP_TS_EN,
@@ -79,6 +80,27 @@ module taxi_eth_mac_phy_10g #
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* USXGMII autonegotiation
*/
input wire logic an_en = 1'b1,
input wire logic an_restart = 1'b0,
input wire logic an_speedup = 1'b0,
input wire logic an_timeout_en = 1'b1,
input wire logic an_usxgmii_en = 1'b0,
input wire logic an_usxgmii_auto = 1'b1,
input wire logic an_usxgmii_5g = 1'b0,
output wire logic an_intr,
output wire logic an_running,
output wire logic an_complete,
output wire logic an_timeout,
output wire logic an_usxgmii_mode,
input wire logic [15:0] an_adv_ability_usxgmii = 16'h1601,
output wire logic [15:0] an_lp_adv_ability,
output wire logic an_lp_usxgmii_link,
output wire logic [2:0] an_lp_usxgmii_speed,
output wire logic an_res_full_duplex,
/*
* PTP
*/
@@ -356,10 +378,149 @@ end else begin
end
// USXGMII autonegotiation
wire [23:0] rx_os;
wire rx_os_sig;
wire rx_os_valid;
wire rx_os_match;
wire rx_idle_match;
wire [23:0] tx_os;
wire tx_os_sig;
wire tx_os_valid;
wire tx_os_ready;
wire cfg_rx_usxgmii_en;
wire cfg_rx_usxgmii_5g;
wire [2:0] cfg_rx_usxgmii_speed;
wire cfg_tx_usxgmii_en;
wire cfg_tx_usxgmii_5g;
wire [2:0] cfg_tx_usxgmii_speed;
if (USXGMII_EN) begin : an
// synchronize RX signals to TX
wire [23:0] sync_rx_os;
wire sync_rx_os_sig;
wire sync_rx_os_valid;
wire sync_rx_os_match;
wire sync_rx_idle_match;
wire sync_rx_block_lock;
taxi_sync_signal #(
.WIDTH(24+5),
.N(2)
)
rx_sync_inst (
.clk(tx_clk),
.in({
rx_os,
rx_os_sig,
rx_os_valid,
rx_os_match,
rx_idle_match,
rx_block_lock
}),
.out({
sync_rx_os,
sync_rx_os_sig,
sync_rx_os_valid,
sync_rx_os_match,
sync_rx_idle_match,
sync_rx_block_lock
})
);
taxi_sync_signal #(
.WIDTH(1+1+3),
.N(2)
)
rx_sync_ctrl_inst (
.clk(rx_clk),
.in({
cfg_tx_usxgmii_en,
cfg_tx_usxgmii_5g,
cfg_tx_usxgmii_speed
}),
.out({
cfg_rx_usxgmii_en,
cfg_rx_usxgmii_5g,
cfg_rx_usxgmii_speed
})
);
taxi_eth_phy_10g_usxgmii_an #(
.DATA_W(DATA_W)
)
an_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* Ordered sets
*/
.rx_os(sync_rx_os),
.rx_os_sig(sync_rx_os_sig),
.rx_os_valid(sync_rx_os_valid),
.rx_os_match(sync_rx_os_match),
.rx_idle_match(sync_rx_idle_match),
.rx_block_lock(sync_rx_block_lock),
.tx_os(tx_os),
.tx_os_sig(tx_os_sig),
.tx_os_valid(tx_os_valid),
.tx_os_ready(tx_os_ready),
/*
* USXGMII Autonegotiation
*/
.an_en(an_en),
.an_restart(an_restart),
.an_speedup(an_speedup),
.an_timeout_en(an_timeout_en),
.an_usxgmii_en(an_usxgmii_en),
.an_usxgmii_auto(an_usxgmii_auto),
.an_usxgmii_5g(an_usxgmii_5g),
.an_intr(an_intr),
.an_running(an_running),
.an_complete(an_complete),
.an_timeout(an_timeout),
.an_usxgmii_mode(an_usxgmii_mode),
.an_adv_ability_usxgmii(an_adv_ability_usxgmii),
.an_lp_adv_ability(an_lp_adv_ability),
.an_lp_usxgmii_link(an_lp_usxgmii_link),
.an_lp_usxgmii_speed(an_lp_usxgmii_speed),
.an_res_full_duplex(an_res_full_duplex)
);
assign cfg_tx_usxgmii_en = an_usxgmii_mode;
assign cfg_tx_usxgmii_5g = an_usxgmii_5g;
assign cfg_tx_usxgmii_speed = an_lp_usxgmii_speed;
end else begin : an
wire [23:0] tx_os = '0;
wire tx_os_sig = 1'b0;
wire tx_os_valid = 1'b0;
wire cfg_rx_usxgmii_en = 1'b0;
wire cfg_rx_usxgmii_5g = 1'b0;
wire [2:0] cfg_rx_usxgmii_speed = 3'b011;
wire cfg_tx_usxgmii_en = 1'b0;
wire cfg_tx_usxgmii_5g = 1'b0;
wire [2:0] cfg_tx_usxgmii_speed = 3'b011;
end
taxi_eth_mac_phy_10g_rx #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.GBX_IF_EN(RX_GBX_IF_EN),
.USXGMII_EN(USXGMII_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
@@ -390,6 +551,15 @@ eth_mac_phy_10g_rx_inst (
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* Ordered sets
*/
.rx_os(rx_os),
.rx_os_sig(rx_os_sig),
.rx_os_valid(rx_os_valid),
.rx_os_match(rx_os_match),
.rx_idle_match(rx_idle_match),
/*
* PTP
*/
@@ -424,6 +594,9 @@ eth_mac_phy_10g_rx_inst (
*/
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_rx_usxgmii_en(cfg_rx_usxgmii_en),
.cfg_rx_usxgmii_5g(cfg_rx_usxgmii_5g),
.cfg_rx_usxgmii_speed(cfg_rx_usxgmii_speed),
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable)
);
@@ -431,6 +604,7 @@ taxi_eth_mac_phy_10g_tx #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.GBX_IF_EN(TX_GBX_IF_EN),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
@@ -462,6 +636,14 @@ eth_mac_phy_10g_tx_inst (
.serdes_tx_gbx_req_stall(serdes_tx_gbx_req_stall),
.serdes_tx_gbx_sync(serdes_tx_gbx_sync),
/*
* Ordered sets
*/
.tx_os(tx_os),
.tx_os_sig(tx_os_sig),
.tx_os_valid(tx_os_valid),
.tx_os_ready(tx_os_ready),
/*
* PTP
*/
@@ -492,6 +674,9 @@ eth_mac_phy_10g_tx_inst (
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_tx_usxgmii_en(cfg_tx_usxgmii_en),
.cfg_tx_usxgmii_5g(cfg_tx_usxgmii_5g),
.cfg_tx_usxgmii_speed(cfg_tx_usxgmii_speed),
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable)
);

View File

@@ -21,6 +21,7 @@ module taxi_eth_mac_phy_10g_fifo #
parameter HDR_W = (DATA_W/32),
parameter logic TX_GBX_IF_EN = 1'b0,
parameter logic RX_GBX_IF_EN = TX_GBX_IF_EN,
parameter logic USXGMII_EN = 1'b0,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TD_EN = PTP_TS_EN,
@@ -92,6 +93,27 @@ module taxi_eth_mac_phy_10g_fifo #
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* USXGMII autonegotiation
*/
input wire logic an_en = 1'b1,
input wire logic an_restart = 1'b0,
input wire logic an_speedup = 1'b0,
input wire logic an_timeout_en = 1'b1,
input wire logic an_usxgmii_en = 1'b0,
input wire logic an_usxgmii_auto = 1'b1,
input wire logic an_usxgmii_5g = 1'b0,
output wire logic an_intr,
output wire logic an_running,
output wire logic an_complete,
output wire logic an_timeout,
output wire logic an_usxgmii_mode,
input wire logic [15:0] an_adv_ability_usxgmii = 16'h1601,
output wire logic [15:0] an_lp_adv_ability,
output wire logic an_lp_usxgmii_link,
output wire logic [2:0] an_lp_usxgmii_speed,
output wire logic an_res_full_duplex,
/*
* PTP clock
*/
@@ -300,6 +322,7 @@ taxi_eth_mac_phy_10g #(
.HDR_W(HDR_W),
.TX_GBX_IF_EN(TX_GBX_IF_EN),
.RX_GBX_IF_EN(RX_GBX_IF_EN),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TD_EN(PTP_TD_EN),
@@ -356,6 +379,27 @@ eth_mac_phy_10g_inst (
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* USXGMII autonegotiation
*/
.an_en(an_en),
.an_restart(an_restart),
.an_speedup(an_speedup),
.an_timeout_en(an_timeout_en),
.an_usxgmii_en(an_usxgmii_en),
.an_usxgmii_auto(an_usxgmii_auto),
.an_usxgmii_5g(an_usxgmii_5g),
.an_intr(an_intr),
.an_running(an_running),
.an_complete(an_complete),
.an_timeout(an_timeout),
.an_usxgmii_mode(an_usxgmii_mode),
.an_adv_ability_usxgmii(an_adv_ability_usxgmii),
.an_lp_adv_ability(an_lp_adv_ability),
.an_lp_usxgmii_link(an_lp_usxgmii_link),
.an_lp_usxgmii_speed(an_lp_usxgmii_speed),
.an_res_full_duplex(an_res_full_duplex),
/*
* PTP
*/

View File

@@ -20,6 +20,7 @@ module taxi_eth_mac_phy_10g_rx #
parameter DATA_W = 64,
parameter HDR_W = (DATA_W/32),
parameter logic GBX_IF_EN = 1'b0,
parameter logic USXGMII_EN = 1'b0,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TS_FMT_TOD = 1'b1,
parameter PTP_TS_W = PTP_TS_FMT_TOD ? 96 : 64,
@@ -50,6 +51,15 @@ module taxi_eth_mac_phy_10g_rx #
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* Ordered sets
*/
output wire logic [23:0] rx_os,
output wire logic rx_os_sig,
output wire logic rx_os_valid,
output wire logic rx_os_match,
output wire logic rx_idle_match,
/*
* PTP
*/
@@ -84,6 +94,9 @@ module taxi_eth_mac_phy_10g_rx #
*/
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518-1,
input wire logic cfg_rx_enable,
input wire logic cfg_rx_usxgmii_en = 1'b1,
input wire logic cfg_rx_usxgmii_5g = 1'b0,
input wire logic [2:0] cfg_rx_usxgmii_speed = 3'b011,
input wire logic cfg_rx_prbs31_enable
);
@@ -144,6 +157,9 @@ eth_phy_10g_rx_if_inst (
if (DATA_W == 64) begin
if (USXGMII_EN)
$fatal(0, "Error: USXGMII is not currently supported in 64-bit mode (instance %m)");
taxi_axis_baser_rx_64 #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
@@ -172,11 +188,11 @@ if (DATA_W == 64) begin
/*
* Ordered sets
*/
.rx_os(),
.rx_os_sig(),
.rx_os_valid(),
.rx_os_match(),
.rx_idle_match(),
.rx_os(rx_os),
.rx_os_sig(rx_os_sig),
.rx_os_valid(rx_os_valid),
.rx_os_match(rx_os_match),
.rx_idle_match(rx_idle_match),
/*
* PTP
@@ -216,6 +232,7 @@ end else begin
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.GBX_IF_EN(GBX_IF_EN),
.USXGMII_EN(USXGMII_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W)
)
@@ -239,11 +256,11 @@ end else begin
/*
* Ordered sets
*/
.rx_os(),
.rx_os_sig(),
.rx_os_valid(),
.rx_os_match(),
.rx_idle_match(),
.rx_os(rx_os),
.rx_os_sig(rx_os_sig),
.rx_os_valid(rx_os_valid),
.rx_os_match(rx_os_match),
.rx_idle_match(rx_idle_match),
/*
* PTP
@@ -255,6 +272,9 @@ end else begin
*/
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_rx_usxgmii_en(cfg_rx_usxgmii_en),
.cfg_rx_usxgmii_5g(cfg_rx_usxgmii_5g),
.cfg_rx_usxgmii_speed(cfg_rx_usxgmii_speed),
/*
* Status

View File

@@ -20,6 +20,7 @@ module taxi_eth_mac_phy_10g_tx #
parameter DATA_W = 64,
parameter HDR_W = (DATA_W/32),
parameter logic GBX_IF_EN = 1'b0,
parameter logic USXGMII_EN = 1'b0,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TS_FMT_TOD = 1'b1,
@@ -51,6 +52,14 @@ module taxi_eth_mac_phy_10g_tx #
input wire logic serdes_tx_gbx_req_stall = 1'b0,
output wire logic serdes_tx_gbx_sync,
/*
* Ordered sets
*/
input wire logic [23:0] tx_os = '0,
input wire logic tx_os_sig = 1'b0,
input wire logic tx_os_valid = 1'b0,
output wire logic tx_os_ready,
/*
* PTP
*/
@@ -81,6 +90,9 @@ module taxi_eth_mac_phy_10g_tx #
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518-1,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable,
input wire logic cfg_tx_usxgmii_en = 1'b1,
input wire logic cfg_tx_usxgmii_5g = 1'b0,
input wire logic [2:0] cfg_tx_usxgmii_speed = 3'b011,
input wire logic cfg_tx_prbs31_enable
);
@@ -135,6 +147,9 @@ tx_pad_inst (
if (DATA_W == 64) begin
if (USXGMII_EN)
$fatal(0, "Error: USXGMII is not currently supported in 64-bit mode (instance %m)");
taxi_axis_baser_tx_64 #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
@@ -170,10 +185,10 @@ if (DATA_W == 64) begin
/*
* Ordered sets
*/
.tx_os('0),
.tx_os_sig(1'b0),
.tx_os_valid(1'b0),
.tx_os_ready(),
.tx_os(tx_os),
.tx_os_sig(tx_os_sig),
.tx_os_valid(tx_os_valid),
.tx_os_ready(tx_os_ready),
/*
* PTP
@@ -211,6 +226,7 @@ end else begin
.HDR_W(HDR_W),
.GBX_IF_EN(GBX_IF_EN),
.GBX_CNT(1),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W),
@@ -240,10 +256,10 @@ end else begin
/*
* Ordered sets
*/
.tx_os('0),
.tx_os_sig(1'b0),
.tx_os_valid(1'b0),
.tx_os_ready(),
.tx_os(tx_os),
.tx_os_sig(tx_os_sig),
.tx_os_valid(tx_os_valid),
.tx_os_ready(tx_os_ready),
/*
* PTP
@@ -256,6 +272,9 @@ end else begin
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_tx_usxgmii_en(cfg_tx_usxgmii_en),
.cfg_tx_usxgmii_5g(cfg_tx_usxgmii_5g),
.cfg_tx_usxgmii_speed(cfg_tx_usxgmii_speed),
/*
* Status

View File

@@ -0,0 +1,374 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* USXGMII autonegotiation
*/
module taxi_eth_phy_10g_usxgmii_an #
(
parameter DATA_W = 32
)
(
input wire logic clk,
input wire logic rst,
/*
* Ordered sets
*/
input wire logic [23:0] rx_os,
input wire logic rx_os_sig,
input wire logic rx_os_valid,
input wire logic rx_os_match,
input wire logic rx_idle_match,
input wire logic rx_block_lock,
output wire logic [23:0] tx_os,
output wire logic tx_os_sig,
output wire logic tx_os_valid,
input wire logic tx_os_ready,
/*
* USXGMII Autonegotiation
*/
input wire logic an_en = 1'b1,
input wire logic an_restart = 1'b0,
input wire logic an_speedup = 1'b0,
input wire logic an_timeout_en = 1'b1,
input wire logic an_usxgmii_en = 1'b0,
input wire logic an_usxgmii_auto = 1'b1,
input wire logic an_usxgmii_5g = 1'b0,
output wire logic an_intr,
output wire logic an_running,
output wire logic an_complete,
output wire logic an_timeout,
output wire logic an_usxgmii_mode,
input wire logic [15:0] an_adv_ability_usxgmii = 16'h1601,
output wire logic [15:0] an_lp_adv_ability,
output wire logic an_lp_usxgmii_link,
output wire logic [2:0] an_lp_usxgmii_speed,
output wire logic an_res_full_duplex
);
localparam logic [15:0] AN_ACK = 16'h4000;
localparam logic [15:0] AN_NP = 16'h8000;
typedef enum logic [2:0] {
STATE_START,
STATE_AN_RESTART,
STATE_ABILITY_DET,
STATE_ACK_DET,
STATE_ACK_CPL,
STATE_IDLE_DET,
STATE_DONE
} state_t;
state_t state_reg = STATE_START, state_next;
// 1 pulse per 100 us for timers
localparam CYC_PER_US = DATA_W == 64 ? 156 : 312;
localparam PRESC_CYC = $rtoi(CYC_PER_US*100);
logic [16:0] presc_cnt_reg = '0;
logic presc_pulse_reg = 1'b0;
logic [4:0] delay_cnt_reg = '0, delay_cnt_next;
logic delay_run_reg = 1'b0, delay_run_next;
logic [9:0] timeout_cnt_reg = '0, timeout_cnt_next;
logic timeout_run_reg = 1'b0, timeout_run_next;
logic [23:0] tx_os_reg = '0, tx_os_next;
logic tx_os_valid_reg = 1'b0, tx_os_valid_next;
logic an_intr_reg = 1'b0, an_intr_next;
logic an_running_reg = 1'b0, an_running_next;
logic an_complete_reg = 1'b0, an_complete_next;
logic an_timeout_reg = 1'b0, an_timeout_next;
logic an_usxgmii_mode_reg = 1'b0, an_usxgmii_mode_next;
logic [15:0] an_lp_adv_ability_reg = '0, an_lp_adv_ability_next;
assign tx_os = tx_os_reg;
assign tx_os_sig = 1'b0;
assign tx_os_valid = tx_os_valid_reg;
assign an_intr = an_intr_reg;
assign an_running = an_running_reg;
assign an_complete = an_complete_reg;
assign an_timeout = an_timeout_reg;
assign an_usxgmii_mode = an_usxgmii_mode_reg;
assign an_lp_adv_ability = an_lp_adv_ability_reg;
// extract config information from link partner ability value
assign an_lp_usxgmii_link = an_usxgmii_mode_reg ? an_lp_adv_ability_reg[15] : 1'b1;
assign an_lp_usxgmii_speed = an_usxgmii_mode_reg ? an_lp_adv_ability_reg[11:9] : 3'b011;
assign an_res_full_duplex = an_usxgmii_mode_reg ? an_lp_adv_ability_reg[12] : 1'b1;
// TODO: check for stabilized RX config reg values
always_comb begin
state_next = STATE_START;
delay_cnt_next = delay_cnt_reg;
delay_run_next = delay_run_reg;
timeout_cnt_next = timeout_cnt_reg;
timeout_run_next = timeout_run_reg;
tx_os_next = tx_os_reg;
tx_os_valid_next = tx_os_valid_reg && !tx_os_ready;
an_intr_next = 1'b0;
an_running_next = an_running_reg;
an_complete_next = an_complete_reg;
an_timeout_next = an_timeout_reg;
an_usxgmii_mode_next = an_usxgmii_mode_reg;
an_lp_adv_ability_next = an_lp_adv_ability_reg;
if (delay_run_reg) begin
if (presc_pulse_reg) begin
if (delay_cnt_reg != 0) begin
delay_cnt_next = delay_cnt_reg - 1;
end else begin
delay_run_next = 1'b0;
end
end
end else begin
// USXGMII: 1.6 ms timer
delay_cnt_next = 16;
end
if (timeout_run_reg) begin
if (presc_pulse_reg) begin
if (timeout_cnt_reg != 0) begin
timeout_cnt_next = timeout_cnt_reg - 1;
end else begin
timeout_run_next = 1'b0;
end
end
end else begin
// 100 ms timer
timeout_cnt_next = 1000;
end
case (state_reg)
STATE_START: begin
// start
an_running_next = 1'b0;
an_complete_next = 1'b0;
an_timeout_next = 1'b0;
timeout_run_next = 1'b0;
if (an_usxgmii_en) begin
an_usxgmii_mode_next = 1'b1;
end else if (!an_usxgmii_auto) begin
an_usxgmii_mode_next = 1'b0;
end
tx_os_next = {16'd0, 8'h03};
if (an_en) begin
if (!an_usxgmii_mode_next) begin
state_next = STATE_DONE;
end else begin
tx_os_valid_next = 1'b1;
if (delay_run_reg) begin
// restart link timer
delay_run_next = 1'b0;
state_next = STATE_START;
end else begin
// AN restart state
delay_run_next = 1'b1;
an_running_next = 1'b1;
state_next = STATE_AN_RESTART;
end
end
end else begin
// AN disabled
an_usxgmii_mode_next = 1'b0;
state_next = STATE_START;
end
end
STATE_AN_RESTART: begin
// AN restart - send zeroed config reg to trigger link partner to restart the AN process
tx_os_next = {16'd0, 8'h03};
tx_os_valid_next = 1'b1;
if (!delay_run_reg) begin
// link timer expired
timeout_run_next = 1'b1;
state_next = STATE_ABILITY_DET;
end else begin
state_next = STATE_AN_RESTART;
end
end
STATE_ABILITY_DET: begin
// ability detect state - transfer AN ability value with ACK clear
tx_os_next[23:8] = an_adv_ability_usxgmii & ~AN_ACK;
tx_os_valid_next = 1'b1;
if (rx_os_match && rx_os[23:8] != 0 && rx_os[7:0] == 8'h03) begin
// got USXGMII ability advertisement from link partner
an_lp_adv_ability_next = rx_os[23:8];
state_next = STATE_ACK_DET;
end else if (!timeout_run_reg && an_timeout_en) begin
// timed out, no AN response from link partner
an_timeout_next = 1'b1;
if (!an_usxgmii_en) begin
an_usxgmii_mode_next = 1'b0;
end
state_next = STATE_DONE;
end else begin
state_next = STATE_ABILITY_DET;
end
end
STATE_ACK_DET: begin
// acknowledge detect - wait for ACK from link partner
tx_os_next[23:8] = tx_os_reg[23:8] | AN_ACK;
tx_os_valid_next = 1'b1;
if (rx_os_match && rx_os[23:8] == 0 && rx_os[7:0] == 8'h03) begin
// restart request from link partner
state_next = STATE_START;
end else if (rx_os_match && rx_os[14+8] && rx_os[7:0] == 8'h03) begin
// acknowledge match
an_lp_adv_ability_next = rx_os[23:8];
if (rx_os[23:8] == (an_lp_adv_ability_reg | AN_ACK)) begin
// consistent with previously-seen value
delay_run_next = 1'b1;
state_next = STATE_ACK_CPL;
end else begin
// inconsistent, restart AN
state_next = STATE_START;
end
end else begin
state_next = STATE_ACK_DET;
end
end
STATE_ACK_CPL: begin
// complete acknowledge - give link partner time to detect our ACK
tx_os_next[23:8] = tx_os_reg[23:8] | AN_ACK;
tx_os_valid_next = 1'b1;
if (rx_os_match && rx_os[23:8] == 0 && rx_os[7:0] == 8'h03) begin
// restart request from link partner
state_next = STATE_START;
end else if (!delay_run_reg) begin
// link timer expired
if (an_lp_adv_ability_reg[0] == 1'b1 && rx_os[7:0] == 8'h03) begin
// mode matches
delay_run_next = 1'b1;
state_next = STATE_IDLE_DET;
end else begin
// mode mismatch
if (an_usxgmii_en) begin
state_next = STATE_START;
end else begin
an_usxgmii_mode_next = 1'b0;
state_next = STATE_DONE;
end
end
end else begin
state_next = STATE_ACK_CPL;
end
end
STATE_IDLE_DET: begin
// idle detect - wait for link to go idle
if (rx_os_match && rx_os[23:8] == 0 && rx_os[7:0] == 8'h03) begin
// restart request from link partner
state_next = STATE_START;
end else if (rx_idle_match && !delay_run_reg) begin
// idle match and link timer expired
an_complete_next = 1'b1;
state_next = STATE_DONE;
end else begin
state_next = STATE_IDLE_DET;
end
end
STATE_DONE: begin
// AN operation complete
an_running_next = 1'b0;
timeout_run_next = 1'b0;
if (rx_os_match && rx_os[23:8] == 0 && rx_os[7:0] == 8'h03) begin
// restart request from link partner
an_usxgmii_mode_next = 1'b1;
state_next = STATE_START;
end else begin
state_next = STATE_DONE;
end
end
default: begin
state_next = STATE_START;
end
endcase
if (!an_en || an_restart || !rx_block_lock) begin
an_usxgmii_mode_next = an_en && an_usxgmii_en;
if (an_usxgmii_en) begin
an_usxgmii_mode_next = 1'b1;
end else if (!an_usxgmii_auto) begin
an_usxgmii_mode_next = 1'b0;
end else if (an_usxgmii_auto && rx_os_match && rx_os[7:0] == 8'h03) begin
an_usxgmii_mode_next = 1'b1;
end
state_next = STATE_START;
end
end
always @(posedge clk) begin
state_reg <= state_next;
delay_cnt_reg <= delay_cnt_next;
delay_run_reg <= delay_run_next;
timeout_cnt_reg <= timeout_cnt_next;
timeout_run_reg <= timeout_run_next;
tx_os_reg <= tx_os_next;
tx_os_valid_reg <= tx_os_valid_next;
an_intr_reg <= an_intr_next;
an_running_reg <= an_running_next;
an_complete_reg <= an_complete_next;
an_timeout_reg <= an_timeout_next;
an_usxgmii_mode_reg <= an_usxgmii_mode_next;
an_lp_adv_ability_reg <= an_lp_adv_ability_next;
presc_pulse_reg <= 1'b0;
if (presc_cnt_reg != 0) begin
presc_cnt_reg <= presc_cnt_reg - 1;
end else begin
presc_pulse_reg <= 1'b1;
presc_cnt_reg <= 17'(an_speedup ? PRESC_CYC / 1000 : PRESC_CYC);
end
if (rst) begin
state_reg <= STATE_START;
presc_cnt_reg <= '0;
presc_pulse_reg <= 1'b0;
delay_cnt_reg <= '0;
delay_run_reg <= 1'b0;
timeout_cnt_reg <= '0;
timeout_run_reg <= 1'b0;
tx_os_valid_reg <= 1'b0;
an_intr_reg <= 1'b0;
an_running_reg <= 1'b0;
an_complete_reg <= 1'b0;
an_timeout_reg <= 1'b0;
an_usxgmii_mode_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -54,6 +54,7 @@ module taxi_eth_mac_25g_us #
// MAC/PHY parameters
parameter logic COMBINED_MAC_PCS = 1'b1,
parameter DATA_W = 64,
parameter logic USXGMII_EN = 1'b0,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TD_EN = PTP_TS_EN,
@@ -133,6 +134,26 @@ module taxi_eth_mac_25g_us #
*/
taxi_axis_if.src m_axis_rx[CNT],
/*
* USXGMII autonegotiation
*/
input wire logic an_en[CNT] = '{CNT{1'b1}},
input wire logic an_restart[CNT] = '{CNT{1'b0}},
input wire logic an_speedup[CNT] = '{CNT{1'b0}},
input wire logic an_timeout_en[CNT] = '{CNT{1'b1}},
input wire logic an_usxgmii_en[CNT] = '{CNT{1'b0}},
input wire logic an_usxgmii_auto[CNT] = '{CNT{1'b1}},
output wire logic an_intr[CNT],
output wire logic an_running[CNT],
output wire logic an_complete[CNT],
output wire logic an_timeout[CNT],
output wire logic an_usxgmii_mode[CNT],
input wire logic [15:0] an_adv_ability_usxgmii[CNT] = '{CNT{16'h1601}},
output wire logic [15:0] an_lp_adv_ability[CNT],
output wire logic an_lp_usxgmii_link[CNT],
output wire logic [2:0] an_lp_usxgmii_speed[CNT],
output wire logic an_res_full_duplex[CNT],
/*
* PTP clock
*/
@@ -426,6 +447,7 @@ for (genvar n = 0; n < CNT; n = n + 1) begin : ch
// MAC/PHY parameters
.COMBINED_MAC_PCS(COMBINED_MAC_PCS),
.DATA_W(DATA_W),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TD_EN(PTP_TD_EN),
@@ -519,6 +541,26 @@ for (genvar n = 0; n < CNT; n = n + 1) begin : ch
*/
.m_axis_rx(m_axis_rx[n]),
/*
* USXGMII autonegotiation
*/
.an_en(an_en[n]),
.an_restart(an_restart[n]),
.an_speedup(an_speedup[n]),
.an_timeout_en(an_timeout_en[n]),
.an_usxgmii_en(an_usxgmii_en[n]),
.an_usxgmii_auto(an_usxgmii_auto[n]),
.an_intr(an_intr[n]),
.an_running(an_running[n]),
.an_complete(an_complete[n]),
.an_timeout(an_timeout[n]),
.an_usxgmii_mode(an_usxgmii_mode[n]),
.an_adv_ability_usxgmii(an_adv_ability_usxgmii[n]),
.an_lp_adv_ability(an_lp_adv_ability[n]),
.an_lp_usxgmii_link(an_lp_usxgmii_link[n]),
.an_lp_usxgmii_speed(an_lp_usxgmii_speed[n]),
.an_res_full_duplex(an_res_full_duplex[n]),
/*
* PTP clock
*/

View File

@@ -53,6 +53,7 @@ module taxi_eth_mac_25g_us_ch #
// MAC/PHY parameters
parameter logic COMBINED_MAC_PCS = 1'b1,
parameter DATA_W = 64,
parameter logic USXGMII_EN = 1'b0,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TD_EN = PTP_TS_EN,
@@ -146,6 +147,26 @@ module taxi_eth_mac_25g_us_ch #
*/
taxi_axis_if.src m_axis_rx,
/*
* USXGMII autonegotiation
*/
input wire logic an_en = 1'b1,
input wire logic an_restart = 1'b0,
input wire logic an_speedup = 1'b0,
input wire logic an_timeout_en = 1'b1,
input wire logic an_usxgmii_en = 1'b0,
input wire logic an_usxgmii_auto = 1'b1,
output wire logic an_intr,
output wire logic an_running,
output wire logic an_complete,
output wire logic an_timeout,
output wire logic an_usxgmii_mode,
input wire logic [15:0] an_adv_ability_usxgmii = 16'h1601,
output wire logic [15:0] an_lp_adv_ability,
output wire logic an_lp_usxgmii_link,
output wire logic [2:0] an_lp_usxgmii_speed,
output wire logic an_res_full_duplex,
/*
* PTP clock
*/
@@ -885,6 +906,7 @@ if (COMBINED_MAC_PCS) begin : mac
.HDR_W(HDR_W),
.TX_GBX_IF_EN(GBX_EN),
.RX_GBX_IF_EN(GBX_EN),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TD_EN(PTP_TD_EN),
@@ -943,6 +965,27 @@ if (COMBINED_MAC_PCS) begin : mac
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(rx_reset_req),
/*
* USXGMII autonegotiation
*/
.an_en(an_en),
.an_restart(an_restart),
.an_speedup(an_speedup),
.an_timeout_en(an_timeout_en),
.an_usxgmii_en(an_usxgmii_en),
.an_usxgmii_auto(an_usxgmii_auto),
.an_usxgmii_5g(1'b0),
.an_intr(an_intr),
.an_running(an_running),
.an_complete(an_complete),
.an_timeout(an_timeout),
.an_usxgmii_mode(an_usxgmii_mode),
.an_adv_ability_usxgmii(an_adv_ability_usxgmii),
.an_lp_adv_ability(an_lp_adv_ability),
.an_lp_usxgmii_link(an_lp_usxgmii_link),
.an_lp_usxgmii_speed(an_lp_usxgmii_speed),
.an_res_full_duplex(an_res_full_duplex),
/*
* PTP
*/

View File

@@ -44,6 +44,7 @@ export PARAM_QPLL0_EXT_CTRL := 0
export PARAM_QPLL1_EXT_CTRL := 0
export PARAM_COMBINED_MAC_PCS := 1
export PARAM_DATA_W := 64
export PARAM_USXGMII_EN := 0
export PARAM_DIC_EN := 1
export PARAM_PTP_TS_EN := 1
export PARAM_PTP_TD_EN := $(PARAM_PTP_TS_EN)

View File

@@ -22,7 +22,7 @@ import cocotb_test.simulator
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.triggers import RisingEdge, Timer
from cocotb.utils import get_time_from_sim_steps
from cocotb.regression import TestFactory
@@ -138,6 +138,14 @@ class TB:
dut.rx_rst_in.setimmediatevalue([0]*4)
dut.tx_rst_in.setimmediatevalue([0]*4)
dut.an_en.setimmediatevalue([0]*4)
dut.an_restart.setimmediatevalue([0]*4)
dut.an_speedup.setimmediatevalue([1]*4)
dut.an_timeout_en.setimmediatevalue([1]*4)
dut.an_usxgmii_en.setimmediatevalue([0]*4)
dut.an_usxgmii_auto.setimmediatevalue([1]*4)
dut.an_adv_ability_usxgmii.setimmediatevalue([0x1601]*4)
dut.stat_rx_fifo_drop.setimmediatevalue([0]*4)
dut.cfg_tx_pad_en.setimmediatevalue([0]*4)
@@ -956,6 +964,159 @@ async def run_test_pfc(dut, port=0, ifg=12):
await RisingEdge(dut.xcvr_ctrl_clk)
async def run_usxgmii_an(tb, port=0, cfg=0x0001):
# link timer scaled by 1000x for faster simulation
link_timer = Timer(1.6, 'us')
dut = tb.dut
for k in range(10):
tb.log.info("AN_RESTART")
tb.serdes_sources[port].set_seq_os((0x0000 << 8) | 0x03)
await link_timer
tb.log.info("ABILITY_DETECT")
tb.serdes_sources[port].set_seq_os(((cfg & ~0x4000) << 8) | 0x03)
tb.serdes_sinks[port].get_os()
lp_cfg = None
while True:
await RisingEdge(dut.tx_clk[port])
lp_cfg = tb.serdes_sinks[port].get_os()[0]
if lp_cfg is None:
continue
if lp_cfg & 0xff != 0x03:
continue
lp_cfg = lp_cfg >> 8
if tb.serdes_sinks[port].get_os_match() and lp_cfg != 0:
break
tb.log.info("ACKNOWLEDGE_DETECT")
tb.serdes_sources[port].set_seq_os(((cfg | 0x4000) << 8) | 0x03)
tb.serdes_sinks[port].get_os()
lp_cfg_ack = None
while True:
await RisingEdge(dut.tx_clk[port])
lp_cfg_ack = tb.serdes_sinks[port].get_os()[0]
if lp_cfg_ack is None:
continue
if lp_cfg_ack & 0xff != 0x03:
continue
lp_cfg_ack = lp_cfg_ack >> 8
if tb.serdes_sinks[port].get_os_match() and lp_cfg_ack & 0x4000:
break
elif tb.serdes_sinks[port].get_os_match() and lp_cfg_ack == 0:
break
if lp_cfg | 0x4000 != lp_cfg_ack:
tb.log.warning("AN inconsistent, restarting (0x%04x != 0x%04x)", lp_cfg | 0x4000, lp_cfg_ack)
continue
if lp_cfg_ack == 0:
tb.log.warning("AN restart requested")
continue
tb.log.info("COMPLETE_ACKNOWLEDGE")
await link_timer
tb.log.info("IDLE_DETECT")
tb.serdes_sources[port].set_seq_os(None)
await link_timer
lp_cfg_ack2 = None
while True:
await RisingEdge(dut.tx_clk[port])
if tb.serdes_sinks[port].get_idle_match():
break
lp_cfg_ack2 = tb.serdes_sinks[port].get_os()[0]
if lp_cfg_ack2 is None:
continue
if lp_cfg_ack2 & 0xff != 0x03:
continue
lp_cfg_ack2 = lp_cfg_ack2 >> 8
if tb.serdes_sinks[port].get_os_match() and (lp_cfg_ack2 == 0 or lp_cfg_ack != lp_cfg_ack2):
break
if lp_cfg_ack2 is not None and lp_cfg_ack != lp_cfg_ack2:
tb.log.warning("AN inconsistent, restarting (0x%04x != 0x%04x)", lp_cfg_ack, lp_cfg_ack2)
continue
if lp_cfg_ack2 == 0:
tb.log.warning("AN restart requested")
continue
tb.log.info("AN done")
return lp_cfg_ack
tb.log.warning("AN timed out")
tb.serdes_sources[port].set_seq_os(None)
return None
async def run_test_usxgmii(dut, port=0):
tb = TB(dut)
dut.an_en[port].value = 1
dut.an_restart[port].value = 0
dut.an_speedup[port].value = 1
dut.an_timeout_en[port].value = 1
dut.an_usxgmii_en[port].value = 0
dut.an_usxgmii_auto[port].value = 1
dut.an_adv_ability_usxgmii[port].value = 0x1601
await tb.reset()
tb.log.info("Wait for reset")
while int(dut.rx_rst_out[port].value):
await RisingEdge(dut.xcvr_ctrl_clk)
tb.log.info("Wait for block lock")
while not int(dut.rx_block_lock[port].value):
await RisingEdge(dut.xcvr_ctrl_clk)
for k in range(100):
await RisingEdge(dut.xcvr_ctrl_clk)
if 1:
for x in range(6):
cfg1 = 0x0001 | (x << 9) | ((x & 1) << 12) | ((x & 2) << 14)
cfg2 = cfg1
dut.an_adv_ability_usxgmii[port].value = cfg2
lp_cfg = await run_usxgmii_an(tb, port, cfg1)
for k in range(2000):
if not dut.an_running[port].value:
break
await RisingEdge(dut.tx_clk[port])
assert lp_cfg == cfg2 | 0x4000
assert not int(dut.an_running[port].value)
assert int(dut.an_complete[port].value)
assert not int(dut.an_timeout[port].value)
assert int(dut.an_usxgmii_mode[port].value)
assert int(dut.an_lp_adv_ability[port].value) == cfg1 | 0x4000
assert bool(dut.an_lp_usxgmii_link[port].value) == bool((cfg1 >> 15) & 1)
assert int(dut.an_lp_usxgmii_speed[port].value) == (cfg1 >> 9) & 0x7
assert bool(dut.an_res_full_duplex[port].value) == bool((cfg1 >> 12) & 1)
else:
lp_cfg = await run_usxgmii_an(tb, port, 0x9801)
for k in range(2000):
if not dut.an_running[port].value:
break
await RisingEdge(dut.tx_clk[port])
assert lp_cfg is None
for k in range(10):
await RisingEdge(dut.tx_clk[port])
def size_list():
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
@@ -1002,6 +1163,11 @@ if getattr(cocotb, 'top', None) is not None:
factory.add_option("ifg", [12])
factory.generate_tests()
if cocotb.top.USXGMII_EN.value:
for test in [run_test_usxgmii]:
factory = TestFactory(test)
factory.generate_tests()
# cocotb-test
@@ -1054,6 +1220,7 @@ def test_taxi_eth_mac_25g_us(request, data_w, combined_mac_pcs, low_latency, dic
parameters['QPLL1_EXT_CTRL'] = 0
parameters['COMBINED_MAC_PCS'] = combined_mac_pcs
parameters['DATA_W'] = data_w
parameters['USXGMII_EN'] = int(data_w == 32)
parameters['DIC_EN'] = dic_en
parameters['PTP_TS_EN'] = 1
parameters['PTP_TD_EN'] = parameters['PTP_TS_EN']

View File

@@ -43,6 +43,7 @@ module test_taxi_eth_mac_25g_us #
parameter logic [CNT-1:0] GT_RX_POLARITY = '0,
parameter logic COMBINED_MAC_PCS = 1'b1,
parameter DATA_W = 64,
parameter logic USXGMII_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TD_EN = PTP_TS_EN,
@@ -113,6 +114,23 @@ taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(TX_USER_W), .ID_EN(1), .ID_
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) m_axis_tx_cpl[CNT]();
taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(RX_USER_W)) m_axis_rx[CNT]();
logic an_en[CNT];
logic an_restart[CNT];
logic an_speedup[CNT];
logic an_timeout_en[CNT];
logic an_usxgmii_en[CNT];
logic an_usxgmii_auto[CNT];
logic an_intr[CNT];
logic an_running[CNT];
logic an_complete[CNT];
logic an_timeout[CNT];
logic an_usxgmii_mode[CNT];
logic [15:0] an_adv_ability_usxgmii[CNT];
logic [15:0] an_lp_adv_ability[CNT];
logic an_lp_usxgmii_link[CNT];
logic [2:0] an_lp_usxgmii_speed[CNT];
logic an_res_full_duplex[CNT];
logic ptp_clk;
logic ptp_rst;
logic ptp_sample_clk;
@@ -266,6 +284,7 @@ taxi_eth_mac_25g_us #(
.GT_RX_POLARITY(GT_RX_POLARITY),
.COMBINED_MAC_PCS(COMBINED_MAC_PCS),
.DATA_W(DATA_W),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TD_EN(PTP_TD_EN),
@@ -345,6 +364,26 @@ uut (
*/
.m_axis_rx(m_axis_rx),
/*
* USXGMII autonegotiation
*/
.an_en(an_en),
.an_restart(an_restart),
.an_speedup(an_speedup),
.an_timeout_en(an_timeout_en),
.an_usxgmii_en(an_usxgmii_en),
.an_usxgmii_auto(an_usxgmii_auto),
.an_intr(an_intr),
.an_running(an_running),
.an_complete(an_complete),
.an_timeout(an_timeout),
.an_usxgmii_mode(an_usxgmii_mode),
.an_adv_ability_usxgmii(an_adv_ability_usxgmii),
.an_lp_adv_ability(an_lp_adv_ability),
.an_lp_usxgmii_link(an_lp_usxgmii_link),
.an_lp_usxgmii_speed(an_lp_usxgmii_speed),
.an_res_full_duplex(an_res_full_duplex),
/*
* PTP
*/

View File

@@ -36,6 +36,7 @@ export PARAM_DATA_W := 64
export PARAM_HDR_W := 2
export PARAM_TX_GBX_IF_EN := 1
export PARAM_RX_GBX_IF_EN := $(PARAM_TX_GBX_IF_EN)
export PARAM_USXGMII_EN := 0
export PARAM_DIC_EN := 1
export PARAM_PTP_TS_EN := 1
export PARAM_PTP_TD_EN := $(PARAM_PTP_TS_EN)

View File

@@ -22,7 +22,7 @@ import cocotb_test.simulator
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.triggers import RisingEdge, Timer
from cocotb.utils import get_time_from_sim_steps
from cocotb.regression import TestFactory
@@ -106,6 +106,15 @@ class TB:
period_ns=self.ptp_clk_period
)
dut.an_en.setimmediatevalue(0)
dut.an_restart.setimmediatevalue(0)
dut.an_speedup.setimmediatevalue(1)
dut.an_timeout_en.setimmediatevalue(1)
dut.an_usxgmii_en.setimmediatevalue(0)
dut.an_usxgmii_auto.setimmediatevalue(1)
dut.an_usxgmii_5g.setimmediatevalue(0)
dut.an_adv_ability_usxgmii.setimmediatevalue(0x1601)
dut.stat_rx_fifo_drop.setimmediatevalue(0)
dut.cfg_tx_pad_en.setimmediatevalue(0)
@@ -850,6 +859,152 @@ async def run_test_pfc(dut, gbx_cfg=None, ifg=12):
await RisingEdge(dut.tx_clk)
async def run_usxgmii_an(tb, cfg):
# link timer scaled by 1000x for faster simulation
link_timer = Timer(1.6, 'us')
dut = tb.dut
for k in range(10):
tb.log.info("AN_RESTART")
tb.serdes_source.set_seq_os((0x0000 << 8) | 0x03)
await link_timer
tb.log.info("ABILITY_DETECT")
tb.serdes_source.set_seq_os(((cfg & ~0x4000) << 8) | 0x03)
tb.serdes_sink.get_os()
lp_cfg = None
while True:
await RisingEdge(dut.tx_clk)
lp_cfg = tb.serdes_sink.get_os()[0]
if lp_cfg is None:
continue
if lp_cfg & 0xff != 0x03:
continue
lp_cfg = lp_cfg >> 8
if tb.serdes_sink.get_os_match() and lp_cfg != 0:
break
tb.log.info("ACKNOWLEDGE_DETECT")
tb.serdes_source.set_seq_os(((cfg | 0x4000) << 8) | 0x03)
tb.serdes_sink.get_os()
lp_cfg_ack = None
while True:
await RisingEdge(dut.tx_clk)
lp_cfg_ack = tb.serdes_sink.get_os()[0]
if lp_cfg_ack is None:
continue
if lp_cfg_ack & 0xff != 0x03:
continue
lp_cfg_ack = lp_cfg_ack >> 8
if tb.serdes_sink.get_os_match() and lp_cfg_ack & 0x4000:
break
elif tb.serdes_sink.get_os_match() and lp_cfg_ack == 0:
break
if lp_cfg | 0x4000 != lp_cfg_ack:
tb.log.warning("AN inconsistent, restarting (0x%04x != 0x%04x)", lp_cfg | 0x4000, lp_cfg_ack)
continue
if lp_cfg_ack == 0:
tb.log.warning("AN restart requested")
continue
tb.log.info("COMPLETE_ACKNOWLEDGE")
await link_timer
tb.log.info("IDLE_DETECT")
tb.serdes_source.set_seq_os(None)
await link_timer
lp_cfg_ack2 = None
while True:
await RisingEdge(dut.tx_clk)
if tb.serdes_sink.get_idle_match():
break
lp_cfg_ack2 = tb.serdes_sink.get_os()[0]
if lp_cfg_ack2 is None:
continue
if lp_cfg_ack2 & 0xff != 0x03:
continue
lp_cfg_ack2 = lp_cfg_ack2 >> 8
if tb.serdes_sink.get_os_match() and (lp_cfg_ack2 == 0 or lp_cfg_ack != lp_cfg_ack2):
break
if lp_cfg_ack2 is not None and lp_cfg_ack != lp_cfg_ack2:
tb.log.warning("AN inconsistent, restarting (0x%04x != 0x%04x)", lp_cfg_ack, lp_cfg_ack2)
continue
if lp_cfg_ack2 == 0:
tb.log.warning("AN restart requested")
continue
tb.log.info("AN done")
return lp_cfg_ack
tb.log.warning("AN timed out")
tb.serdes_source.set_seq_os(None)
return None
async def run_test_usxgmii(dut, gbx_cfg=None):
tb = TB(dut, gbx_cfg)
dut.an_en.value = 1
dut.an_restart.value = 0
dut.an_speedup.value = 1
dut.an_timeout_en.value = 1
dut.an_usxgmii_en.value = 0
dut.an_usxgmii_auto.value = 1
dut.an_usxgmii_5g.value = 0
dut.an_adv_ability_usxgmii.value = 0x1601
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
if 1:
for x in range(6):
cfg1 = 0x0001 | (x << 9) | ((x & 1) << 12) | ((x & 2) << 14)
cfg2 = cfg1
dut.an_adv_ability_usxgmii.value = cfg2
lp_cfg = await run_usxgmii_an(tb, cfg1)
for k in range(2000):
if not dut.an_running.value:
break
await RisingEdge(dut.tx_clk)
assert lp_cfg == cfg2 | 0x4000
assert not int(dut.an_running.value)
assert int(dut.an_complete.value)
assert not int(dut.an_timeout.value)
assert int(dut.an_usxgmii_mode.value)
assert int(dut.an_lp_adv_ability.value) == cfg1 | 0x4000
assert bool(dut.an_lp_usxgmii_link.value) == bool((cfg1 >> 15) & 1)
assert int(dut.an_lp_usxgmii_speed.value) == (cfg1 >> 9) & 0x7
assert bool(dut.an_res_full_duplex.value) == bool((cfg1 >> 12) & 1)
else:
lp_cfg = await run_usxgmii_an(tb, 0x9801)
for k in range(2000):
if not dut.an_running.value:
break
await RisingEdge(dut.tx_clk)
assert lp_cfg is None
for k in range(10):
await RisingEdge(dut.tx_clk)
def size_list():
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
@@ -904,6 +1059,12 @@ if getattr(cocotb, 'top', None) is not None:
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
if cocotb.top.USXGMII_EN.value:
for test in [run_test_usxgmii]:
factory = TestFactory(test)
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
# cocotb-test
@@ -948,6 +1109,7 @@ def test_taxi_eth_mac_phy_10g(request, data_w, ptp_td_en, gbx_en, dic_en, pfc_en
parameters['HDR_W'] = 2
parameters['TX_GBX_IF_EN'] = gbx_en
parameters['RX_GBX_IF_EN'] = parameters['TX_GBX_IF_EN']
parameters['USXGMII_EN'] = int(data_w == 32)
parameters['DIC_EN'] = dic_en
parameters['PTP_TS_EN'] = 1
parameters['PTP_TD_EN'] = ptp_td_en

View File

@@ -22,6 +22,7 @@ module test_taxi_eth_mac_phy_10g #
parameter HDR_W = 2,
parameter logic TX_GBX_IF_EN = 1'b0,
parameter logic RX_GBX_IF_EN = TX_GBX_IF_EN,
parameter logic USXGMII_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
parameter logic PTP_TD_EN = PTP_TS_EN,
@@ -76,6 +77,24 @@ logic serdes_rx_hdr_valid;
logic serdes_rx_bitslip;
logic serdes_rx_reset_req;
logic an_en;
logic an_restart;
logic an_speedup;
logic an_timeout_en;
logic an_usxgmii_en;
logic an_usxgmii_auto;
logic an_usxgmii_5g;
logic an_intr;
logic an_running;
logic an_complete;
logic an_timeout;
logic an_usxgmii_mode;
logic [15:0] an_adv_ability_usxgmii;
logic [15:0] an_lp_adv_ability;
logic an_lp_usxgmii_link;
logic [2:0] an_lp_usxgmii_speed;
logic an_res_full_duplex;
logic ptp_clk;
logic ptp_rst;
logic ptp_sample_clk;
@@ -208,6 +227,7 @@ taxi_eth_mac_phy_10g #(
.HDR_W(HDR_W),
.TX_GBX_IF_EN(TX_GBX_IF_EN),
.RX_GBX_IF_EN(RX_GBX_IF_EN),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TD_EN(PTP_TD_EN),
@@ -266,6 +286,27 @@ uut (
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* USXGMII autonegotiation
*/
.an_en(an_en),
.an_restart(an_restart),
.an_speedup(an_speedup),
.an_timeout_en(an_timeout_en),
.an_usxgmii_en(an_usxgmii_en),
.an_usxgmii_auto(an_usxgmii_auto),
.an_usxgmii_5g(an_usxgmii_5g),
.an_intr(an_intr),
.an_running(an_running),
.an_complete(an_complete),
.an_timeout(an_timeout),
.an_usxgmii_mode(an_usxgmii_mode),
.an_adv_ability_usxgmii(an_adv_ability_usxgmii),
.an_lp_adv_ability(an_lp_adv_ability),
.an_lp_usxgmii_link(an_lp_usxgmii_link),
.an_lp_usxgmii_speed(an_lp_usxgmii_speed),
.an_res_full_duplex(an_res_full_duplex),
/*
* PTP
*/

View File

@@ -36,6 +36,7 @@ export PARAM_DATA_W := 64
export PARAM_HDR_W := 2
export PARAM_TX_GBX_IF_EN := 1
export PARAM_RX_GBX_IF_EN := $(PARAM_TX_GBX_IF_EN)
export PARAM_USXGMII_EN := 0
export PARAM_AXIS_DATA_W := $(PARAM_DATA_W)
export PARAM_DIC_EN := 1
export PARAM_PTP_TS_EN := 1

View File

@@ -19,7 +19,7 @@ import cocotb_test.simulator
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.triggers import RisingEdge, Timer
from cocotb.utils import get_time_from_sim_steps
from cocotb.regression import TestFactory
@@ -104,6 +104,15 @@ class TB:
period_ns=self.ptp_clk_period
)
dut.an_en.setimmediatevalue(0)
dut.an_restart.setimmediatevalue(0)
dut.an_speedup.setimmediatevalue(1)
dut.an_timeout_en.setimmediatevalue(1)
dut.an_usxgmii_en.setimmediatevalue(0)
dut.an_usxgmii_auto.setimmediatevalue(1)
dut.an_usxgmii_5g.setimmediatevalue(0)
dut.an_adv_ability_usxgmii.setimmediatevalue(0x1601)
dut.cfg_tx_pad_en.setimmediatevalue(0)
dut.cfg_tx_min_pkt_len.setimmediatevalue(0)
dut.cfg_tx_max_pkt_len.setimmediatevalue(0)
@@ -405,6 +414,152 @@ async def run_test_rx_frame_sync(dut, gbx_cfg=None):
await RisingEdge(dut.rx_clk)
async def run_usxgmii_an(tb, cfg):
# link timer scaled by 1000x for faster simulation
link_timer = Timer(1.6, 'us')
dut = tb.dut
for k in range(10):
tb.log.info("AN_RESTART")
tb.serdes_source.set_seq_os((0x0000 << 8) | 0x03)
await link_timer
tb.log.info("ABILITY_DETECT")
tb.serdes_source.set_seq_os(((cfg & ~0x4000) << 8) | 0x03)
tb.serdes_sink.get_os()
lp_cfg = None
while True:
await RisingEdge(dut.tx_clk)
lp_cfg = tb.serdes_sink.get_os()[0]
if lp_cfg is None:
continue
if lp_cfg & 0xff != 0x03:
continue
lp_cfg = lp_cfg >> 8
if tb.serdes_sink.get_os_match() and lp_cfg != 0:
break
tb.log.info("ACKNOWLEDGE_DETECT")
tb.serdes_source.set_seq_os(((cfg | 0x4000) << 8) | 0x03)
tb.serdes_sink.get_os()
lp_cfg_ack = None
while True:
await RisingEdge(dut.tx_clk)
lp_cfg_ack = tb.serdes_sink.get_os()[0]
if lp_cfg_ack is None:
continue
if lp_cfg_ack & 0xff != 0x03:
continue
lp_cfg_ack = lp_cfg_ack >> 8
if tb.serdes_sink.get_os_match() and lp_cfg_ack & 0x4000:
break
elif tb.serdes_sink.get_os_match() and lp_cfg_ack == 0:
break
if lp_cfg | 0x4000 != lp_cfg_ack:
tb.log.warning("AN inconsistent, restarting (0x%04x != 0x%04x)", lp_cfg | 0x4000, lp_cfg_ack)
continue
if lp_cfg_ack == 0:
tb.log.warning("AN restart requested")
continue
tb.log.info("COMPLETE_ACKNOWLEDGE")
await link_timer
tb.log.info("IDLE_DETECT")
tb.serdes_source.set_seq_os(None)
await link_timer
lp_cfg_ack2 = None
while True:
await RisingEdge(dut.tx_clk)
if tb.serdes_sink.get_idle_match():
break
lp_cfg_ack2 = tb.serdes_sink.get_os()[0]
if lp_cfg_ack2 is None:
continue
if lp_cfg_ack2 & 0xff != 0x03:
continue
lp_cfg_ack2 = lp_cfg_ack2 >> 8
if tb.serdes_sink.get_os_match() and (lp_cfg_ack2 == 0 or lp_cfg_ack != lp_cfg_ack2):
break
if lp_cfg_ack2 is not None and lp_cfg_ack != lp_cfg_ack2:
tb.log.warning("AN inconsistent, restarting (0x%04x != 0x%04x)", lp_cfg_ack, lp_cfg_ack2)
continue
if lp_cfg_ack2 == 0:
tb.log.warning("AN restart requested")
continue
tb.log.info("AN done")
return lp_cfg_ack
tb.log.warning("AN timed out")
tb.serdes_source.set_seq_os(None)
return None
async def run_test_usxgmii(dut, gbx_cfg=None):
tb = TB(dut, gbx_cfg)
dut.an_en.value = 1
dut.an_restart.value = 0
dut.an_speedup.value = 1
dut.an_timeout_en.value = 1
dut.an_usxgmii_en.value = 0
dut.an_usxgmii_auto.value = 1
dut.an_usxgmii_5g.value = 0
dut.an_adv_ability_usxgmii.value = 0x1601
await tb.reset()
for k in range(100):
await RisingEdge(dut.tx_clk)
if 1:
for x in range(6):
cfg1 = 0x0001 | (x << 9) | ((x & 1) << 12) | ((x & 2) << 14)
cfg2 = cfg1
dut.an_adv_ability_usxgmii.value = cfg2
lp_cfg = await run_usxgmii_an(tb, cfg1)
for k in range(2000):
if not dut.an_running.value:
break
await RisingEdge(dut.tx_clk)
assert lp_cfg == cfg2 | 0x4000
assert not int(dut.an_running.value)
assert int(dut.an_complete.value)
assert not int(dut.an_timeout.value)
assert int(dut.an_usxgmii_mode.value)
assert int(dut.an_lp_adv_ability.value) == cfg1 | 0x4000
assert bool(dut.an_lp_usxgmii_link.value) == bool((cfg1 >> 15) & 1)
assert int(dut.an_lp_usxgmii_speed.value) == (cfg1 >> 9) & 0x7
assert bool(dut.an_res_full_duplex.value) == bool((cfg1 >> 12) & 1)
else:
lp_cfg = await run_usxgmii_an(tb, 0x9801)
for k in range(2000):
if not dut.an_running.value:
break
await RisingEdge(dut.tx_clk)
assert lp_cfg is None
for k in range(10):
await RisingEdge(dut.tx_clk)
def size_list():
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
@@ -445,6 +600,12 @@ if getattr(cocotb, 'top', None) is not None:
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
if cocotb.top.USXGMII_EN.value:
for test in [run_test_usxgmii]:
factory = TestFactory(test)
factory.add_option("gbx_cfg", gbx_cfgs)
factory.generate_tests()
# cocotb-test
@@ -488,6 +649,7 @@ def test_taxi_eth_mac_phy_10g_fifo(request, data_w, gbx_en, dic_en):
parameters['HDR_W'] = 2
parameters['TX_GBX_IF_EN'] = gbx_en
parameters['RX_GBX_IF_EN'] = parameters['TX_GBX_IF_EN']
parameters['USXGMII_EN'] = int(data_w == 32)
parameters['AXIS_DATA_W'] = parameters['DATA_W']
parameters['DIC_EN'] = dic_en
parameters['PTP_TS_EN'] = 1

View File

@@ -22,6 +22,7 @@ module test_taxi_eth_mac_phy_10g_fifo #
parameter HDR_W = 2,
parameter logic TX_GBX_IF_EN = 1'b0,
parameter logic RX_GBX_IF_EN = TX_GBX_IF_EN,
parameter logic USXGMII_EN = 1'b1,
parameter AXIS_DATA_W = 8,
parameter logic DIC_EN = 1'b1,
parameter logic PTP_TS_EN = 1'b0,
@@ -90,6 +91,24 @@ logic serdes_rx_hdr_valid;
logic serdes_rx_bitslip;
logic serdes_rx_reset_req;
logic an_en;
logic an_restart;
logic an_speedup;
logic an_timeout_en;
logic an_usxgmii_en;
logic an_usxgmii_auto;
logic an_usxgmii_5g;
logic an_intr;
logic an_running;
logic an_complete;
logic an_timeout;
logic an_usxgmii_mode;
logic [15:0] an_adv_ability_usxgmii;
logic [15:0] an_lp_adv_ability;
logic an_lp_usxgmii_link;
logic [2:0] an_lp_usxgmii_speed;
logic an_res_full_duplex;
logic ptp_clk;
logic ptp_rst;
logic ptp_sample_clk;
@@ -137,6 +156,7 @@ taxi_eth_mac_phy_10g_fifo #(
.HDR_W(HDR_W),
.TX_GBX_IF_EN(TX_GBX_IF_EN),
.RX_GBX_IF_EN(RX_GBX_IF_EN),
.USXGMII_EN(USXGMII_EN),
.DIC_EN(DIC_EN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TD_EN(PTP_TD_EN),
@@ -208,6 +228,27 @@ uut (
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* USXGMII autonegotiation
*/
.an_en(an_en),
.an_restart(an_restart),
.an_speedup(an_speedup),
.an_timeout_en(an_timeout_en),
.an_usxgmii_en(an_usxgmii_en),
.an_usxgmii_auto(an_usxgmii_auto),
.an_usxgmii_5g(an_usxgmii_5g),
.an_intr(an_intr),
.an_running(an_running),
.an_complete(an_complete),
.an_timeout(an_timeout),
.an_usxgmii_mode(an_usxgmii_mode),
.an_adv_ability_usxgmii(an_adv_ability_usxgmii),
.an_lp_adv_ability(an_lp_adv_ability),
.an_lp_usxgmii_link(an_lp_usxgmii_link),
.an_lp_usxgmii_speed(an_lp_usxgmii_speed),
.an_res_full_duplex(an_res_full_duplex),
/*
* PTP clock
*/