eth: Suport AN config reg exchange in GMII-BASE-X encode/decode logic

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-06-17 16:34:44 -07:00
parent f4a986b4fd
commit 3a0c0057d4
8 changed files with 252 additions and 8 deletions

View File

@@ -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
*/

View File

@@ -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 #(

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()}

View File

@@ -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

View File

@@ -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()}