From 3a0c0057d40f86713f8a2dba301b1824b8c6e6a0 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Wed, 17 Jun 2026 16:34:44 -0700 Subject: [PATCH] eth: Suport AN config reg exchange in GMII-BASE-X encode/decode logic Signed-off-by: Alex Forencich --- src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv | 12 ++- src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv | 12 ++- src/eth/rtl/taxi_gmii_basex_dec.sv | 101 +++++++++++++++++- src/eth/rtl/taxi_gmii_basex_enc.sv | 57 +++++++++- src/eth/tb/taxi_gmii_basex_dec/Makefile | 1 + .../test_taxi_gmii_basex_dec.py | 36 +++++++ src/eth/tb/taxi_gmii_basex_enc/Makefile | 1 + .../test_taxi_gmii_basex_enc.py | 40 +++++++ 8 files changed, 252 insertions(+), 8 deletions(-) diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv b/src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv index 5deb7b5..1636b99 100644 --- a/src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv +++ b/src/eth/rtl/taxi_eth_phy_1g_basex_rx.sv @@ -114,7 +114,8 @@ rx_if_inst ( taxi_gmii_basex_dec #( .DATA_W(DATA_W), .CTRL_W(CTRL_W), - .GBX_IF_EN(GBX_IF_EN) + .GBX_IF_EN(GBX_IF_EN), + .AN_EN(1'b0) ) dec_inst ( .clk(clk), @@ -135,6 +136,15 @@ dec_inst ( .gmii_rx_er(gmii_rx_er), .gmii_rx_valid(gmii_rx_valid), + /* + * AN config register + */ + .rx_an_cfg(), + .rx_an_cfg_valid(), + .rx_an_ability_match(), + .rx_an_ack_match(), + .rx_an_idle_match(), + /* * Status */ diff --git a/src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv b/src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv index 3e01485..827a344 100644 --- a/src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv +++ b/src/eth/rtl/taxi_eth_phy_1g_basex_tx.sv @@ -70,7 +70,8 @@ taxi_gmii_basex_enc #( .DATA_W(DATA_W), .CTRL_W(CTRL_W), .GBX_IF_EN(GBX_IF_EN), - .GBX_CNT(1) + .GBX_CNT(1), + .AN_EN(1'b0) ) enc_inst ( .clk(clk), @@ -93,7 +94,14 @@ enc_inst ( .encoded_tx_data_dm(encoded_tx_data_dm), .encoded_tx_data_dv(encoded_tx_data_dv), .encoded_tx_data_valid(encoded_tx_data_valid), - .tx_gbx_sync_out(tx_gbx_sync_int) + .tx_gbx_sync_out(tx_gbx_sync_int), + + /* + * AN config register + */ + .tx_an_cfg('0), + .tx_an_cfg_valid(1'b0), + .tx_an_cfg_ready() ); taxi_eth_phy_1g_basex_tx_if #( diff --git a/src/eth/rtl/taxi_gmii_basex_dec.sv b/src/eth/rtl/taxi_gmii_basex_dec.sv index 01797d1..e735ed7 100644 --- a/src/eth/rtl/taxi_gmii_basex_dec.sv +++ b/src/eth/rtl/taxi_gmii_basex_dec.sv @@ -19,7 +19,8 @@ module taxi_gmii_basex_dec # ( parameter DATA_W = 16, parameter CTRL_W = (DATA_W/8), - parameter logic GBX_IF_EN = 1'b0 + parameter logic GBX_IF_EN = 1'b0, + parameter logic AN_EN = 1'b1 ) ( input wire logic clk, @@ -40,6 +41,15 @@ module taxi_gmii_basex_dec # output wire logic [CTRL_W-1:0] gmii_rx_er, output wire logic gmii_rx_valid, + /* + * AN config register + */ + output wire logic [15:0] rx_an_cfg, + output wire logic rx_an_cfg_valid, + output wire logic rx_an_ability_match, + output wire logic rx_an_ack_match, + output wire logic rx_an_idle_match, + /* * Status */ @@ -82,12 +92,23 @@ logic frame_reg = 1'b0, frame_next; logic frame_cyc; logic odd_reg = 1'b0, odd_next; logic odd_cyc; +logic k28p5_reg = 1'b0, k28p5_next; +logic k28p5_cyc; +logic c_reg = 1'b0, c_next; +logic c_cyc; logic [DATA_W-1:0] gmii_rxd_reg = '0, gmii_rxd_next; logic [CTRL_W-1:0] gmii_rx_dv_reg = '0, gmii_rx_dv_next; logic [CTRL_W-1:0] gmii_rx_er_reg = '0, gmii_rx_er_next; logic gmii_rx_valid_reg = '0, gmii_rx_valid_next; +logic [15:0] rx_an_cfg_reg = '0, rx_an_cfg_next; +logic rx_an_cfg_valid_reg = 1'b0, rx_an_cfg_valid_next; +logic an_cfg_match_reg = 1'b0, an_cfg_match_next; +logic [1:0] an_ability_match_cnt_reg = '0, an_ability_match_cnt_next; +logic [1:0] an_ack_match_cnt_reg = '0, an_ack_match_cnt_next; +logic [1:0] an_idle_match_cnt_reg = '0, an_idle_match_cnt_next; + logic stat_rx_err_bad_block_reg = '0, stat_rx_err_bad_block_next; logic stat_rx_err_framing_reg = '0, stat_rx_err_framing_next; @@ -96,23 +117,40 @@ assign gmii_rx_dv = gmii_rx_dv_reg; assign gmii_rx_er = gmii_rx_er_reg; assign gmii_rx_valid = gmii_rx_valid_reg; +assign rx_an_cfg = AN_EN ? rx_an_cfg_reg : '0; +assign rx_an_cfg_valid = AN_EN ? rx_an_cfg_valid_reg : 1'b0; +assign rx_an_ability_match = AN_EN ? &an_ability_match_cnt_reg : 1'b0; +assign rx_an_ack_match = AN_EN ? &an_ack_match_cnt_reg : 1'b0; +assign rx_an_idle_match = AN_EN ? &an_idle_match_cnt_reg : 1'b0; + assign stat_rx_err_bad_block = stat_rx_err_bad_block_reg; assign stat_rx_err_framing = stat_rx_err_framing_reg; always_comb begin frame_next = frame_reg; odd_next = odd_reg; + k28p5_next = k28p5_reg; + c_next = c_reg; gmii_rxd_next = '0; gmii_rx_dv_next = '0; gmii_rx_er_next = '0; gmii_rx_valid_next = 1'b0; + rx_an_cfg_next = rx_an_cfg_reg; + rx_an_cfg_valid_next = 1'b0; + an_cfg_match_next = an_cfg_match_reg; + an_ability_match_cnt_next = an_ability_match_cnt_reg; + an_ack_match_cnt_next = an_ack_match_cnt_reg; + an_idle_match_cnt_next = an_idle_match_cnt_reg; + stat_rx_err_bad_block_next = 1'b0; stat_rx_err_framing_next = 1'b0; frame_cyc = frame_reg; odd_cyc = odd_reg; + k28p5_cyc = CTRL_W == 0 ? 1'b0 : k28p5_reg; + c_cyc = c_reg; if (encoded_rx_data_valid) begin // loop over bytes @@ -123,6 +161,7 @@ always_comb begin if (encoded_rx_data_k[seg]) begin // Kx.y + c_cyc = 1'b0; if (encoded_rx_data[seg*8 +: 8] == K(28,5)) begin // K28.5 odd_cyc = 1'b0; // sync @@ -161,14 +200,58 @@ always_comb begin gmii_rxd_next[seg*8 +: 8] = encoded_rx_data[seg*8 +: 8]; gmii_rx_dv_next[seg] = 1'b1; gmii_rx_er_next[seg] = 1'b0; + end else if (AN_EN && k28p5_cyc && (encoded_rx_data[seg*8 +: 8] == D(5,6) || encoded_rx_data[seg*8 +: 8] == D(16,2))) begin + // I1/I2 + an_ability_match_cnt_next = '0; + an_ack_match_cnt_next = '0; + if (!(&an_idle_match_cnt_next)) begin + an_idle_match_cnt_next = an_idle_match_cnt_next + 1; + end + end else if (AN_EN && k28p5_cyc && (encoded_rx_data[seg*8 +: 8] == D(21,5) || encoded_rx_data[seg*8 +: 8] == D(2,2))) begin + // C1/C2 + c_cyc = 1'b1; + end else if (AN_EN && c_cyc) begin + if (!odd_cyc) begin + rx_an_cfg_next[7:0] = encoded_rx_data[seg*8 +: 8]; + an_cfg_match_next = rx_an_cfg_next[7:0] == encoded_rx_data[seg*8 +: 8]; + an_idle_match_cnt_next = '0; + end else begin + rx_an_cfg_next[15:8] = encoded_rx_data[seg*8 +: 8]; + rx_an_cfg_valid_next = 1'b1; + if (an_cfg_match_next && ((rx_an_cfg_next[15:8] ^ encoded_rx_data[seg*8 +: 8]) & 8'h40) == 0) begin + if (!(&an_ability_match_cnt_next)) begin + an_ability_match_cnt_next = an_ability_match_cnt_next + 1; + end + end else begin + an_ability_match_cnt_next = '0; + end + if (an_cfg_match_next && rx_an_cfg_next[14] && rx_an_cfg_next[15:8] == encoded_rx_data[seg*8 +: 8]) begin + if (!(&an_ack_match_cnt_next)) begin + an_ack_match_cnt_next = an_ack_match_cnt_next + 1; + end + end else begin + an_ack_match_cnt_next = '0; + end + an_idle_match_cnt_next = '0; + c_cyc = 1'b0; + end end end + // detect K28.5 symbols + if (encoded_rx_data_k[seg] && encoded_rx_data[seg*8 +: 8] == K(28,5)) begin + k28p5_cyc = 1'b1; + end else begin + k28p5_cyc = 1'b0; + end + odd_cyc = !odd_cyc; end frame_next = frame_cyc; odd_next = odd_cyc; + k28p5_next = k28p5_cyc; + c_next = c_cyc; gmii_rx_valid_next = 1'b1; end @@ -177,24 +260,40 @@ end always_ff @(posedge clk) begin frame_reg <= frame_next; odd_reg <= odd_next; + k28p5_reg <= k28p5_next; + c_reg <= c_next; gmii_rxd_reg <= gmii_rxd_next; gmii_rx_dv_reg <= gmii_rx_dv_next; gmii_rx_er_reg <= gmii_rx_er_next; gmii_rx_valid_reg <= gmii_rx_valid_next; + rx_an_cfg_reg <= rx_an_cfg_next; + rx_an_cfg_valid_reg <= rx_an_cfg_valid_next; + an_cfg_match_reg <= an_cfg_match_next; + an_ability_match_cnt_reg <= an_ability_match_cnt_next; + an_ack_match_cnt_reg <= an_ack_match_cnt_next; + an_idle_match_cnt_reg <= an_idle_match_cnt_next; + stat_rx_err_bad_block_reg <= stat_rx_err_bad_block_next; stat_rx_err_framing_reg <= stat_rx_err_framing_next; if (rst) begin frame_reg <= 1'b0; odd_reg <= 1'b0; + k28p5_reg <= 1'b0; + c_reg <= 1'b0; gmii_rxd_reg <= '0; gmii_rx_dv_reg <= '0; gmii_rx_er_reg <= '0; gmii_rx_valid_reg <= 1'b0; + rx_an_cfg_valid_reg <= 1'b0; + an_ability_match_cnt_reg <= '0; + an_ack_match_cnt_reg <= '0; + an_idle_match_cnt_reg <= '0; + stat_rx_err_bad_block_reg <= 1'b0; stat_rx_err_framing_reg <= 1'b0; end diff --git a/src/eth/rtl/taxi_gmii_basex_enc.sv b/src/eth/rtl/taxi_gmii_basex_enc.sv index cce3505..10cb3e6 100644 --- a/src/eth/rtl/taxi_gmii_basex_enc.sv +++ b/src/eth/rtl/taxi_gmii_basex_enc.sv @@ -20,7 +20,8 @@ module taxi_gmii_basex_enc # parameter DATA_W = 16, parameter CTRL_W = (DATA_W/8), parameter logic GBX_IF_EN = 1'b0, - parameter GBX_CNT = 1 + parameter GBX_CNT = 1, + parameter logic AN_EN = 1'b1 ) ( input wire logic clk, @@ -43,7 +44,14 @@ module taxi_gmii_basex_enc # output wire logic [CTRL_W-1:0] encoded_tx_data_dm, output wire logic [CTRL_W-1:0] encoded_tx_data_dv, output wire logic encoded_tx_data_valid, - output wire logic [GBX_CNT-1:0] tx_gbx_sync_out + output wire logic [GBX_CNT-1:0] tx_gbx_sync_out, + + /* + * AN config register + */ + input wire logic [15:0] tx_an_cfg = '0, + input wire logic tx_an_cfg_valid = 1'b0, + output wire logic tx_an_cfg_ready ); // check configuration @@ -137,6 +145,10 @@ logic odd_reg = 1'b0, odd_next; logic odd_cyc; logic cext_reg = 1'b0, cext_next; logic cext_cyc; +logic an_cfg_reg = 1'b0, an_cfg_next; +logic an_cfg_cyc; +logic an_phase_reg = 1'b0, an_phase_next; +logic an_phase_cyc; logic rd_reg = 1'b0, rd_next; logic rd_cyc; @@ -147,6 +159,8 @@ logic [CTRL_W-1:0] encoded_tx_data_dv_reg = '0, encoded_tx_data_dv_next; logic encoded_tx_data_valid_reg = '0, encoded_tx_data_valid_next; logic [GBX_CNT-1:0] tx_gbx_sync_reg = '0, tx_gbx_sync_next; +logic tx_an_cfg_ready_reg = 1'b0, tx_an_cfg_ready_next; + assign encoded_tx_data = encoded_tx_data_reg; assign encoded_tx_data_k = encoded_tx_data_k_reg; assign encoded_tx_data_dm = encoded_tx_data_dm_reg; @@ -154,10 +168,14 @@ assign encoded_tx_data_dv = encoded_tx_data_dv_reg; assign encoded_tx_data_valid = encoded_tx_data_valid_reg; assign tx_gbx_sync_out = tx_gbx_sync_reg; +assign tx_an_cfg_ready = AN_EN ? tx_an_cfg_ready_reg : 1'b0; + always_comb begin frame_next = frame_reg; odd_next = odd_reg; cext_next = cext_reg; + an_cfg_next = an_cfg_reg; + an_phase_next = an_phase_reg; rd_next = rd_reg; encoded_tx_data_next = '0; @@ -166,13 +184,17 @@ always_comb begin encoded_tx_data_dv_next = '0; encoded_tx_data_valid_next = '0; + tx_gbx_sync_next = tx_gbx_sync_in; + + tx_an_cfg_ready_next = 1'b0; + frame_cyc = frame_reg; odd_cyc = odd_reg; cext_cyc = cext_reg; + an_cfg_cyc = an_cfg_reg; + an_phase_cyc = an_phase_reg; rd_cyc = rd_reg; - tx_gbx_sync_next = tx_gbx_sync_in; - if (gmii_tx_valid) begin // loop over bytes for (integer seg = 0; seg < CTRL_W; seg = seg + 1) begin @@ -198,12 +220,29 @@ always_comb begin encoded_tx_data_k_next[seg] = 1'b1; cext_cyc = 1'b1; end + end else if (AN_EN && an_cfg_cyc) begin + // config reg + if (!odd_cyc) begin + encoded_tx_data_next[seg*8 +: 8] = tx_an_cfg[7:0]; + encoded_tx_data_k_next[seg] = 1'b0; + end else begin + encoded_tx_data_next[seg*8 +: 8] = tx_an_cfg[15:8]; + encoded_tx_data_k_next[seg] = 1'b0; + tx_an_cfg_ready_next = 1'b1; + an_phase_cyc = !an_phase_cyc; + an_cfg_cyc = 1'b0; + end end else begin if (gmii_tx_en[seg] && odd_cyc == 0) begin // start of frame frame_cyc = 1'b1; encoded_tx_data_next[seg*8 +: 8] = CTRL_S; encoded_tx_data_k_next[seg] = 1'b1; + end else if (AN_EN && tx_an_cfg_valid && odd_cyc == 1) begin + // config reg + an_cfg_cyc = 1'b1; + encoded_tx_data_next[seg*8 +: 8] = an_phase_reg ? D(2,2) : D(21,5); + encoded_tx_data_k_next[seg] = 1'b0; end else begin if (cext_cyc) begin // carrier extend @@ -235,6 +274,8 @@ always_comb begin frame_next = frame_cyc; odd_next = odd_cyc; cext_next = cext_cyc; + an_cfg_next = an_cfg_cyc; + an_phase_next = an_phase_cyc; rd_next = rd_cyc; encoded_tx_data_valid_next = 1'b1; @@ -245,6 +286,8 @@ always_ff @(posedge clk) begin frame_reg <= frame_next; odd_reg <= odd_next; cext_reg <= cext_next; + an_cfg_reg <= an_cfg_next; + an_phase_reg <= an_phase_next; rd_reg <= rd_next; encoded_tx_data_reg <= encoded_tx_data_next; @@ -254,10 +297,14 @@ always_ff @(posedge clk) begin encoded_tx_data_valid_reg <= encoded_tx_data_valid_next; tx_gbx_sync_reg <= tx_gbx_sync_next; + tx_an_cfg_ready_reg <= tx_an_cfg_ready_next; + if (rst) begin frame_reg <= 1'b0; odd_reg <= 1'b0; cext_reg <= 1'b0; + an_cfg_reg <= 1'b0; + an_phase_reg <= 1'b0; rd_reg <= 1'b0; encoded_tx_data_reg <= '0; @@ -266,6 +313,8 @@ always_ff @(posedge clk) begin encoded_tx_data_dv_reg <= '0; encoded_tx_data_valid_reg <= 1'b0; tx_gbx_sync_reg <= '0; + + tx_an_cfg_ready_reg <= 1'b0; end end diff --git a/src/eth/tb/taxi_gmii_basex_dec/Makefile b/src/eth/tb/taxi_gmii_basex_dec/Makefile index d0a0477..d03ff6c 100644 --- a/src/eth/tb/taxi_gmii_basex_dec/Makefile +++ b/src/eth/tb/taxi_gmii_basex_dec/Makefile @@ -33,6 +33,7 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES))) # module parameters export PARAM_DATA_W := 16 export PARAM_GBX_IF_EN := 0 +export PARAM_AN_EN := "1'b1" ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/src/eth/tb/taxi_gmii_basex_dec/test_taxi_gmii_basex_dec.py b/src/eth/tb/taxi_gmii_basex_dec/test_taxi_gmii_basex_dec.py index 1fa8a4a..8fec9a2 100644 --- a/src/eth/tb/taxi_gmii_basex_dec/test_taxi_gmii_basex_dec.py +++ b/src/eth/tb/taxi_gmii_basex_dec/test_taxi_gmii_basex_dec.py @@ -101,6 +101,38 @@ async def run_test(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, i await RisingEdge(dut.clk) +async def run_test_an(dut, gbx_cfg=None): + + tb = TB(dut, gbx_cfg) + + await tb.reset() + + for k in range(16): + an_cfg = 1 << k + + tb.source.set_an_cfg(an_cfg) + + for k in range(20): + await RisingEdge(dut.clk) + + assert int(dut.rx_an_cfg.value) == an_cfg + assert int(dut.rx_an_ability_match.value) + assert int(dut.rx_an_ack_match.value) == bool(an_cfg & 0x4000) + assert not int(dut.rx_an_idle_match.value) + + tb.source.set_an_cfg(None) + + for k in range(20): + await RisingEdge(dut.clk) + + assert not int(dut.rx_an_ability_match.value) + assert not int(dut.rx_an_ack_match.value) + assert int(dut.rx_an_idle_match.value) + + for k in range(10): + await RisingEdge(dut.clk) + + def size_list(): return list(range(60, 128)) + [512, 1514, 9214] + [60]*10 + [i for i in range(64, 73) for k in range(8)] @@ -118,6 +150,9 @@ if getattr(cocotb, 'top', None) is not None: factory.add_option("pre_len", [8, 7]) factory.generate_tests() + factory = TestFactory(run_test_an) + factory.generate_tests() + # cocotb-test @@ -156,6 +191,7 @@ def test_taxi_gmii_basex_dec(request, data_w): parameters['DATA_W'] = data_w parameters['GBX_IF_EN'] = 0 + parameters['AN_EN'] = "1'b1" extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} diff --git a/src/eth/tb/taxi_gmii_basex_enc/Makefile b/src/eth/tb/taxi_gmii_basex_enc/Makefile index 1874fec..da6b873 100644 --- a/src/eth/tb/taxi_gmii_basex_enc/Makefile +++ b/src/eth/tb/taxi_gmii_basex_enc/Makefile @@ -33,6 +33,7 @@ VERILOG_SOURCES := $(call uniq_base,$(call process_f_files,$(VERILOG_SOURCES))) # module parameters export PARAM_DATA_W := 16 export PARAM_GBX_IF_EN := 0 +export PARAM_AN_EN := "1'b1" ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/src/eth/tb/taxi_gmii_basex_enc/test_taxi_gmii_basex_enc.py b/src/eth/tb/taxi_gmii_basex_enc/test_taxi_gmii_basex_enc.py index 70a61b4..0f0f840 100644 --- a/src/eth/tb/taxi_gmii_basex_enc/test_taxi_gmii_basex_enc.py +++ b/src/eth/tb/taxi_gmii_basex_enc/test_taxi_gmii_basex_enc.py @@ -63,6 +63,9 @@ class TB: gbx_cfg=gbx_cfg ) + dut.tx_an_cfg.setimmediatevalue(0) + dut.tx_an_cfg_valid.setimmediatevalue(0) + async def reset(self): self.dut.rst.setimmediatevalue(0) await RisingEdge(self.dut.clk) @@ -103,6 +106,39 @@ async def run_test(dut, gbx_cfg=None, payload_lengths=None, payload_data=None, i await RisingEdge(dut.clk) +async def run_test_an(dut, gbx_cfg=None): + + tb = TB(dut, gbx_cfg) + + await tb.reset() + + for k in range(16): + an_cfg = 1 << k + + dut.tx_an_cfg.value = an_cfg + dut.tx_an_cfg_valid.value = 1 + + for k in range(20): + await RisingEdge(dut.clk) + + assert tb.sink.get_an_cfg() == an_cfg + assert tb.sink.get_an_ability_match() + assert tb.sink.get_an_ack_match() == bool(an_cfg & 0x4000) + assert not tb.sink.get_an_idle_match() + + dut.tx_an_cfg_valid.value = 0 + + for k in range(20): + await RisingEdge(dut.clk) + + assert not tb.sink.get_an_ability_match() + assert not tb.sink.get_an_ack_match() + assert tb.sink.get_an_idle_match() + + for k in range(10): + await RisingEdge(dut.clk) + + def size_list(): return list(range(60, 128)) + [512, 1514, 9214] + [60]*10 + [i for i in range(64, 73) for k in range(8)] @@ -120,6 +156,9 @@ if getattr(cocotb, 'top', None) is not None: factory.add_option("pre_len", [8, 7]) factory.generate_tests() + factory = TestFactory(run_test_an) + factory.generate_tests() + # cocotb-test @@ -158,6 +197,7 @@ def test_taxi_gmii_basex_enc(request, data_w): parameters['DATA_W'] = data_w parameters['GBX_IF_EN'] = 0 + parameters['AN_EN'] = "1'b1" extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}