eth: Support ordered sets in XGMII-BASE-R encode/decode logic

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-06-18 22:02:58 -07:00
parent 3c2c86e35f
commit 62c544444b
6 changed files with 173 additions and 1 deletions

View File

@@ -146,6 +146,13 @@ xgmii_baser_dec_inst (
.xgmii_rxc(xgmii_rxc),
.xgmii_rx_valid(xgmii_rx_valid),
/*
* Ordered sets
*/
.rx_os(),
.rx_os_sig(),
.rx_os_valid(),
/*
* Status
*/

View File

@@ -97,6 +97,14 @@ xgmii_baser_enc_inst (
.encoded_tx_hdr_valid(encoded_tx_hdr_valid),
.tx_gbx_sync_out(tx_gbx_sync_int),
/*
* Ordered sets
*/
.tx_os('0),
.tx_os_sig(1'b0),
.tx_os_valid(1'b0),
.tx_os_ready(),
/*
* Status
*/

View File

@@ -41,6 +41,13 @@ module taxi_xgmii_baser_dec #
output wire logic [CTRL_W-1:0] xgmii_rxc,
output wire logic xgmii_rx_valid,
/*
* Ordered sets
*/
output wire logic [23:0] rx_os = '0,
output wire logic rx_os_sig = 1'b0,
output wire logic rx_os_valid = 1'b0,
/*
* Status
*/
@@ -130,6 +137,10 @@ logic [DATA_W_INT-1:0] xgmii_rxd_reg = '0, xgmii_rxd_next;
logic [CTRL_W_INT-1:0] xgmii_rxc_reg = '0, xgmii_rxc_next;
logic [SEG_CNT-1:0] xgmii_rx_valid_reg = '0, xgmii_rx_valid_next;
logic [23:0] rx_os_reg = '0, rx_os_next;
logic rx_os_sig_reg = 1'b0, rx_os_sig_next;
logic rx_os_valid_reg = 1'b0, rx_os_valid_next;
logic rx_bad_block_reg = 1'b0, rx_bad_block_next;
logic rx_sequence_error_reg = 1'b0, rx_sequence_error_next;
logic frame_reg = 1'b0, frame_next;
@@ -138,6 +149,10 @@ assign xgmii_rxd = xgmii_rxd_reg[DATA_W-1:0];
assign xgmii_rxc = xgmii_rxc_reg[CTRL_W-1:0];
assign xgmii_rx_valid = GBX_IF_EN ? xgmii_rx_valid_reg[0] : 1'b1;
assign rx_os = rx_os_reg;
assign rx_os_sig = rx_os_sig_reg;
assign rx_os_valid = rx_os_valid_reg;
assign rx_bad_block = rx_bad_block_reg;
assign rx_sequence_error = rx_sequence_error_reg;
@@ -177,6 +192,11 @@ always_comb begin
xgmii_rxd_next = {CTRL_W_INT{XGMII_ERROR}};
xgmii_rxc_next = '1;
xgmii_rx_valid_next = '0;
rx_os_next = '0;
rx_os_sig_next = 1'b0;
rx_os_valid_next = 1'b0;
rx_bad_block_next = 1'b0;
rx_sequence_error_next = 1'b0;
frame_next = frame_reg;
@@ -263,9 +283,19 @@ always_comb begin
xgmii_rxc_next[3:0] = 4'hf;
xgmii_rxd_next[63:40] = encoded_rx_data_int[63:40];
xgmii_rxc_next[7:4] = 4'h1;
rx_os_next[7:0] = encoded_rx_data_int[63:56];
rx_os_next[15:8] = encoded_rx_data_int[55:48];
rx_os_next[23:16] = encoded_rx_data_int[47:40];
if (encoded_rx_data_int[39:36] == O_SEQ_OS) begin
xgmii_rxd_next[39:32] = XGMII_SEQ_OS;
rx_bad_block_next = decode_err[3:0] != 0;
rx_os_sig_next = 1'b0;
rx_os_valid_next = 1'b1;
end else if (encoded_rx_data_int[39:36] == O_SIG_OS) begin
xgmii_rxd_next[39:32] = XGMII_SIG_OS;
rx_bad_block_next = decode_err[3:0] != 0;
rx_os_sig_next = 1'b1;
rx_os_valid_next = 1'b1;
end else begin
xgmii_rxd_next[39:32] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
@@ -283,9 +313,19 @@ always_comb begin
// D7 D6 D5 O0 D3 D2 D1 BT
xgmii_rxd_next[31:8] = encoded_rx_data_int[31:8];
xgmii_rxc_next[3:0] = 4'h1;
rx_os_next[7:0] = encoded_rx_data_int[31:24];
rx_os_next[15:8] = encoded_rx_data_int[23:16];
rx_os_next[23:16] = encoded_rx_data_int[15:8];
if (encoded_rx_data_int[35:32] == O_SEQ_OS) begin
xgmii_rxd_next[7:0] = XGMII_SEQ_OS;
rx_bad_block_next = 1'b0;
rx_os_sig_next = 1'b0;
rx_os_valid_next = 1'b1;
end else if (encoded_rx_data_int[35:32] == O_SIG_OS) begin
xgmii_rxd_next[7:0] = XGMII_SIG_OS;
rx_bad_block_next = 1'b0;
rx_os_sig_next = 1'b1;
rx_os_valid_next = 1'b1;
end else begin
xgmii_rxd_next[7:0] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
@@ -302,14 +342,25 @@ always_comb begin
xgmii_rxc_next[3:0] = 4'h1;
if (encoded_rx_data_int[35:32] == O_SEQ_OS) begin
xgmii_rxd_next[7:0] = XGMII_SEQ_OS;
end else if (encoded_rx_data_int[35:32] == O_SIG_OS) begin
xgmii_rxd_next[7:0] = XGMII_SIG_OS;
end else begin
xgmii_rxd_next[7:0] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
end
xgmii_rxd_next[63:40] = encoded_rx_data_int[63:40];
xgmii_rxc_next[7:4] = 4'h1;
rx_os_next[7:0] = encoded_rx_data_int[63:56];
rx_os_next[15:8] = encoded_rx_data_int[55:48];
rx_os_next[23:16] = encoded_rx_data_int[47:40];
if (encoded_rx_data_int[39:36] == O_SEQ_OS) begin
xgmii_rxd_next[39:32] = XGMII_SEQ_OS;
rx_os_sig_next = 1'b0;
rx_os_valid_next = 1'b1;
end else if (encoded_rx_data_int[39:36] == O_SIG_OS) begin
xgmii_rxd_next[39:32] = XGMII_SIG_OS;
rx_os_sig_next = 1'b1;
rx_os_valid_next = 1'b1;
end else begin
xgmii_rxd_next[39:32] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
@@ -327,9 +378,19 @@ always_comb begin
// C7 C6 C5 C4 O0 D3 D2 D1 BT
xgmii_rxd_next[31:8] = encoded_rx_data_int[31:8];
xgmii_rxc_next[3:0] = 4'h1;
rx_os_next[7:0] = encoded_rx_data_int[31:24];
rx_os_next[15:8] = encoded_rx_data_int[23:16];
rx_os_next[23:16] = encoded_rx_data_int[15:8];
if (encoded_rx_data_int[35:32] == O_SEQ_OS) begin
xgmii_rxd_next[7:0] = XGMII_SEQ_OS;
rx_bad_block_next = decode_err[7:4] != 0;
rx_os_sig_next = 1'b0;
rx_os_valid_next = 1'b1;
end else if (encoded_rx_data_int[35:32] == O_SIG_OS) begin
xgmii_rxd_next[7:0] = XGMII_SIG_OS;
rx_bad_block_next = decode_err[7:4] != 0;
rx_os_sig_next = 1'b1;
rx_os_valid_next = 1'b1;
end else begin
xgmii_rxd_next[7:0] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
@@ -453,12 +514,17 @@ always_ff @(posedge clk) begin
xgmii_rxc_reg <= xgmii_rxc_next;
xgmii_rx_valid_reg <= xgmii_rx_valid_next;
rx_os_reg <= rx_os_next;
rx_os_sig_reg <= rx_os_sig_next;
rx_os_valid_reg <= rx_os_valid_next;
rx_bad_block_reg <= rx_bad_block_next;
rx_sequence_error_reg <= rx_sequence_error_next;
frame_reg <= frame_next;
if (rst) begin
xgmii_rx_valid_reg <= '0;
rx_os_valid_reg <= 1'b0;
frame_reg <= 1'b0;
end
end

View File

@@ -44,6 +44,14 @@ module taxi_xgmii_baser_enc #
output wire logic encoded_tx_hdr_valid,
output wire logic [GBX_CNT-1:0] tx_gbx_sync_out,
/*
* 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,
/*
* Status
*/
@@ -136,6 +144,8 @@ logic [HDR_W-1:0] encoded_tx_hdr_reg = '0, encoded_tx_hdr_next;
logic encoded_tx_hdr_valid_reg = 1'b0, encoded_tx_hdr_valid_next;
logic [GBX_CNT-1:0] tx_gbx_sync_reg = '0, tx_gbx_sync_next;
logic tx_os_ready_reg = 1'b0, tx_os_ready_next;
logic tx_bad_block_reg = 1'b0, tx_bad_block_next;
assign encoded_tx_data = encoded_tx_data_reg[DATA_W-1:0];
@@ -144,6 +154,8 @@ assign encoded_tx_hdr = encoded_tx_hdr_reg;
assign encoded_tx_hdr_valid = USE_HDR_VLD ? encoded_tx_hdr_valid_reg : 1'b1;
assign tx_gbx_sync_out = GBX_IF_EN ? tx_gbx_sync_reg : '0;
assign tx_os_ready = tx_os_ready_reg;
assign tx_bad_block = tx_bad_block_reg;
if (DATA_W == 64) begin : repack_in
@@ -187,6 +199,8 @@ always_comb begin
encoded_tx_hdr_valid_next = '0;
tx_gbx_sync_next = '0;
tx_os_ready_next = 1'b0;
tx_bad_block_next = 1'b0;
for (integer i = 0; i < CTRL_W_INT; i = i + 1) begin
@@ -321,7 +335,21 @@ always_comb begin
tx_bad_block_next = 1'b0;
end else if (xgmii_txc_int == 8'hff) begin
// all control
encoded_tx_data_next = {encoded_ctrl, BLOCK_TYPE_CTRL};
if (tx_os_valid && xgmii_txd_int[7:0] == XGMII_IDLE) begin
// assume all idles, replace with ordered sets
encoded_tx_data_next[7:0] = BLOCK_TYPE_OS_04;
encoded_tx_data_next[15:8] = tx_os[23:16];
encoded_tx_data_next[23:16] = tx_os[15:8];
encoded_tx_data_next[31:24] = tx_os[7:0];
encoded_tx_data_next[35:32] = tx_os_sig ? O_SIG_OS : O_SEQ_OS;
encoded_tx_data_next[39:36] = tx_os_sig ? O_SIG_OS : O_SEQ_OS;
encoded_tx_data_next[47:40] = tx_os[23:16];
encoded_tx_data_next[55:48] = tx_os[15:8];
encoded_tx_data_next[63:56] = tx_os[7:0];
tx_os_ready_next = 1'b1;
end else begin
encoded_tx_data_next = {encoded_ctrl, BLOCK_TYPE_CTRL};
end
tx_bad_block_next = encode_err != 0;
end else begin
// no corresponding block format
@@ -347,6 +375,8 @@ always_ff @(posedge clk) begin
encoded_tx_hdr_valid_reg <= encoded_tx_hdr_valid_next;
tx_gbx_sync_reg <= tx_gbx_sync_next;
tx_os_ready_reg <= tx_os_ready_next;
tx_bad_block_reg <= tx_bad_block_next;
end

View File

@@ -176,6 +176,33 @@ async def run_test_alignment(dut, payload_data=None, ifg=12, enable_dic=True,
await RisingEdge(dut.clk)
async def run_test_os(dut):
tb = TB(dut)
await tb.reset()
for sig in [False, True]:
for k in range(24):
os = 1 << k
tb.source.set_os(os, sig)
for k in range(20):
await RisingEdge(dut.clk)
assert int(dut.rx_os.value) == os
assert int(dut.rx_os_sig.value) == sig
tb.source.set_os(None)
for k in range(20):
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
def size_list():
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
@@ -205,6 +232,9 @@ if getattr(cocotb, 'top', None) is not None:
factory.add_option("force_offset_start", [False, True])
factory.generate_tests()
factory = TestFactory(run_test_os)
factory.generate_tests()
# cocotb-test

View File

@@ -176,6 +176,34 @@ async def run_test_alignment(dut, payload_data=None, ifg=12, enable_dic=True,
await RisingEdge(dut.clk)
async def run_test_os(dut):
tb = TB(dut)
await tb.reset()
for sig in [False, True]:
for k in range(24):
os = 1 << k
dut.tx_os.value = os
dut.tx_os_sig.value = sig
dut.tx_os_valid.value = 1
for k in range(20):
await RisingEdge(dut.clk)
assert tb.sink.get_os() == (os, sig)
dut.tx_os_valid.value = 0
for k in range(20):
await RisingEdge(dut.clk)
for k in range(10):
await RisingEdge(dut.clk)
def size_list():
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
@@ -206,6 +234,9 @@ if getattr(cocotb, 'top', None) is not None:
factory.add_option("force_offset_start", [False, True])
factory.generate_tests()
factory = TestFactory(run_test_os)
factory.generate_tests()
# cocotb-test