Reorganize repository

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-05-18 12:25:59 -07:00
parent 8cdae180a1
commit 66b53d98a2
690 changed files with 2314 additions and 1581 deletions

View File

@@ -0,0 +1,872 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream 10GBASE-R frame receiver (10GBASE-R in, AXI out)
*/
module taxi_axis_baser_rx_64 #
(
parameter DATA_W = 64,
parameter HDR_W = 2,
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
)
(
input wire logic clk,
input wire logic rst,
/*
* 10GBASE-R encoded input
*/
input wire logic [DATA_W-1:0] encoded_rx_data,
input wire logic [HDR_W-1:0] encoded_rx_hdr,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Configuration
*/
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable,
/*
* Status
*/
output wire logic [1:0] rx_start_packet,
output wire logic [3:0] stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble
);
// extract parameters
localparam KEEP_W = DATA_W/8;
localparam USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64 (instance %m)");
if (KEEP_W*8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity (instance %m)");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2 (instance %m)");
if (m_axis_rx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (m_axis_rx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [7:0]
XGMII_IDLE = 8'h07,
XGMII_START = 8'hfb,
XGMII_TERM = 8'hfd,
XGMII_ERROR = 8'hfe;
localparam [6:0]
CTRL_IDLE = 7'h00,
CTRL_LPI = 7'h06,
CTRL_ERROR = 7'h1e,
CTRL_RES_0 = 7'h2d,
CTRL_RES_1 = 7'h33,
CTRL_RES_2 = 7'h4b,
CTRL_RES_3 = 7'h55,
CTRL_RES_4 = 7'h66,
CTRL_RES_5 = 7'h78;
localparam [3:0]
O_SEQ_OS = 4'h0,
O_SIG_OS = 4'hf;
localparam [1:0]
SYNC_DATA = 2'b10,
SYNC_CTRL = 2'b01;
localparam [7:0]
BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT
BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT
BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT
BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT
BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT
BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT
BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT
BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT
BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT
BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT
localparam [3:0]
INPUT_TYPE_IDLE = 4'd0,
INPUT_TYPE_ERROR = 4'd1,
INPUT_TYPE_START_0 = 4'd2,
INPUT_TYPE_START_4 = 4'd3,
INPUT_TYPE_DATA = 4'd4,
INPUT_TYPE_TERM_0 = 4'd8,
INPUT_TYPE_TERM_1 = 4'd9,
INPUT_TYPE_TERM_2 = 4'd10,
INPUT_TYPE_TERM_3 = 4'd11,
INPUT_TYPE_TERM_4 = 4'd12,
INPUT_TYPE_TERM_5 = 4'd13,
INPUT_TYPE_TERM_6 = 4'd14,
INPUT_TYPE_TERM_7 = 4'd15;
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_PAYLOAD = 2'd1,
STATE_LAST = 2'd2;
logic [1:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic lanes_swapped = 1'b0;
logic [31:0] swap_data = 32'd0;
logic delay_type_valid = 1'b0;
logic [3:0] delay_type = INPUT_TYPE_IDLE;
logic [DATA_W-1:0] input_data_d0 = '0;
logic [DATA_W-1:0] input_data_d1 = '0;
logic [3:0] input_type_d0 = INPUT_TYPE_IDLE;
logic [3:0] input_type_d1 = INPUT_TYPE_IDLE;
logic input_start_swap = 1'b0;
logic input_start_d0 = 1'b0;
logic input_start_d1 = 1'b0;
logic frame_oversize_reg = 1'b0, frame_oversize_next;
logic pre_ok_reg = 1'b0, pre_ok_next;
logic [1:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [DATA_W-1:0] m_axis_rx_tdata_reg = '0, m_axis_rx_tdata_next;
logic [KEEP_W-1:0] m_axis_rx_tkeep_reg = '0, m_axis_rx_tkeep_next;
logic m_axis_rx_tvalid_reg = 1'b0, m_axis_rx_tvalid_next;
logic m_axis_rx_tlast_reg = 1'b0, m_axis_rx_tlast_next;
logic m_axis_rx_tuser_reg = 1'b0, m_axis_rx_tuser_next;
logic [1:0] start_packet_reg = 2'b00;
logic frame_reg = 1'b0;
logic [3:0] stat_rx_byte_reg = '0, stat_rx_byte_next;
logic [15:0] stat_rx_pkt_len_reg = '0, stat_rx_pkt_len_next;
logic stat_rx_pkt_fragment_reg = 1'b0, stat_rx_pkt_fragment_next;
logic stat_rx_pkt_jabber_reg = 1'b0, stat_rx_pkt_jabber_next;
logic stat_rx_pkt_ucast_reg = 1'b0, stat_rx_pkt_ucast_next;
logic stat_rx_pkt_mcast_reg = 1'b0, stat_rx_pkt_mcast_next;
logic stat_rx_pkt_bcast_reg = 1'b0, stat_rx_pkt_bcast_next;
logic stat_rx_pkt_vlan_reg = 1'b0, stat_rx_pkt_vlan_next;
logic stat_rx_pkt_good_reg = 1'b0, stat_rx_pkt_good_next;
logic stat_rx_pkt_bad_reg = 1'b0, stat_rx_pkt_bad_next;
logic stat_rx_err_oversize_reg = 1'b0, stat_rx_err_oversize_next;
logic stat_rx_err_bad_fcs_reg = 1'b0, stat_rx_err_bad_fcs_next;
logic stat_rx_err_bad_block_reg = 1'b0;
logic stat_rx_err_framing_reg = 1'b0;
logic stat_rx_err_preamble_reg = 1'b0, stat_rx_err_preamble_next;
logic [PTP_TS_W-1:0] ptp_ts_reg = '0;
logic [PTP_TS_W-1:0] ptp_ts_out_reg = '0, ptp_ts_out_next;
logic [PTP_TS_W-1:0] ptp_ts_adj_reg = '0;
logic ptp_ts_borrow_reg = '0;
logic [31:0] crc_state = '1;
wire [31:0] crc_next;
wire [7:0] crc_valid;
logic [7:0] crc_valid_save;
assign crc_valid[7] = crc_next == ~32'h2144df1c;
assign crc_valid[6] = crc_next == ~32'hc622f71d;
assign crc_valid[5] = crc_next == ~32'hb1c2a1a3;
assign crc_valid[4] = crc_next == ~32'h9d6cdf7e;
assign crc_valid[3] = crc_next == ~32'h6522df69;
assign crc_valid[2] = crc_next == ~32'he60914ae;
assign crc_valid[1] = crc_next == ~32'he38a6876;
assign crc_valid[0] = crc_next == ~32'h6b87b1ec;
logic [4+16-1:0] last_ts_reg = '0;
logic [4+16-1:0] ts_inc_reg = '0;
assign m_axis_rx.tdata = m_axis_rx_tdata_reg;
assign m_axis_rx.tkeep = m_axis_rx_tkeep_reg;
assign m_axis_rx.tstrb = m_axis_rx.tkeep;
assign m_axis_rx.tvalid = m_axis_rx_tvalid_reg;
assign m_axis_rx.tlast = m_axis_rx_tlast_reg;
assign m_axis_rx.tid = '0;
assign m_axis_rx.tdest = '0;
assign m_axis_rx.tuser[0] = m_axis_rx_tuser_reg;
if (PTP_TS_EN) begin
assign m_axis_rx.tuser[1 +: PTP_TS_W] = ptp_ts_out_reg;
end
assign rx_start_packet = start_packet_reg;
assign stat_rx_byte = stat_rx_byte_reg;
assign stat_rx_pkt_len = stat_rx_pkt_len_reg;
assign stat_rx_pkt_fragment = stat_rx_pkt_fragment_reg;
assign stat_rx_pkt_jabber = stat_rx_pkt_jabber_reg;
assign stat_rx_pkt_ucast = stat_rx_pkt_ucast_reg;
assign stat_rx_pkt_mcast = stat_rx_pkt_mcast_reg;
assign stat_rx_pkt_bcast = stat_rx_pkt_bcast_reg;
assign stat_rx_pkt_vlan = stat_rx_pkt_vlan_reg;
assign stat_rx_pkt_good = stat_rx_pkt_good_reg;
assign stat_rx_pkt_bad = stat_rx_pkt_bad_reg;
assign stat_rx_err_oversize = stat_rx_err_oversize_reg;
assign stat_rx_err_bad_fcs = stat_rx_err_bad_fcs_reg;
assign stat_rx_err_bad_block = stat_rx_err_bad_block_reg;
assign stat_rx_err_framing = stat_rx_err_framing_reg;
assign stat_rx_err_preamble = stat_rx_err_preamble_reg;
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(64)
)
eth_crc (
.data_in(input_data_d0),
.state_in(crc_state),
.data_out(),
.state_out(crc_next)
);
// Mask input data
logic [DATA_W-1:0] encoded_rx_data_masked;
always_comb begin
// minimal checks of control info to simplify datapath logic, full checks performed later
encoded_rx_data_masked = encoded_rx_data;
if (encoded_rx_hdr[0] == 0) begin
// data
encoded_rx_data_masked = encoded_rx_data;
end else if (encoded_rx_data[7]) begin
// terminate
case (encoded_rx_data[6:4])
3'd0: encoded_rx_data_masked = 64'd0;
3'd1: encoded_rx_data_masked = {56'd0, encoded_rx_data[15:8]};
3'd2: encoded_rx_data_masked = {48'd0, encoded_rx_data[23:8]};
3'd3: encoded_rx_data_masked = {40'd0, encoded_rx_data[31:8]};
3'd4: encoded_rx_data_masked = {32'd0, encoded_rx_data[39:8]};
3'd5: encoded_rx_data_masked = {24'd0, encoded_rx_data[47:8]};
3'd6: encoded_rx_data_masked = {16'd0, encoded_rx_data[55:8]};
3'd7: encoded_rx_data_masked = {8'd0, encoded_rx_data[63:8]};
endcase
end else begin
// start, OS, etc.
encoded_rx_data_masked = encoded_rx_data;
end
end
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
frame_oversize_next = frame_oversize_reg;
pre_ok_next = pre_ok_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
m_axis_rx_tdata_next = input_data_d1;
m_axis_rx_tkeep_next = 8'd0;
m_axis_rx_tvalid_next = 1'b0;
m_axis_rx_tlast_next = 1'b0;
m_axis_rx_tuser_next = m_axis_rx_tuser_reg;
m_axis_rx_tuser_next = 1'b0;
ptp_ts_out_next = ptp_ts_out_reg;
stat_rx_byte_next = '0;
stat_rx_pkt_len_next = '0;
stat_rx_pkt_fragment_next = 1'b0;
stat_rx_pkt_jabber_next = 1'b0;
stat_rx_pkt_ucast_next = 1'b0;
stat_rx_pkt_mcast_next = 1'b0;
stat_rx_pkt_bcast_next = 1'b0;
stat_rx_pkt_vlan_next = 1'b0;
stat_rx_pkt_good_next = 1'b0;
stat_rx_pkt_bad_next = 1'b0;
stat_rx_err_oversize_next = 1'b0;
stat_rx_err_bad_fcs_next = 1'b0;
stat_rx_err_preamble_next = 1'b0;
// counter to measure frame length
if (&frame_len_reg[15:3] == 0) begin
if (input_type_d0[3]) begin
frame_len_next = frame_len_reg + 16'(input_type_d0[2:0]);
end else begin
frame_len_next = frame_len_reg + 16'(KEEP_W);
end
end else begin
frame_len_next = '1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg[15:3] != 0) begin
frame_len_lim_next = frame_len_lim_reg - 16'(KEEP_W);
end else begin
frame_len_lim_next = '0;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
2'd0: begin
is_mcast_next = input_data_d1[0];
is_bcast_next = &input_data_d1[47:0];
end
2'd1: is_8021q_next = {input_data_d1[39:32], input_data_d1[47:40]} == 16'h8100;
default: begin
// do nothing
end
endcase
case (state_reg)
STATE_IDLE: begin
// idle state - wait for packet
reset_crc = 1'b1;
frame_len_next = 16'(KEEP_W);
frame_len_lim_next = cfg_rx_max_pkt_len;
hdr_ptr_next = 0;
pre_ok_next = input_data_d1[63:8] == 56'hD5555555555555;
if (input_start_d1 && cfg_rx_enable) begin
// start condition
reset_crc = 1'b0;
stat_rx_byte_next = 4'(KEEP_W);
state_next = STATE_PAYLOAD;
end else begin
state_next = STATE_IDLE;
end
end
STATE_PAYLOAD: begin
// read payload
m_axis_rx_tdata_next = input_data_d1;
m_axis_rx_tkeep_next = 8'hff;
m_axis_rx_tvalid_next = 1'b1;
m_axis_rx_tlast_next = 1'b0;
m_axis_rx_tuser_next = 1'b0;
if (input_type_d0[3]) begin
stat_rx_byte_next = 4'(input_type_d0[2:0]);
frame_oversize_next = frame_len_lim_reg < 16'(8+input_type_d0[2:0]);
end else begin
stat_rx_byte_next = 4'(KEEP_W);
frame_oversize_next = frame_len_lim_reg < 8;
end
if (input_type_d0[3]) begin
// INPUT_TYPE_TERM_*
reset_crc = 1'b1;
end
if (PTP_TS_EN) begin
ptp_ts_out_next = (!PTP_TS_FMT_TOD || ptp_ts_borrow_reg) ? ptp_ts_reg : ptp_ts_adj_reg;
end
if (input_type_d0 == INPUT_TYPE_DATA) begin
state_next = STATE_PAYLOAD;
end else if (input_type_d0[3]) begin
// INPUT_TYPE_TERM_*
if (input_type_d0 <= INPUT_TYPE_TERM_4) begin
// end this cycle
case (input_type_d0)
INPUT_TYPE_TERM_0: m_axis_rx_tkeep_next = 8'b00001111;
INPUT_TYPE_TERM_1: m_axis_rx_tkeep_next = 8'b00011111;
INPUT_TYPE_TERM_2: m_axis_rx_tkeep_next = 8'b00111111;
INPUT_TYPE_TERM_3: m_axis_rx_tkeep_next = 8'b01111111;
INPUT_TYPE_TERM_4: m_axis_rx_tkeep_next = 8'b11111111;
default: m_axis_rx_tkeep_next = 8'b11111111;
endcase
m_axis_rx_tlast_next = 1'b1;
if ((input_type_d0 == INPUT_TYPE_TERM_0 && crc_valid_save[7]) ||
(input_type_d0 == INPUT_TYPE_TERM_1 && crc_valid[0]) ||
(input_type_d0 == INPUT_TYPE_TERM_2 && crc_valid[1]) ||
(input_type_d0 == INPUT_TYPE_TERM_3 && crc_valid[2]) ||
(input_type_d0 == INPUT_TYPE_TERM_4 && crc_valid[3])) begin
// CRC valid
if (frame_oversize_next) begin
// too long
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
end else begin
// length OK
m_axis_rx_tuser_next = 1'b0;
stat_rx_pkt_good_next = 1'b1;
end
end else begin
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_next[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_next;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_err_bad_fcs_next = 1'b1;
end
stat_rx_pkt_len_next = frame_len_next;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_next;
stat_rx_err_preamble_next = !pre_ok_reg;
state_next = STATE_IDLE;
end else begin
// need extra cycle
state_next = STATE_LAST;
end
end else begin
// control or error characters in packet
m_axis_rx_tlast_next = 1'b1;
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_pkt_len_next = frame_len_next;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_next;
stat_rx_err_preamble_next = !pre_ok_reg;
stat_rx_pkt_fragment_next = frame_len_next[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_next;
reset_crc = 1'b1;
state_next = STATE_IDLE;
end
end
STATE_LAST: begin
// last cycle of packet
m_axis_rx_tdata_next = input_data_d1;
m_axis_rx_tkeep_next = 8'hff;
m_axis_rx_tvalid_next = 1'b1;
m_axis_rx_tlast_next = 1'b1;
m_axis_rx_tuser_next = 1'b0;
reset_crc = 1'b1;
case (input_type_d1)
INPUT_TYPE_TERM_5: m_axis_rx_tkeep_next = 8'b00000001;
INPUT_TYPE_TERM_6: m_axis_rx_tkeep_next = 8'b00000011;
INPUT_TYPE_TERM_7: m_axis_rx_tkeep_next = 8'b00000111;
default: m_axis_rx_tkeep_next = 8'b00000111;
endcase
if ((input_type_d1 == INPUT_TYPE_TERM_5 && crc_valid_save[4]) ||
(input_type_d1 == INPUT_TYPE_TERM_6 && crc_valid_save[5]) ||
(input_type_d1 == INPUT_TYPE_TERM_7 && crc_valid_save[6])) begin
// CRC valid
if (frame_oversize_reg) begin
// too long
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
end else begin
// length OK
m_axis_rx_tuser_next = 1'b0;
stat_rx_pkt_good_next = 1'b1;
end
end else begin
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_reg[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_reg;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_err_bad_fcs_next = 1'b1;
end
stat_rx_pkt_len_next = frame_len_reg;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_reg;
stat_rx_err_preamble_next = !pre_ok_reg;
state_next = STATE_IDLE;
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
end
always_ff @(posedge clk) begin
state_reg <= state_next;
frame_oversize_reg <= frame_oversize_next;
pre_ok_reg <= pre_ok_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
m_axis_rx_tdata_reg <= m_axis_rx_tdata_next;
m_axis_rx_tkeep_reg <= m_axis_rx_tkeep_next;
m_axis_rx_tvalid_reg <= m_axis_rx_tvalid_next;
m_axis_rx_tlast_reg <= m_axis_rx_tlast_next;
m_axis_rx_tuser_reg <= m_axis_rx_tuser_next;
ptp_ts_out_reg <= ptp_ts_out_next;
start_packet_reg <= 2'b00;
stat_rx_byte_reg <= stat_rx_byte_next;
stat_rx_pkt_len_reg <= stat_rx_pkt_len_next;
stat_rx_pkt_fragment_reg <= stat_rx_pkt_fragment_next;
stat_rx_pkt_jabber_reg <= stat_rx_pkt_jabber_next;
stat_rx_pkt_ucast_reg <= stat_rx_pkt_ucast_next;
stat_rx_pkt_mcast_reg <= stat_rx_pkt_mcast_next;
stat_rx_pkt_bcast_reg <= stat_rx_pkt_bcast_next;
stat_rx_pkt_vlan_reg <= stat_rx_pkt_vlan_next;
stat_rx_pkt_good_reg <= stat_rx_pkt_good_next;
stat_rx_pkt_bad_reg <= stat_rx_pkt_bad_next;
stat_rx_err_oversize_reg <= stat_rx_err_oversize_next;
stat_rx_err_bad_fcs_reg <= stat_rx_err_bad_fcs_next;
stat_rx_err_bad_block_reg <= 1'b0;
stat_rx_err_framing_reg <= 1'b0;
stat_rx_err_preamble_reg <= stat_rx_err_preamble_next;
delay_type_valid <= 1'b0;
swap_data <= encoded_rx_data_masked[63:32];
input_start_swap <= 1'b0;
input_start_d0 <= input_start_swap;
if (PTP_TS_EN && PTP_TS_FMT_TOD) begin
// ns field rollover
ptp_ts_adj_reg[15:0] <= ptp_ts_reg[15:0];
{ptp_ts_borrow_reg, ptp_ts_adj_reg[45:16]} <= $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000);
ptp_ts_adj_reg[47:46] <= 0;
ptp_ts_adj_reg[95:48] <= ptp_ts_reg[95:48] + 1;
end
// lane swapping and termination character detection
if (lanes_swapped) begin
if (delay_type_valid) begin
input_type_d0 <= delay_type;
end else if (encoded_rx_hdr[0] == SYNC_DATA[0]) begin
input_type_d0 <= INPUT_TYPE_DATA;
end else begin
case (encoded_rx_data[7:4])
BLOCK_TYPE_TERM_0[7:4]: input_type_d0 <= INPUT_TYPE_TERM_4;
BLOCK_TYPE_TERM_1[7:4]: input_type_d0 <= INPUT_TYPE_TERM_5;
BLOCK_TYPE_TERM_2[7:4]: input_type_d0 <= INPUT_TYPE_TERM_6;
BLOCK_TYPE_TERM_3[7:4]: input_type_d0 <= INPUT_TYPE_TERM_7;
BLOCK_TYPE_TERM_4[7:4]: begin
delay_type_valid <= 1'b1;
input_type_d0 <= INPUT_TYPE_DATA;
end
BLOCK_TYPE_TERM_5[7:4]: begin
delay_type_valid <= 1'b1;
input_type_d0 <= INPUT_TYPE_DATA;
end
BLOCK_TYPE_TERM_6[7:4]: begin
delay_type_valid <= 1'b1;
input_type_d0 <= INPUT_TYPE_DATA;
end
BLOCK_TYPE_TERM_7[7:4]: begin
delay_type_valid <= 1'b1;
input_type_d0 <= INPUT_TYPE_DATA;
end
BLOCK_TYPE_CTRL[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
BLOCK_TYPE_OS_4[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
BLOCK_TYPE_OS_04[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
BLOCK_TYPE_OS_0[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
default: begin
input_type_d0 <= INPUT_TYPE_ERROR;
end
endcase
end
if (delay_type_valid) begin
// mask off trailing data
input_data_d0 <= {32'd0, swap_data};
end else begin
input_data_d0 <= {encoded_rx_data_masked[31:0], swap_data};
end
end else begin
if (encoded_rx_hdr[0] == SYNC_DATA[0]) begin
input_type_d0 <= INPUT_TYPE_DATA;
end else begin
case (encoded_rx_data[7:4])
BLOCK_TYPE_CTRL[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
BLOCK_TYPE_OS_4[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
BLOCK_TYPE_OS_04[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
BLOCK_TYPE_OS_0[7:4]: input_type_d0 <= INPUT_TYPE_IDLE;
BLOCK_TYPE_TERM_0[7:4]: input_type_d0 <= INPUT_TYPE_TERM_0;
BLOCK_TYPE_TERM_1[7:4]: input_type_d0 <= INPUT_TYPE_TERM_1;
BLOCK_TYPE_TERM_2[7:4]: input_type_d0 <= INPUT_TYPE_TERM_2;
BLOCK_TYPE_TERM_3[7:4]: input_type_d0 <= INPUT_TYPE_TERM_3;
BLOCK_TYPE_TERM_4[7:4]: input_type_d0 <= INPUT_TYPE_TERM_4;
BLOCK_TYPE_TERM_5[7:4]: input_type_d0 <= INPUT_TYPE_TERM_5;
BLOCK_TYPE_TERM_6[7:4]: input_type_d0 <= INPUT_TYPE_TERM_6;
BLOCK_TYPE_TERM_7[7:4]: input_type_d0 <= INPUT_TYPE_TERM_7;
default: begin
input_type_d0 <= INPUT_TYPE_ERROR;
end
endcase
end
input_data_d0 <= encoded_rx_data_masked;
end
// start control character detection
if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin
lanes_swapped <= 1'b0;
input_start_d0 <= 1'b1;
input_data_d0 <= encoded_rx_data_masked;
end else if (encoded_rx_hdr == SYNC_CTRL && (encoded_rx_data[7:0] == BLOCK_TYPE_START_4 || encoded_rx_data[7:0] == BLOCK_TYPE_OS_START)) begin
lanes_swapped <= 1'b1;
input_start_swap <= 1'b1;
end
if (encoded_rx_hdr == SYNC_DATA) begin
delay_type <= INPUT_TYPE_DATA;
end else if (encoded_rx_hdr == SYNC_CTRL) begin
case (encoded_rx_data[7:4])
BLOCK_TYPE_START_4[7:4]: delay_type <= INPUT_TYPE_START_0;
BLOCK_TYPE_OS_START[7:4]: delay_type <= INPUT_TYPE_START_0;
BLOCK_TYPE_TERM_0[7:4]: delay_type <= INPUT_TYPE_TERM_4;
BLOCK_TYPE_TERM_1[7:4]: delay_type <= INPUT_TYPE_TERM_5;
BLOCK_TYPE_TERM_2[7:4]: delay_type <= INPUT_TYPE_TERM_6;
BLOCK_TYPE_TERM_3[7:4]: delay_type <= INPUT_TYPE_TERM_7;
BLOCK_TYPE_TERM_4[7:4]: delay_type <= INPUT_TYPE_TERM_0;
BLOCK_TYPE_TERM_5[7:4]: delay_type <= INPUT_TYPE_TERM_1;
BLOCK_TYPE_TERM_6[7:4]: delay_type <= INPUT_TYPE_TERM_2;
BLOCK_TYPE_TERM_7[7:4]: delay_type <= INPUT_TYPE_TERM_3;
default: delay_type <= INPUT_TYPE_ERROR;
endcase
end else begin
delay_type <= INPUT_TYPE_ERROR;
end
// check for framing errors
stat_rx_err_framing_reg <= 1'b0;
if (encoded_rx_hdr == SYNC_DATA) begin
// data - must be in a frame
stat_rx_err_framing_reg <= !frame_reg;
end else if (encoded_rx_hdr == SYNC_CTRL) begin
// control - control only allowed between frames
frame_reg <= 1'b0;
case (encoded_rx_data[7:4])
BLOCK_TYPE_CTRL[7:4]: begin
stat_rx_err_framing_reg <= frame_reg;
end
BLOCK_TYPE_OS_4[7:4]: begin
stat_rx_err_framing_reg <= frame_reg;
end
BLOCK_TYPE_START_4[7:4]: begin
frame_reg <= 1'b1;
stat_rx_err_framing_reg <= frame_reg;
end
BLOCK_TYPE_OS_START[7:4]: begin
frame_reg <= 1'b1;
stat_rx_err_framing_reg <= frame_reg;
end
BLOCK_TYPE_OS_04[7:4]: begin
stat_rx_err_framing_reg <= frame_reg;
end
BLOCK_TYPE_START_0[7:4]: begin
frame_reg <= 1'b1;
stat_rx_err_framing_reg <= frame_reg;
end
BLOCK_TYPE_OS_0[7:4]: begin
stat_rx_err_framing_reg <= frame_reg;
end
BLOCK_TYPE_TERM_0[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
BLOCK_TYPE_TERM_1[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
BLOCK_TYPE_TERM_2[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
BLOCK_TYPE_TERM_3[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
BLOCK_TYPE_TERM_4[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
BLOCK_TYPE_TERM_5[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
BLOCK_TYPE_TERM_6[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
BLOCK_TYPE_TERM_7[7:4]: begin
stat_rx_err_framing_reg <= !frame_reg;
end
default: begin
// invalid block type
frame_reg <= 1'b0;
end
endcase
end else begin
// invalid header
frame_reg <= 1'b0;
end
// check all block type bits to detect bad encodings
if (encoded_rx_hdr == SYNC_DATA) begin
// data - nothing encoded
end else if (encoded_rx_hdr == SYNC_CTRL) begin
// control - check for bad block types
case (encoded_rx_data[7:0])
BLOCK_TYPE_CTRL: begin end
BLOCK_TYPE_OS_4: begin end
BLOCK_TYPE_START_4: begin end
BLOCK_TYPE_OS_START: begin end
BLOCK_TYPE_OS_04: begin end
BLOCK_TYPE_START_0: begin end
BLOCK_TYPE_OS_0: begin end
BLOCK_TYPE_TERM_0: begin end
BLOCK_TYPE_TERM_1: begin end
BLOCK_TYPE_TERM_2: begin end
BLOCK_TYPE_TERM_3: begin end
BLOCK_TYPE_TERM_4: begin end
BLOCK_TYPE_TERM_5: begin end
BLOCK_TYPE_TERM_6: begin end
BLOCK_TYPE_TERM_7: begin end
default: begin
// invalid block type
stat_rx_err_bad_block_reg <= 1'b1;
end
endcase
end else begin
// invalid header
stat_rx_err_bad_block_reg <= 1'b1;
end
// capture timestamps
if (input_start_swap) begin
start_packet_reg <= 2'b10;
if (PTP_TS_FMT_TOD) begin
ptp_ts_reg[45:0] <= ptp_ts[45:0] + 46'(ts_inc_reg >> 1);
ptp_ts_reg[95:48] <= ptp_ts[95:48];
end else begin
ptp_ts_reg <= ptp_ts + PTP_TS_W'(ts_inc_reg >> 1);
end
end
if (input_start_d0 && !lanes_swapped) begin
start_packet_reg <= 2'b01;
ptp_ts_reg <= ptp_ts;
end
input_type_d1 <= input_type_d0;
input_start_d1 <= input_start_d0;
input_data_d1 <= input_data_d0;
if (reset_crc) begin
crc_state <= '1;
end else begin
crc_state <= crc_next;
end
crc_valid_save <= crc_valid;
last_ts_reg <= (4+16)'(ptp_ts);
ts_inc_reg <= (4+16)'(ptp_ts) - last_ts_reg;
if (rst) begin
state_reg <= STATE_IDLE;
m_axis_rx_tvalid_reg <= 1'b0;
start_packet_reg <= 2'b00;
frame_reg <= 1'b0;
stat_rx_byte_reg <= '0;
stat_rx_pkt_len_reg <= '0;
stat_rx_pkt_fragment_reg <= 1'b0;
stat_rx_pkt_jabber_reg <= 1'b0;
stat_rx_pkt_ucast_reg <= 1'b0;
stat_rx_pkt_mcast_reg <= 1'b0;
stat_rx_pkt_bcast_reg <= 1'b0;
stat_rx_pkt_vlan_reg <= 1'b0;
stat_rx_pkt_good_reg <= 1'b0;
stat_rx_pkt_bad_reg <= 1'b0;
stat_rx_err_oversize_reg <= 1'b0;
stat_rx_err_bad_fcs_reg <= 1'b0;
stat_rx_err_bad_block_reg <= 1'b0;
stat_rx_err_framing_reg <= 1'b0;
stat_rx_err_preamble_reg <= 1'b0;
input_type_d0 <= INPUT_TYPE_IDLE;
input_type_d1 <= INPUT_TYPE_IDLE;
input_start_swap <= 1'b0;
input_start_d0 <= 1'b0;
input_start_d1 <= 1'b0;
lanes_swapped <= 1'b0;
delay_type_valid <= 1'b0;
delay_type <= INPUT_TYPE_IDLE;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,938 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream 10GBASE-R frame transmitter (AXI in, 10GBASE-R out)
*/
module taxi_axis_baser_tx_64 #
(
parameter DATA_W = 64,
parameter HDR_W = 2,
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic TX_CPL_CTRL_IN_TUSER = 1'b1
)
(
input wire logic clk,
input wire logic rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* 10GBASE-R encoded interface
*/
output wire logic [DATA_W-1:0] encoded_tx_data,
output wire logic [HDR_W-1:0] encoded_tx_hdr,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable,
/*
* Status
*/
output wire logic [1:0] tx_start_packet,
output wire logic [3:0] stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow
);
// extract parameters
localparam KEEP_W = DATA_W/8;
localparam USER_W = TX_CPL_CTRL_IN_TUSER ? 2 : 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
localparam EMPTY_W = $clog2(KEEP_W);
localparam MIN_LEN_W = $clog2(MIN_FRAME_LEN-4-KEEP_W+1);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64 (instance %m)");
if (KEEP_W * 8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity (instance %m)");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2 (instance %m)");
if (s_axis_tx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (s_axis_tx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [6:0]
CTRL_IDLE = 7'h00,
CTRL_LPI = 7'h06,
CTRL_ERROR = 7'h1e,
CTRL_RES_0 = 7'h2d,
CTRL_RES_1 = 7'h33,
CTRL_RES_2 = 7'h4b,
CTRL_RES_3 = 7'h55,
CTRL_RES_4 = 7'h66,
CTRL_RES_5 = 7'h78;
localparam [3:0]
O_SEQ_OS = 4'h0,
O_SIG_OS = 4'hf;
localparam [1:0]
SYNC_DATA = 2'b10,
SYNC_CTRL = 2'b01;
localparam [7:0]
BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT
BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT
BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT
BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT
BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT
BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT
BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT
BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT
BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT
BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT
localparam [3:0]
OUTPUT_TYPE_IDLE = 4'd0,
OUTPUT_TYPE_ERROR = 4'd1,
OUTPUT_TYPE_START_0 = 4'd2,
OUTPUT_TYPE_START_4 = 4'd3,
OUTPUT_TYPE_DATA = 4'd4,
OUTPUT_TYPE_TERM_0 = 4'd8,
OUTPUT_TYPE_TERM_1 = 4'd9,
OUTPUT_TYPE_TERM_2 = 4'd10,
OUTPUT_TYPE_TERM_3 = 4'd11,
OUTPUT_TYPE_TERM_4 = 4'd12,
OUTPUT_TYPE_TERM_5 = 4'd13,
OUTPUT_TYPE_TERM_6 = 4'd14,
OUTPUT_TYPE_TERM_7 = 4'd15;
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_PAYLOAD = 3'd1,
STATE_PAD = 3'd2,
STATE_FCS_1 = 3'd3,
STATE_FCS_2 = 3'd4,
STATE_ERR = 3'd5,
STATE_IFG = 3'd6;
logic [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic update_crc;
logic swap_lanes_reg = 1'b0, swap_lanes_next;
logic [31:0] swap_data = 32'd0;
logic delay_type_valid = 1'b0;
logic [3:0] delay_type = OUTPUT_TYPE_IDLE;
logic [DATA_W-1:0] s_axis_tx_tdata_masked;
logic [DATA_W-1:0] s_tdata_reg = '0, s_tdata_next;
logic [EMPTY_W-1:0] s_empty_reg = '0, s_empty_next;
logic [DATA_W-1:0] fcs_output_data_0;
logic [DATA_W-1:0] fcs_output_data_1;
logic [3:0] fcs_output_type_0;
logic [3:0] fcs_output_type_1;
logic [7:0] ifg_offset;
logic frame_start_reg = 1'b0, frame_start_next;
logic frame_reg = 1'b0, frame_next;
logic frame_error_reg = 1'b0, frame_error_next;
logic frame_oversize_reg = 1'b0, frame_oversize_next;
logic [MIN_LEN_W-1:0] frame_min_count_reg = '0, frame_min_count_next;
logic [1:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [7:0] ifg_cnt_reg = '0, ifg_cnt_next;
logic [7:0] ifg_count_reg = 8'd0, ifg_count_next;
logic [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next;
logic s_axis_tx_tready_reg = 1'b0, s_axis_tx_tready_next;
logic [PTP_TS_W-1:0] m_axis_tx_cpl_ts_reg = '0;
logic [PTP_TS_W-1:0] m_axis_tx_cpl_ts_adj_reg = '0;
logic [TX_TAG_W-1:0] m_axis_tx_cpl_tag_reg = '0;
logic m_axis_tx_cpl_valid_reg = 1'b0;
logic m_axis_tx_cpl_valid_int_reg = 1'b0;
logic m_axis_tx_cpl_ts_borrow_reg = 1'b0;
logic [31:0] crc_state_reg[7:0];
wire [31:0] crc_state_next[7:0];
logic [DATA_W-1:0] encoded_tx_data_reg = {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL};
logic [HDR_W-1:0] encoded_tx_hdr_reg = SYNC_CTRL;
logic [DATA_W-1:0] output_data_reg = '0, output_data_next;
logic [3:0] output_type_reg = OUTPUT_TYPE_IDLE, output_type_next;
logic [1:0] start_packet_reg = 2'b00;
logic [3:0] stat_tx_byte_reg = '0, stat_tx_byte_next;
logic [15:0] stat_tx_pkt_len_reg = '0, stat_tx_pkt_len_next;
logic stat_tx_pkt_ucast_reg = 1'b0, stat_tx_pkt_ucast_next;
logic stat_tx_pkt_mcast_reg = 1'b0, stat_tx_pkt_mcast_next;
logic stat_tx_pkt_bcast_reg = 1'b0, stat_tx_pkt_bcast_next;
logic stat_tx_pkt_vlan_reg = 1'b0, stat_tx_pkt_vlan_next;
logic stat_tx_pkt_good_reg = 1'b0, stat_tx_pkt_good_next;
logic stat_tx_pkt_bad_reg = 1'b0, stat_tx_pkt_bad_next;
logic stat_tx_err_oversize_reg = 1'b0, stat_tx_err_oversize_next;
logic stat_tx_err_user_reg = 1'b0, stat_tx_err_user_next;
logic stat_tx_err_underflow_reg = 1'b0, stat_tx_err_underflow_next;
logic [4+16-1:0] last_ts_reg = '0;
logic [4+16-1:0] ts_inc_reg = '0;
assign s_axis_tx.tready = s_axis_tx_tready_reg;
assign encoded_tx_data = encoded_tx_data_reg;
assign encoded_tx_hdr = encoded_tx_hdr_reg;
assign m_axis_tx_cpl.tdata = PTP_TS_EN ? ((!PTP_TS_FMT_TOD || m_axis_tx_cpl_ts_borrow_reg) ? m_axis_tx_cpl_ts_reg : m_axis_tx_cpl_ts_adj_reg) : '0;
assign m_axis_tx_cpl.tkeep = 1'b1;
assign m_axis_tx_cpl.tstrb = m_axis_tx_cpl.tkeep;
assign m_axis_tx_cpl.tvalid = m_axis_tx_cpl_valid_reg;
assign m_axis_tx_cpl.tlast = 1'b1;
assign m_axis_tx_cpl.tid = m_axis_tx_cpl_tag_reg;
assign m_axis_tx_cpl.tdest = '0;
assign m_axis_tx_cpl.tuser = '0;
assign tx_start_packet = start_packet_reg;
assign stat_tx_byte = stat_tx_byte_reg;
assign stat_tx_pkt_len = stat_tx_pkt_len_reg;
assign stat_tx_pkt_ucast = stat_tx_pkt_ucast_reg;
assign stat_tx_pkt_mcast = stat_tx_pkt_mcast_reg;
assign stat_tx_pkt_bcast = stat_tx_pkt_bcast_reg;
assign stat_tx_pkt_vlan = stat_tx_pkt_vlan_reg;
assign stat_tx_pkt_good = stat_tx_pkt_good_reg;
assign stat_tx_pkt_bad = stat_tx_pkt_bad_reg;
assign stat_tx_err_oversize = stat_tx_err_oversize_reg;
assign stat_tx_err_user = stat_tx_err_user_reg;
assign stat_tx_err_underflow = stat_tx_err_underflow_reg;
for (genvar n = 0; n < 8; n = n + 1) begin : crc
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(8*(n+1))
)
eth_crc (
.data_in(s_tdata_reg[0 +: 8*(n+1)]),
.state_in(crc_state_reg[7]),
.data_out(),
.state_out(crc_state_next[n])
);
end
function [2:0] keep2empty(input [7:0] k);
casez (k)
8'bzzzzzzz0: keep2empty = 3'd7;
8'bzzzzzz01: keep2empty = 3'd7;
8'bzzzzz011: keep2empty = 3'd6;
8'bzzzz0111: keep2empty = 3'd5;
8'bzzz01111: keep2empty = 3'd4;
8'bzz011111: keep2empty = 3'd3;
8'bz0111111: keep2empty = 3'd2;
8'b01111111: keep2empty = 3'd1;
8'b11111111: keep2empty = 3'd0;
endcase
endfunction
// Mask input data
always_comb begin
for (integer j = 0; j < 8; j = j + 1) begin
s_axis_tx_tdata_masked[j*8 +: 8] = s_axis_tx.tkeep[j] ? s_axis_tx.tdata[j*8 +: 8] : 8'd0;
end
end
// FCS cycle calculation
always_comb begin
casez (s_empty_reg)
3'd7: begin
fcs_output_data_0 = {24'd0, ~crc_state_next[0][31:0], s_tdata_reg[7:0]};
fcs_output_data_1 = 64'd0;
fcs_output_type_0 = OUTPUT_TYPE_TERM_5;
fcs_output_type_1 = OUTPUT_TYPE_IDLE;
ifg_offset = 8'd3;
end
3'd6: begin
fcs_output_data_0 = {16'd0, ~crc_state_next[1][31:0], s_tdata_reg[15:0]};
fcs_output_data_1 = 64'd0;
fcs_output_type_0 = OUTPUT_TYPE_TERM_6;
fcs_output_type_1 = OUTPUT_TYPE_IDLE;
ifg_offset = 8'd2;
end
3'd5: begin
fcs_output_data_0 = {8'd0, ~crc_state_next[2][31:0], s_tdata_reg[23:0]};
fcs_output_data_1 = 64'd0;
fcs_output_type_0 = OUTPUT_TYPE_TERM_7;
fcs_output_type_1 = OUTPUT_TYPE_IDLE;
ifg_offset = 8'd1;
end
3'd4: begin
fcs_output_data_0 = {~crc_state_next[3][31:0], s_tdata_reg[31:0]};
fcs_output_data_1 = 64'd0;
fcs_output_type_0 = OUTPUT_TYPE_DATA;
fcs_output_type_1 = OUTPUT_TYPE_TERM_0;
ifg_offset = 8'd8;
end
3'd3: begin
fcs_output_data_0 = {~crc_state_next[4][23:0], s_tdata_reg[39:0]};
fcs_output_data_1 = {56'd0, ~crc_state_reg[4][31:24]};
fcs_output_type_0 = OUTPUT_TYPE_DATA;
fcs_output_type_1 = OUTPUT_TYPE_TERM_1;
ifg_offset = 8'd7;
end
3'd2: begin
fcs_output_data_0 = {~crc_state_next[5][15:0], s_tdata_reg[47:0]};
fcs_output_data_1 = {48'd0, ~crc_state_reg[5][31:16]};
fcs_output_type_0 = OUTPUT_TYPE_DATA;
fcs_output_type_1 = OUTPUT_TYPE_TERM_2;
ifg_offset = 8'd6;
end
3'd1: begin
fcs_output_data_0 = {~crc_state_next[6][7:0], s_tdata_reg[55:0]};
fcs_output_data_1 = {40'd0, ~crc_state_reg[6][31:8]};
fcs_output_type_0 = OUTPUT_TYPE_DATA;
fcs_output_type_1 = OUTPUT_TYPE_TERM_3;
ifg_offset = 8'd5;
end
3'd0: begin
fcs_output_data_0 = s_tdata_reg;
fcs_output_data_1 = {32'd0, ~crc_state_reg[7][31:0]};
fcs_output_type_0 = OUTPUT_TYPE_DATA;
fcs_output_type_1 = OUTPUT_TYPE_TERM_4;
ifg_offset = 8'd4;
end
endcase
end
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
update_crc = 1'b0;
swap_lanes_next = swap_lanes_reg;
frame_start_next = 1'b0;
frame_next = frame_reg;
frame_error_next = frame_error_reg;
frame_oversize_next = frame_oversize_reg;
frame_min_count_next = frame_min_count_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
ifg_cnt_next = ifg_cnt_reg;
ifg_count_next = ifg_count_reg;
deficit_idle_count_next = deficit_idle_count_reg;
s_axis_tx_tready_next = 1'b0;
s_tdata_next = s_tdata_reg;
s_empty_next = s_empty_reg;
output_data_next = s_tdata_reg;
output_type_next = OUTPUT_TYPE_IDLE;
stat_tx_byte_next = '0;
stat_tx_pkt_len_next = '0;
stat_tx_pkt_ucast_next = 1'b0;
stat_tx_pkt_mcast_next = 1'b0;
stat_tx_pkt_bcast_next = 1'b0;
stat_tx_pkt_vlan_next = 1'b0;
stat_tx_pkt_good_next = 1'b0;
stat_tx_pkt_bad_next = 1'b0;
stat_tx_err_oversize_next = 1'b0;
stat_tx_err_user_next = 1'b0;
stat_tx_err_underflow_next = 1'b0;
if (s_axis_tx.tvalid && s_axis_tx.tready) begin
frame_next = !s_axis_tx.tlast;
end
// counter for min frame length enforcement
if (frame_min_count_reg > MIN_LEN_W'(KEEP_W)) begin
frame_min_count_next = MIN_LEN_W'(frame_min_count_reg - KEEP_W);
end else begin
frame_min_count_next = 0;
end
// counter to measure frame length
if (&frame_len_reg[15:3] == 0) begin
frame_len_next = frame_len_reg + 16'(KEEP_W);
end else begin
frame_len_next = '1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg[15:3] != 0) begin
frame_len_lim_next = frame_len_lim_reg - 16'(KEEP_W);
end else begin
frame_len_lim_next = '0;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
2'd0: begin
is_mcast_next = s_tdata_reg[0];
is_bcast_next = &s_tdata_reg[47:0];
end
2'd1: is_8021q_next = {s_tdata_reg[39:32], s_tdata_reg[47:40]} == 16'h8100;
default: begin
// do nothing
end
endcase
if (ifg_cnt_reg[7:3] != 0) begin
ifg_cnt_next = ifg_cnt_reg - 8'(KEEP_W);
end else begin
ifg_cnt_next = '0;
end
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_error_next = 1'b0;
frame_min_count_next = MIN_LEN_W'(MIN_FRAME_LEN-4-KEEP_W);
hdr_ptr_next = 0;
frame_len_next = 0;
frame_len_lim_next = cfg_tx_max_pkt_len;
reset_crc = 1'b1;
s_axis_tx_tready_next = 1'b1;
output_data_next = s_tdata_reg;
output_type_next = OUTPUT_TYPE_IDLE;
s_tdata_next = s_axis_tx_tdata_masked;
s_empty_next = keep2empty(s_axis_tx.tkeep);
if (s_axis_tx.tvalid && cfg_tx_enable) begin
// Preamble and SFD
output_data_next = {ETH_SFD, {7{ETH_PRE}}};
output_type_next = OUTPUT_TYPE_START_0;
frame_start_next = 1'b1;
s_axis_tx_tready_next = 1'b1;
state_next = STATE_PAYLOAD;
end else begin
swap_lanes_next = 1'b0;
ifg_count_next = 8'd0;
deficit_idle_count_next = 2'd0;
state_next = STATE_IDLE;
end
end
STATE_PAYLOAD: begin
// transfer payload
update_crc = 1'b1;
s_axis_tx_tready_next = 1'b1;
output_data_next = s_tdata_reg;
output_type_next = OUTPUT_TYPE_DATA;
s_tdata_next = s_axis_tx_tdata_masked;
s_empty_next = keep2empty(s_axis_tx.tkeep);
stat_tx_byte_next = 4'(KEEP_W);
if (s_axis_tx.tvalid && s_axis_tx.tlast) begin
frame_oversize_next = frame_len_lim_reg < 16'(8+8+4-keep2empty(s_axis_tx.tkeep));
end else begin
frame_oversize_next = frame_len_lim_reg < 8+8;
end
if (!s_axis_tx.tvalid || s_axis_tx.tlast || frame_oversize_next) begin
s_axis_tx_tready_next = frame_next; // drop frame
frame_error_next = !s_axis_tx.tvalid || s_axis_tx.tuser[0] || frame_oversize_next;
stat_tx_err_user_next = s_axis_tx.tuser[0];
stat_tx_err_underflow_next = !s_axis_tx.tvalid;
if (PADDING_EN && frame_min_count_reg != 0) begin
if (frame_min_count_reg > MIN_LEN_W'(KEEP_W)) begin
s_empty_next = 0;
state_next = STATE_PAD;
end else begin
if (keep2empty(s_axis_tx.tkeep) > 3'(KEEP_W-frame_min_count_reg)) begin
s_empty_next = 3'(KEEP_W-frame_min_count_reg);
end
if (frame_error_next) begin
state_next = STATE_ERR;
end else begin
state_next = STATE_FCS_1;
end
end
end else begin
if (frame_error_next) begin
state_next = STATE_ERR;
end else begin
state_next = STATE_FCS_1;
end
end
end else begin
state_next = STATE_PAYLOAD;
end
end
STATE_PAD: begin
// pad frame to MIN_FRAME_LEN
s_axis_tx_tready_next = frame_next; // drop frame
output_data_next = s_tdata_reg;
output_type_next = OUTPUT_TYPE_DATA;
s_tdata_next = 64'd0;
s_empty_next = 0;
stat_tx_byte_next = 4'(KEEP_W);
update_crc = 1'b1;
if (frame_min_count_reg > MIN_LEN_W'(KEEP_W)) begin
state_next = STATE_PAD;
end else begin
s_empty_next = 3'(KEEP_W-frame_min_count_reg);
if (frame_error_reg) begin
state_next = STATE_ERR;
end else begin
state_next = STATE_FCS_1;
end
end
end
STATE_FCS_1: begin
// last cycle
s_axis_tx_tready_next = frame_next; // drop frame
output_data_next = fcs_output_data_0;
output_type_next = fcs_output_type_0;
update_crc = 1'b1;
ifg_count_next = (cfg_tx_ifg > 8'd12 ? cfg_tx_ifg : 8'd12) - ifg_offset + (swap_lanes_reg ? 8'd4 : 8'd0) + 8'(deficit_idle_count_reg);
if (s_empty_reg <= 4) begin
stat_tx_byte_next = 4'(KEEP_W);
state_next = STATE_FCS_2;
end else begin
stat_tx_byte_next = 12-s_empty_reg;
frame_len_next = frame_len_reg + 16'(12-s_empty_reg);
stat_tx_pkt_len_next = frame_len_next;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
state_next = STATE_IFG;
end
end
STATE_FCS_2: begin
// last cycle
s_axis_tx_tready_next = frame_next; // drop frame
output_data_next = fcs_output_data_1;
output_type_next = fcs_output_type_1;
stat_tx_byte_next = 4-s_empty_reg;
frame_len_next = frame_len_reg + 16'(4-s_empty_reg);
reset_crc = 1'b1;
stat_tx_pkt_len_next = frame_len_next;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
if (DIC_EN) begin
if (ifg_count_next > 8'd7) begin
state_next = STATE_IFG;
end else begin
if (ifg_count_next >= 8'd4) begin
deficit_idle_count_next = 2'(ifg_count_next - 8'd4);
swap_lanes_next = 1'b1;
end else begin
deficit_idle_count_next = 2'(ifg_count_next);
ifg_count_next = 8'd0;
swap_lanes_next = 1'b0;
end
s_axis_tx_tready_next = 1'b1;
state_next = STATE_IDLE;
end
end else begin
if (ifg_count_next > 8'd4) begin
state_next = STATE_IFG;
end else begin
s_axis_tx_tready_next = 1'b1;
swap_lanes_next = ifg_count_next != 0;
state_next = STATE_IDLE;
end
end
end
STATE_ERR: begin
// terminate packet with error
s_axis_tx_tready_next = frame_next; // drop frame
output_data_next = s_tdata_reg;
output_type_next = OUTPUT_TYPE_ERROR;
ifg_count_next = cfg_tx_ifg > 8'd12 ? cfg_tx_ifg : 8'd12;
stat_tx_pkt_len_next = frame_len_reg;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
state_next = STATE_IFG;
end
STATE_IFG: begin
// send IFG
s_axis_tx_tready_next = frame_next; // drop frame
output_data_next = s_tdata_reg;
output_type_next = OUTPUT_TYPE_IDLE;
if (ifg_count_reg > 8'd8) begin
ifg_count_next = ifg_count_reg - 8'd8;
end else begin
ifg_count_next = 8'd0;
end
reset_crc = 1'b1;
if (DIC_EN) begin
if (ifg_count_next > 8'd7 || frame_reg) begin
state_next = STATE_IFG;
end else begin
if (ifg_count_next >= 8'd4) begin
deficit_idle_count_next = 2'(ifg_count_next - 8'd4);
swap_lanes_next = 1'b1;
end else begin
deficit_idle_count_next = 2'(ifg_count_next);
ifg_count_next = 8'd0;
swap_lanes_next = 1'b0;
end
s_axis_tx_tready_next = 1'b1;
state_next = STATE_IDLE;
end
end else begin
if (ifg_count_next > 8'd4 || frame_reg) begin
state_next = STATE_IFG;
end else begin
s_axis_tx_tready_next = 1'b1;
swap_lanes_next = ifg_count_next != 0;
state_next = STATE_IDLE;
end
end
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
end
always_ff @(posedge clk) begin
state_reg <= state_next;
swap_lanes_reg <= swap_lanes_next;
frame_start_reg <= frame_start_next;
frame_reg <= frame_next;
frame_error_reg <= frame_error_next;
frame_oversize_reg <= frame_oversize_next;
frame_min_count_reg <= frame_min_count_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
ifg_cnt_reg <= ifg_cnt_next;
ifg_count_reg <= ifg_count_next;
deficit_idle_count_reg <= deficit_idle_count_next;
s_tdata_reg <= s_tdata_next;
s_empty_reg <= s_empty_next;
s_axis_tx_tready_reg <= s_axis_tx_tready_next;
m_axis_tx_cpl_valid_reg <= 1'b0;
m_axis_tx_cpl_valid_int_reg <= 1'b0;
start_packet_reg <= 2'b00;
stat_tx_byte_reg <= stat_tx_byte_next;
stat_tx_pkt_len_reg <= stat_tx_pkt_len_next;
stat_tx_pkt_ucast_reg <= stat_tx_pkt_ucast_next;
stat_tx_pkt_mcast_reg <= stat_tx_pkt_mcast_next;
stat_tx_pkt_bcast_reg <= stat_tx_pkt_bcast_next;
stat_tx_pkt_vlan_reg <= stat_tx_pkt_vlan_next;
stat_tx_pkt_good_reg <= stat_tx_pkt_good_next;
stat_tx_pkt_bad_reg <= stat_tx_pkt_bad_next;
stat_tx_err_oversize_reg <= stat_tx_err_oversize_next;
stat_tx_err_user_reg <= stat_tx_err_user_next;
stat_tx_err_underflow_reg <= stat_tx_err_underflow_next;
delay_type_valid <= 1'b0;
delay_type <= output_type_next ^ 4'd4;
swap_data <= output_data_next[63:32];
if (swap_lanes_reg) begin
output_data_reg <= {output_data_next[31:0], swap_data};
if (delay_type_valid) begin
output_type_reg <= delay_type;
end else if (output_type_next == OUTPUT_TYPE_START_0) begin
output_type_reg <= OUTPUT_TYPE_START_4;
end else if (output_type_next[3]) begin
// OUTPUT_TYPE_TERM_*
if (output_type_next[2]) begin
delay_type_valid <= 1'b1;
output_type_reg <= OUTPUT_TYPE_DATA;
end else begin
output_type_reg <= output_type_next ^ 4'd4;
end
end else begin
output_type_reg <= output_type_next;
end
end else begin
output_data_reg <= output_data_next;
output_type_reg <= output_type_next;
end
if (PTP_TS_EN && PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_valid_reg <= m_axis_tx_cpl_valid_int_reg;
m_axis_tx_cpl_ts_adj_reg[15:0] <= m_axis_tx_cpl_ts_reg[15:0];
{m_axis_tx_cpl_ts_borrow_reg, m_axis_tx_cpl_ts_adj_reg[45:16]} <= $signed({1'b0, m_axis_tx_cpl_ts_reg[45:16]}) - $signed(31'd1000000000);
m_axis_tx_cpl_ts_adj_reg[47:46] <= 0;
m_axis_tx_cpl_ts_adj_reg[95:48] <= m_axis_tx_cpl_ts_reg[95:48] + 1;
end
if (frame_start_reg) begin
if (swap_lanes_reg) begin
if (PTP_TS_EN) begin
if (PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_ts_reg[45:0] <= ptp_ts[45:0] + 46'(ts_inc_reg >> 1);
m_axis_tx_cpl_ts_reg[95:48] <= ptp_ts[95:48];
end else begin
m_axis_tx_cpl_ts_reg <= ptp_ts + PTP_TS_W'(ts_inc_reg >> 1);
end
end
start_packet_reg <= 2'b10;
end else begin
if (PTP_TS_EN) begin
m_axis_tx_cpl_ts_reg <= ptp_ts;
end
start_packet_reg <= 2'b01;
end
m_axis_tx_cpl_tag_reg <= s_axis_tx.tid;
if (TX_CPL_CTRL_IN_TUSER) begin
if (PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_valid_int_reg <= (s_axis_tx.tuser >> 1) == 0;
end else begin
m_axis_tx_cpl_valid_reg <= (s_axis_tx.tuser >> 1) == 0;
end
end else begin
if (PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_valid_int_reg <= 1'b1;
end else begin
m_axis_tx_cpl_valid_reg <= 1'b1;
end
end
end
case (output_type_reg)
OUTPUT_TYPE_IDLE: begin
encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_ERROR: begin
encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_START_0: begin
encoded_tx_data_reg <= {output_data_reg[63:8], BLOCK_TYPE_START_0};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_START_4: begin
encoded_tx_data_reg <= {output_data_reg[63:40], 4'd0, {4{CTRL_IDLE}}, BLOCK_TYPE_START_4};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_DATA: begin
encoded_tx_data_reg <= output_data_reg;
encoded_tx_hdr_reg <= SYNC_DATA;
end
OUTPUT_TYPE_TERM_0: begin
encoded_tx_data_reg <= {{7{CTRL_IDLE}}, 7'd0, BLOCK_TYPE_TERM_0};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_TERM_1: begin
encoded_tx_data_reg <= {{6{CTRL_IDLE}}, 6'd0, output_data_reg[7:0], BLOCK_TYPE_TERM_1};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_TERM_2: begin
encoded_tx_data_reg <= {{5{CTRL_IDLE}}, 5'd0, output_data_reg[15:0], BLOCK_TYPE_TERM_2};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_TERM_3: begin
encoded_tx_data_reg <= {{4{CTRL_IDLE}}, 4'd0, output_data_reg[23:0], BLOCK_TYPE_TERM_3};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_TERM_4: begin
encoded_tx_data_reg <= {{3{CTRL_IDLE}}, 3'd0, output_data_reg[31:0], BLOCK_TYPE_TERM_4};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_TERM_5: begin
encoded_tx_data_reg <= {{2{CTRL_IDLE}}, 2'd0, output_data_reg[39:0], BLOCK_TYPE_TERM_5};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_TERM_6: begin
encoded_tx_data_reg <= {{1{CTRL_IDLE}}, 1'd0, output_data_reg[47:0], BLOCK_TYPE_TERM_6};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
OUTPUT_TYPE_TERM_7: begin
encoded_tx_data_reg <= {output_data_reg[55:0], BLOCK_TYPE_TERM_7};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
default: begin
encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL};
encoded_tx_hdr_reg <= SYNC_CTRL;
end
endcase
crc_state_reg[0] <= crc_state_next[0];
crc_state_reg[1] <= crc_state_next[1];
crc_state_reg[2] <= crc_state_next[2];
crc_state_reg[3] <= crc_state_next[3];
crc_state_reg[4] <= crc_state_next[4];
crc_state_reg[5] <= crc_state_next[5];
crc_state_reg[6] <= crc_state_next[6];
if (update_crc) begin
crc_state_reg[7] <= crc_state_next[7];
end
if (reset_crc) begin
crc_state_reg[7] <= '1;
end
last_ts_reg <= (4+16)'(ptp_ts);
ts_inc_reg <= (4+16)'(ptp_ts) - last_ts_reg;
if (rst) begin
state_reg <= STATE_IDLE;
frame_start_reg <= 1'b0;
frame_reg <= 1'b0;
swap_lanes_reg <= 1'b0;
ifg_count_reg <= 8'd0;
deficit_idle_count_reg <= 2'd0;
s_axis_tx_tready_reg <= 1'b0;
m_axis_tx_cpl_valid_reg <= 1'b0;
m_axis_tx_cpl_valid_int_reg <= 1'b0;
encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL};
encoded_tx_hdr_reg <= SYNC_CTRL;
output_data_reg <= '0;
output_type_reg <= OUTPUT_TYPE_IDLE;
start_packet_reg <= 2'b00;
stat_tx_byte_reg <= '0;
stat_tx_pkt_len_reg <= '0;
stat_tx_pkt_ucast_reg <= 1'b0;
stat_tx_pkt_mcast_reg <= 1'b0;
stat_tx_pkt_bcast_reg <= 1'b0;
stat_tx_pkt_vlan_reg <= 1'b0;
stat_tx_pkt_good_reg <= 1'b0;
stat_tx_pkt_bad_reg <= 1'b0;
stat_tx_err_oversize_reg <= 1'b0;
stat_tx_err_user_reg <= 1'b0;
stat_tx_err_underflow_reg <= 1'b0;
delay_type_valid <= 1'b0;
delay_type <= OUTPUT_TYPE_IDLE;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,561 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream GMII frame receiver (GMII in, AXI out)
*/
module taxi_axis_gmii_rx #
(
parameter DATA_W = 8,
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96
)
(
input wire logic clk,
input wire logic rst,
/*
* GMII input
*/
input wire logic [DATA_W-1:0] gmii_rxd,
input wire logic gmii_rx_dv,
input wire logic gmii_rx_er,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Control
*/
input wire logic clk_enable,
input wire logic mii_select,
/*
* Configuration
*/
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable,
/*
* Status
*/
output wire logic rx_start_packet,
output wire logic stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble
);
localparam USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
// check configuration
if (DATA_W != 8)
$fatal(0, "Error: Interface width must be 8 (instance %m)");
if (m_axis_rx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (m_axis_rx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_PIPE = 2'd1,
STATE_PAYLOAD = 2'd2;
logic [1:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic update_crc;
logic mii_odd = 1'b0;
logic in_frame = 1'b0;
logic [DATA_W-1:0] gmii_rxd_d0 = '0;
logic [DATA_W-1:0] gmii_rxd_d1 = '0;
logic [DATA_W-1:0] gmii_rxd_d2 = '0;
logic [DATA_W-1:0] gmii_rxd_d3 = '0;
logic [DATA_W-1:0] gmii_rxd_d4 = '0;
logic gmii_rx_dv_d0 = 1'b0;
logic gmii_rx_dv_d1 = 1'b0;
logic gmii_rx_dv_d2 = 1'b0;
logic gmii_rx_dv_d3 = 1'b0;
logic gmii_rx_dv_d4 = 1'b0;
logic gmii_rx_er_d0 = 1'b0;
logic gmii_rx_er_d1 = 1'b0;
logic gmii_rx_er_d2 = 1'b0;
logic gmii_rx_er_d3 = 1'b0;
logic gmii_rx_er_d4 = 1'b0;
logic frame_error_reg = 1'b0, frame_error_next;
logic in_pre_reg = 1'b0, in_pre_next;
logic pre_ok_reg = 1'b0, pre_ok_next;
logic [3:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [DATA_W-1:0] m_axis_rx_tdata_reg = '0, m_axis_rx_tdata_next;
logic m_axis_rx_tvalid_reg = 1'b0, m_axis_rx_tvalid_next;
logic m_axis_rx_tlast_reg = 1'b0, m_axis_rx_tlast_next;
logic m_axis_rx_tuser_reg = 1'b0, m_axis_rx_tuser_next;
logic start_packet_int_reg = 1'b0;
logic start_packet_reg = 1'b0;
logic stat_rx_byte_reg = 1'b0, stat_rx_byte_next;
logic [15:0] stat_rx_pkt_len_reg = '0, stat_rx_pkt_len_next;
logic stat_rx_pkt_fragment_reg = 1'b0, stat_rx_pkt_fragment_next;
logic stat_rx_pkt_jabber_reg = 1'b0, stat_rx_pkt_jabber_next;
logic stat_rx_pkt_ucast_reg = 1'b0, stat_rx_pkt_ucast_next;
logic stat_rx_pkt_mcast_reg = 1'b0, stat_rx_pkt_mcast_next;
logic stat_rx_pkt_bcast_reg = 1'b0, stat_rx_pkt_bcast_next;
logic stat_rx_pkt_vlan_reg = 1'b0, stat_rx_pkt_vlan_next;
logic stat_rx_pkt_good_reg = 1'b0, stat_rx_pkt_good_next;
logic stat_rx_pkt_bad_reg = 1'b0, stat_rx_pkt_bad_next;
logic stat_rx_err_oversize_reg = 1'b0, stat_rx_err_oversize_next;
logic stat_rx_err_bad_fcs_reg = 1'b0, stat_rx_err_bad_fcs_next;
logic stat_rx_err_bad_block_reg = 1'b0, stat_rx_err_bad_block_next;
logic stat_rx_err_framing_reg = 1'b0, stat_rx_err_framing_next;
logic stat_rx_err_preamble_reg = 1'b0, stat_rx_err_preamble_next;
logic [PTP_TS_W-1:0] ptp_ts_out_reg = '0;
logic [31:0] crc_state = '1;
wire [31:0] crc_next;
assign m_axis_rx.tdata = m_axis_rx_tdata_reg;
assign m_axis_rx.tkeep = 1'b1;
assign m_axis_rx.tstrb = m_axis_rx.tkeep;
assign m_axis_rx.tvalid = m_axis_rx_tvalid_reg;
assign m_axis_rx.tlast = m_axis_rx_tlast_reg;
assign m_axis_rx.tid = '0;
assign m_axis_rx.tdest = '0;
assign m_axis_rx.tuser[0] = m_axis_rx_tuser_reg;
if (PTP_TS_EN) begin
assign m_axis_rx.tuser[1 +: PTP_TS_W] = ptp_ts_out_reg;
end
assign rx_start_packet = start_packet_reg;
assign stat_rx_byte = stat_rx_byte_reg;
assign stat_rx_pkt_len = stat_rx_pkt_len_reg;
assign stat_rx_pkt_fragment = stat_rx_pkt_fragment_reg;
assign stat_rx_pkt_jabber = stat_rx_pkt_jabber_reg;
assign stat_rx_pkt_ucast = stat_rx_pkt_ucast_reg;
assign stat_rx_pkt_mcast = stat_rx_pkt_mcast_reg;
assign stat_rx_pkt_bcast = stat_rx_pkt_bcast_reg;
assign stat_rx_pkt_vlan = stat_rx_pkt_vlan_reg;
assign stat_rx_pkt_good = stat_rx_pkt_good_reg;
assign stat_rx_pkt_bad = stat_rx_pkt_bad_reg;
assign stat_rx_err_oversize = stat_rx_err_oversize_reg;
assign stat_rx_err_bad_fcs = stat_rx_err_bad_fcs_reg;
assign stat_rx_err_bad_block = stat_rx_err_bad_block_reg;
assign stat_rx_err_framing = stat_rx_err_framing_reg;
assign stat_rx_err_preamble = stat_rx_err_preamble_reg;
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(8)
)
eth_crc_8 (
.data_in(gmii_rxd_d0),
.state_in(crc_state),
.data_out(),
.state_out(crc_next)
);
wire crc_valid = crc_next == ~32'h2144df1c;
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
update_crc = 1'b0;
frame_error_next = frame_error_reg;
in_pre_next = in_pre_reg;
pre_ok_next = pre_ok_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
m_axis_rx_tdata_next = '0;
m_axis_rx_tvalid_next = 1'b0;
m_axis_rx_tlast_next = 1'b0;
m_axis_rx_tuser_next = 1'b0;
stat_rx_byte_next = 1'b0;
stat_rx_pkt_len_next = '0;
stat_rx_pkt_fragment_next = 1'b0;
stat_rx_pkt_jabber_next = 1'b0;
stat_rx_pkt_ucast_next = 1'b0;
stat_rx_pkt_mcast_next = 1'b0;
stat_rx_pkt_bcast_next = 1'b0;
stat_rx_pkt_vlan_next = 1'b0;
stat_rx_pkt_good_next = 1'b0;
stat_rx_pkt_bad_next = 1'b0;
stat_rx_err_oversize_next = 1'b0;
stat_rx_err_bad_fcs_next = 1'b0;
stat_rx_err_bad_block_next = 1'b0;
stat_rx_err_framing_next = 1'b0;
stat_rx_err_preamble_next = 1'b0;
if (!clk_enable) begin
// clock disabled - hold state
state_next = state_reg;
end else if (mii_select && !mii_odd) begin
// MII even cycle - hold state
state_next = state_reg;
end else begin
// counter to measure frame length
if (&frame_len_reg == 0) begin
frame_len_next = frame_len_reg + 1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg != 0) begin
frame_len_lim_next = frame_len_lim_reg - 1;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
4'd0: begin
is_mcast_next = gmii_rxd_d4[0];
is_bcast_next = gmii_rxd_d4 == 8'hff;
end
4'd1: is_bcast_next = is_bcast_reg && gmii_rxd_d4 == 8'hff;
4'd2: is_bcast_next = is_bcast_reg && gmii_rxd_d4 == 8'hff;
4'd3: is_bcast_next = is_bcast_reg && gmii_rxd_d4 == 8'hff;
4'd4: is_bcast_next = is_bcast_reg && gmii_rxd_d4 == 8'hff;
4'd5: is_bcast_next = is_bcast_reg && gmii_rxd_d4 == 8'hff;
4'd12: is_8021q_next = gmii_rxd_d4 == 8'h81;
4'd13: is_8021q_next = is_8021q_reg && gmii_rxd_d4 == 8'h00;
default: begin
// do nothing
end
endcase
case (state_reg)
STATE_IDLE: begin
// idle state - wait for packet
reset_crc = 1'b1;
frame_error_next = 1'b0;
frame_len_next = 1;
frame_len_lim_next = cfg_rx_max_pkt_len;
hdr_ptr_next = 0;
is_mcast_next = 1'b0;
is_bcast_next = 1'b0;
is_8021q_next = 1'b0;
state_next = STATE_IDLE;
if (gmii_rx_dv_d0) begin
if (gmii_rx_er_d0) begin
// error in preamble
in_pre_next = 1'b0;
pre_ok_next = 1'b0;
stat_rx_err_framing_next = 1'b1;
end else if (gmii_rxd_d0 == ETH_PRE) begin
// normal preamble
end else if (gmii_rxd_d0 == ETH_SFD) begin
// start
in_pre_next = 1'b0;
if (in_pre_reg && cfg_rx_enable) begin
stat_rx_byte_next = 1'b1;
state_next = STATE_PIPE;
end
end else begin
// abnormal preamble
pre_ok_next = 1'b0;
end
end else begin
// reset and wait for data
in_pre_next = 1'b1;
pre_ok_next = 1'b1;
end
end
STATE_PIPE: begin
// wait for FCS pipeline to fill
update_crc = 1'b1;
hdr_ptr_next = 0;
is_mcast_next = 1'b0;
is_bcast_next = 1'b0;
is_8021q_next = 1'b0;
stat_rx_byte_next = gmii_rx_dv;
if (gmii_rx_dv && gmii_rx_er) begin
frame_error_next = 1'b1;
stat_rx_err_framing_next = 1'b1;
end
if (gmii_rx_dv_d4 && !gmii_rx_er_d4 && gmii_rxd_d4 == ETH_SFD) begin
state_next = STATE_PAYLOAD;
end else begin
state_next = STATE_PIPE;
end
end
STATE_PAYLOAD: begin
// read payload
update_crc = 1'b1;
m_axis_rx_tdata_next = gmii_rxd_d4;
m_axis_rx_tvalid_next = 1'b1;
stat_rx_byte_next = gmii_rx_dv;
if (gmii_rx_dv && gmii_rx_er) begin
frame_error_next = 1'b1;
stat_rx_err_framing_next = 1'b1;
end
if (!gmii_rx_dv) begin
// end of packet
m_axis_rx_tlast_next = 1'b1;
stat_rx_pkt_len_next = frame_len_reg;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_len_lim_reg == 0;
stat_rx_err_framing_next = !gmii_rx_dv_d0;
stat_rx_err_preamble_next = !pre_ok_reg;
if (frame_error_next) begin
// error
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_reg[15:6] == 0;
stat_rx_pkt_jabber_next = frame_len_lim_reg == 0;
stat_rx_pkt_bad_next = 1'b1;
end else if (crc_valid) begin
// FCS good
if (frame_len_lim_reg == 0) begin
// too long
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
end else begin
// length OK
m_axis_rx_tuser_next = 1'b0;
stat_rx_pkt_good_next = 1'b1;
end
end else begin
// FCS bad
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_reg[15:6] == 0;
stat_rx_pkt_jabber_next = frame_len_lim_reg == 0;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_err_bad_fcs_next = 1'b1;
end
reset_crc = 1'b1;
state_next = STATE_IDLE;
end else begin
state_next = STATE_PAYLOAD;
end
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
end
end
always_ff @(posedge clk) begin
state_reg <= state_next;
frame_error_reg <= frame_error_next;
in_pre_reg <= in_pre_next;
pre_ok_reg <= pre_ok_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
m_axis_rx_tdata_reg <= m_axis_rx_tdata_next;
m_axis_rx_tvalid_reg <= m_axis_rx_tvalid_next;
m_axis_rx_tlast_reg <= m_axis_rx_tlast_next;
m_axis_rx_tuser_reg <= m_axis_rx_tuser_next;
start_packet_int_reg <= 1'b0;
start_packet_reg <= 1'b0;
if (start_packet_int_reg) begin
ptp_ts_out_reg <= ptp_ts;
start_packet_reg <= 1'b1;
end
if (clk_enable) begin
if (mii_select) begin
mii_odd <= !mii_odd || !gmii_rx_dv;
if (in_frame) begin
in_frame <= gmii_rx_dv;
end else if (gmii_rx_dv && {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == ETH_SFD) begin
in_frame <= 1'b1;
start_packet_int_reg <= 1'b1;
mii_odd <= 1'b1;
end
gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]};
if (mii_odd) begin
gmii_rxd_d1 <= gmii_rxd_d0;
gmii_rxd_d2 <= gmii_rxd_d1;
gmii_rxd_d3 <= gmii_rxd_d2;
gmii_rxd_d4 <= gmii_rxd_d3;
gmii_rx_dv_d0 <= gmii_rx_dv;
gmii_rx_dv_d1 <= gmii_rx_dv_d0;
gmii_rx_dv_d2 <= gmii_rx_dv_d1;
gmii_rx_dv_d3 <= gmii_rx_dv_d2;
gmii_rx_dv_d4 <= gmii_rx_dv_d3;
gmii_rx_er_d0 <= gmii_rx_er;
gmii_rx_er_d1 <= gmii_rx_er_d0;
gmii_rx_er_d2 <= gmii_rx_er_d1;
gmii_rx_er_d3 <= gmii_rx_er_d2;
gmii_rx_er_d4 <= gmii_rx_er_d3;
end else begin
gmii_rx_dv_d0 <= gmii_rx_dv & gmii_rx_dv_d0;
gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0;
end
end else begin
if (in_frame) begin
in_frame <= gmii_rx_dv;
end else if (gmii_rx_dv && gmii_rxd == ETH_SFD) begin
in_frame <= 1'b1;
start_packet_int_reg <= 1'b1;
end
gmii_rxd_d0 <= gmii_rxd;
gmii_rxd_d1 <= gmii_rxd_d0;
gmii_rxd_d2 <= gmii_rxd_d1;
gmii_rxd_d3 <= gmii_rxd_d2;
gmii_rxd_d4 <= gmii_rxd_d3;
gmii_rx_dv_d0 <= gmii_rx_dv;
gmii_rx_dv_d1 <= gmii_rx_dv_d0;
gmii_rx_dv_d2 <= gmii_rx_dv_d1;
gmii_rx_dv_d3 <= gmii_rx_dv_d2;
gmii_rx_dv_d4 <= gmii_rx_dv_d3;
gmii_rx_er_d0 <= gmii_rx_er;
gmii_rx_er_d1 <= gmii_rx_er_d0;
gmii_rx_er_d2 <= gmii_rx_er_d1;
gmii_rx_er_d3 <= gmii_rx_er_d2;
gmii_rx_er_d4 <= gmii_rx_er_d3;
end
end
if (reset_crc) begin
crc_state <= '1;
end else if (update_crc) begin
crc_state <= crc_next;
end
stat_rx_byte_reg <= stat_rx_byte_next;
stat_rx_pkt_len_reg <= stat_rx_pkt_len_next;
stat_rx_pkt_fragment_reg <= stat_rx_pkt_fragment_next;
stat_rx_pkt_jabber_reg <= stat_rx_pkt_jabber_next;
stat_rx_pkt_ucast_reg <= stat_rx_pkt_ucast_next;
stat_rx_pkt_mcast_reg <= stat_rx_pkt_mcast_next;
stat_rx_pkt_bcast_reg <= stat_rx_pkt_bcast_next;
stat_rx_pkt_vlan_reg <= stat_rx_pkt_vlan_next;
stat_rx_pkt_good_reg <= stat_rx_pkt_good_next;
stat_rx_pkt_bad_reg <= stat_rx_pkt_bad_next;
stat_rx_err_oversize_reg <= stat_rx_err_oversize_next;
stat_rx_err_bad_fcs_reg <= stat_rx_err_bad_fcs_next;
stat_rx_err_bad_block_reg <= stat_rx_err_bad_block_next;
stat_rx_err_framing_reg <= stat_rx_err_framing_next;
stat_rx_err_preamble_reg <= stat_rx_err_preamble_next;
if (rst) begin
state_reg <= STATE_IDLE;
m_axis_rx_tvalid_reg <= 1'b0;
start_packet_int_reg <= 1'b0;
start_packet_reg <= 1'b0;
stat_rx_byte_reg <= 1'b0;
stat_rx_pkt_len_reg <= '0;
stat_rx_pkt_fragment_reg <= 1'b0;
stat_rx_pkt_jabber_reg <= 1'b0;
stat_rx_pkt_ucast_reg <= 1'b0;
stat_rx_pkt_mcast_reg <= 1'b0;
stat_rx_pkt_bcast_reg <= 1'b0;
stat_rx_pkt_vlan_reg <= 1'b0;
stat_rx_pkt_good_reg <= 1'b0;
stat_rx_pkt_bad_reg <= 1'b0;
stat_rx_err_oversize_reg <= 1'b0;
stat_rx_err_bad_fcs_reg <= 1'b0;
stat_rx_err_bad_block_reg <= 1'b0;
stat_rx_err_framing_reg <= 1'b0;
stat_rx_err_preamble_reg <= 1'b0;
in_frame <= 1'b0;
mii_odd <= 1'b0;
gmii_rx_dv_d0 <= 1'b0;
gmii_rx_dv_d1 <= 1'b0;
gmii_rx_dv_d2 <= 1'b0;
gmii_rx_dv_d3 <= 1'b0;
gmii_rx_dv_d4 <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,602 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream GMII frame transmitter (AXI in, GMII out)
*/
module taxi_axis_gmii_tx #
(
parameter DATA_W = 8,
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96,
parameter logic TX_CPL_CTRL_IN_TUSER = 1'b1
)
(
input wire logic clk,
input wire logic rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* GMII output
*/
output wire logic [DATA_W-1:0] gmii_txd,
output wire logic gmii_tx_en,
output wire logic gmii_tx_er,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Control
*/
input wire logic clk_enable,
input wire logic mii_select,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable,
/*
* Status
*/
output wire logic tx_start_packet,
output wire logic stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow
);
localparam USER_W = TX_CPL_CTRL_IN_TUSER ? 2 : 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
localparam MIN_LEN_W = $clog2(MIN_FRAME_LEN-4-1+1);
// check configuration
if (DATA_W != 8)
$fatal(0, "Error: Interface width must be 8 (instance %m)");
if (s_axis_tx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (s_axis_tx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_PREAMBLE = 3'd1,
STATE_PAYLOAD = 3'd2,
STATE_LAST = 3'd3,
STATE_PAD = 3'd4,
STATE_FCS = 3'd5,
STATE_IFG = 3'd6;
logic [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic update_crc;
logic [7:0] s_tdata_reg = 8'd0, s_tdata_next;
logic mii_odd_reg = 1'b0, mii_odd_next;
logic [3:0] mii_msn_reg = 4'b0, mii_msn_next;
logic frame_reg = 1'b0, frame_next;
logic frame_error_reg = 1'b0, frame_error_next;
logic [MIN_LEN_W-1:0] frame_min_count_reg = '0, frame_min_count_next;
logic [3:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [1:0] fcs_ptr_reg = '0, fcs_ptr_next;
logic [2:0] pre_cnt_reg = '0, pre_cnt_next;
logic [7:0] ifg_cnt_reg = '0, ifg_cnt_next;
logic [7:0] gmii_txd_reg = 8'd0, gmii_txd_next;
logic gmii_tx_en_reg = 1'b0, gmii_tx_en_next;
logic gmii_tx_er_reg = 1'b0, gmii_tx_er_next;
logic s_axis_tx_tready_reg = 1'b0, s_axis_tx_tready_next;
logic [PTP_TS_W-1:0] m_axis_tx_cpl_ts_reg = '0, m_axis_tx_cpl_ts_next;
logic [TX_TAG_W-1:0] m_axis_tx_cpl_tag_reg = '0, m_axis_tx_cpl_tag_next;
logic m_axis_tx_cpl_valid_reg = 1'b0, m_axis_tx_cpl_valid_next;
logic start_packet_int_reg = 1'b0, start_packet_int_next;
logic start_packet_reg = 1'b0, start_packet_next;
logic stat_tx_byte_reg = 1'b0, stat_tx_byte_next;
logic [15:0] stat_tx_pkt_len_reg = '0, stat_tx_pkt_len_next;
logic stat_tx_pkt_ucast_reg = 1'b0, stat_tx_pkt_ucast_next;
logic stat_tx_pkt_mcast_reg = 1'b0, stat_tx_pkt_mcast_next;
logic stat_tx_pkt_bcast_reg = 1'b0, stat_tx_pkt_bcast_next;
logic stat_tx_pkt_vlan_reg = 1'b0, stat_tx_pkt_vlan_next;
logic stat_tx_pkt_good_reg = 1'b0, stat_tx_pkt_good_next;
logic stat_tx_pkt_bad_reg = 1'b0, stat_tx_pkt_bad_next;
logic stat_tx_err_oversize_reg = 1'b0, stat_tx_err_oversize_next;
logic stat_tx_err_user_reg = 1'b0, stat_tx_err_user_next;
logic stat_tx_err_underflow_reg = 1'b0, stat_tx_err_underflow_next;
logic [31:0] crc_state = '1;
wire [31:0] crc_next;
assign s_axis_tx.tready = s_axis_tx_tready_reg;
assign gmii_txd = gmii_txd_reg;
assign gmii_tx_en = gmii_tx_en_reg;
assign gmii_tx_er = gmii_tx_er_reg;
assign m_axis_tx_cpl.tdata = PTP_TS_EN ? m_axis_tx_cpl_ts_reg : '0;
assign m_axis_tx_cpl.tkeep = 1'b1;
assign m_axis_tx_cpl.tstrb = m_axis_tx_cpl.tkeep;
assign m_axis_tx_cpl.tvalid = m_axis_tx_cpl_valid_reg;
assign m_axis_tx_cpl.tlast = 1'b1;
assign m_axis_tx_cpl.tid = m_axis_tx_cpl_tag_reg;
assign m_axis_tx_cpl.tdest = '0;
assign m_axis_tx_cpl.tuser = '0;
assign tx_start_packet = start_packet_reg;
assign stat_tx_byte = stat_tx_byte_reg;
assign stat_tx_pkt_len = stat_tx_pkt_len_reg;
assign stat_tx_pkt_ucast = stat_tx_pkt_ucast_reg;
assign stat_tx_pkt_mcast = stat_tx_pkt_mcast_reg;
assign stat_tx_pkt_bcast = stat_tx_pkt_bcast_reg;
assign stat_tx_pkt_vlan = stat_tx_pkt_vlan_reg;
assign stat_tx_pkt_good = stat_tx_pkt_good_reg;
assign stat_tx_pkt_bad = stat_tx_pkt_bad_reg;
assign stat_tx_err_oversize = stat_tx_err_oversize_reg;
assign stat_tx_err_user = stat_tx_err_user_reg;
assign stat_tx_err_underflow = stat_tx_err_underflow_reg;
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(8)
)
eth_crc_8 (
.data_in(s_tdata_reg),
.state_in(crc_state),
.data_out(),
.state_out(crc_next)
);
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
update_crc = 1'b0;
mii_odd_next = mii_odd_reg;
mii_msn_next = mii_msn_reg;
frame_next = frame_reg;
frame_error_next = frame_error_reg;
frame_min_count_next = frame_min_count_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
fcs_ptr_next = fcs_ptr_reg;
pre_cnt_next = pre_cnt_reg;
ifg_cnt_next = ifg_cnt_reg;
s_axis_tx_tready_next = 1'b0;
s_tdata_next = s_tdata_reg;
m_axis_tx_cpl_ts_next = m_axis_tx_cpl_ts_reg;
m_axis_tx_cpl_tag_next = m_axis_tx_cpl_tag_reg;
m_axis_tx_cpl_valid_next = 1'b0;
if (start_packet_reg) begin
m_axis_tx_cpl_ts_next = ptp_ts;
m_axis_tx_cpl_tag_next = s_axis_tx.tid;
if (TX_CPL_CTRL_IN_TUSER) begin
m_axis_tx_cpl_valid_next = (s_axis_tx.tuser >> 1) == 0;
end else begin
m_axis_tx_cpl_valid_next = 1'b1;
end
end
gmii_txd_next = '0;
gmii_tx_en_next = 1'b0;
gmii_tx_er_next = 1'b0;
start_packet_int_next = start_packet_int_reg;
start_packet_next = 1'b0;
stat_tx_byte_next = 1'b0;
stat_tx_pkt_len_next = '0;
stat_tx_pkt_ucast_next = 1'b0;
stat_tx_pkt_mcast_next = 1'b0;
stat_tx_pkt_bcast_next = 1'b0;
stat_tx_pkt_vlan_next = 1'b0;
stat_tx_pkt_good_next = 1'b0;
stat_tx_pkt_bad_next = 1'b0;
stat_tx_err_oversize_next = 1'b0;
stat_tx_err_user_next = 1'b0;
stat_tx_err_underflow_next = 1'b0;
if (s_axis_tx.tvalid && s_axis_tx.tready) begin
frame_next = !s_axis_tx.tlast;
end
if (!clk_enable) begin
// clock disabled - hold state and outputs
gmii_txd_next = gmii_txd_reg;
gmii_tx_en_next = gmii_tx_en_reg;
gmii_tx_er_next = gmii_tx_er_reg;
state_next = state_reg;
end else if (mii_select && mii_odd_reg) begin
// MII odd cycle - hold state, output MSN
mii_odd_next = 1'b0;
gmii_txd_next = {4'd0, mii_msn_reg};
gmii_tx_en_next = gmii_tx_en_reg;
gmii_tx_er_next = gmii_tx_er_reg;
state_next = state_reg;
if (start_packet_int_reg) begin
start_packet_int_next = 1'b0;
start_packet_next = 1'b1;
end
end else begin
// counter for min frame length enforcement
if (frame_min_count_reg != 0) begin
frame_min_count_next = frame_min_count_reg - 1;
end
// counter to measure frame length
if (&frame_len_reg == 0) begin
frame_len_next = frame_len_reg + 1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg != 0) begin
frame_len_lim_next = frame_len_lim_reg - 1;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
4'd0: begin
is_mcast_next = s_tdata_reg[0];
is_bcast_next = s_tdata_reg == 8'hff;
end
4'd1: is_bcast_next = is_bcast_reg && s_tdata_reg == 8'hff;
4'd2: is_bcast_next = is_bcast_reg && s_tdata_reg == 8'hff;
4'd3: is_bcast_next = is_bcast_reg && s_tdata_reg == 8'hff;
4'd4: is_bcast_next = is_bcast_reg && s_tdata_reg == 8'hff;
4'd5: is_bcast_next = is_bcast_reg && s_tdata_reg == 8'hff;
4'd12: is_8021q_next = s_tdata_reg == 8'h81;
4'd13: is_8021q_next = is_8021q_reg && s_tdata_reg == 8'h00;
default: begin
// do nothing
end
endcase
if (&fcs_ptr_reg == 0) begin
fcs_ptr_next = fcs_ptr_reg + 1;
end
if (pre_cnt_reg != 0) begin
pre_cnt_next = pre_cnt_reg - 1;
end
if (ifg_cnt_reg != 0) begin
ifg_cnt_next = ifg_cnt_reg - 1;
end
case (state_reg)
STATE_IDLE: begin
// idle state - wait for packet
reset_crc = 1'b1;
mii_odd_next = 1'b0;
hdr_ptr_next = 0;
frame_len_next = 1;
frame_len_lim_next = cfg_tx_max_pkt_len;
pre_cnt_next = 3'd6;
frame_error_next = 1'b0;
frame_min_count_next = MIN_LEN_W'(MIN_FRAME_LEN-4-1);
gmii_txd_next = '0;
gmii_tx_en_next = 1'b0;
if (s_axis_tx.tvalid && cfg_tx_enable) begin
mii_odd_next = 1'b1;
gmii_txd_next = ETH_PRE;
gmii_tx_en_next = 1'b1;
state_next = STATE_PREAMBLE;
end else begin
state_next = STATE_IDLE;
end
end
STATE_PREAMBLE: begin
// send preamble
reset_crc = 1'b1;
mii_odd_next = 1'b1;
hdr_ptr_next = 0;
frame_len_next = 1;
frame_len_lim_next = cfg_tx_max_pkt_len;
frame_error_next = 1'b0;
frame_min_count_next = MIN_LEN_W'(MIN_FRAME_LEN-4-1);
gmii_txd_next = ETH_PRE;
gmii_tx_en_next = 1'b1;
if (pre_cnt_reg == 1) begin
s_axis_tx_tready_next = 1'b1;
s_tdata_next = s_axis_tx.tdata;
state_next = STATE_PREAMBLE;
end else if (pre_cnt_reg == 0) begin
// end of preamble; start payload
if (s_axis_tx_tready_reg) begin
s_axis_tx_tready_next = 1'b1;
s_tdata_next = s_axis_tx.tdata;
end
gmii_txd_next = ETH_SFD;
if (mii_select) begin
start_packet_int_next = 1'b1;
end else begin
start_packet_next = 1'b1;
end
state_next = STATE_PAYLOAD;
end else begin
state_next = STATE_PREAMBLE;
end
end
STATE_PAYLOAD: begin
// send payload
update_crc = 1'b1;
s_axis_tx_tready_next = 1'b1;
mii_odd_next = 1'b1;
gmii_txd_next = s_tdata_reg;
gmii_tx_en_next = 1'b1;
s_tdata_next = s_axis_tx.tdata;
stat_tx_byte_next = 1'b1;
if (!s_axis_tx.tvalid || s_axis_tx.tlast || frame_len_lim_reg < 6) begin
s_axis_tx_tready_next = frame_next; // drop frame
frame_error_next = !s_axis_tx.tvalid || s_axis_tx.tuser[0] || frame_len_lim_reg < 6;
stat_tx_err_user_next = s_axis_tx.tuser[0];
stat_tx_err_underflow_next = !s_axis_tx.tvalid;
state_next = STATE_LAST;
end else begin
state_next = STATE_PAYLOAD;
end
end
STATE_LAST: begin
// last payload word
update_crc = 1'b1;
s_axis_tx_tready_next = frame_next; // drop frame
mii_odd_next = 1'b1;
fcs_ptr_next = 2'd0;
gmii_txd_next = s_tdata_reg;
gmii_tx_en_next = 1'b1;
gmii_tx_er_next = frame_error_reg;
stat_tx_byte_next = 1'b1;
if (PADDING_EN && frame_min_count_reg != 0) begin
s_tdata_next = 8'd0;
state_next = STATE_PAD;
end else begin
state_next = STATE_FCS;
end
end
STATE_PAD: begin
// send padding
s_axis_tx_tready_next = frame_next; // drop frame
update_crc = 1'b1;
mii_odd_next = 1'b1;
fcs_ptr_next = 2'd0;
gmii_txd_next = s_tdata_reg;
gmii_tx_en_next = 1'b1;
gmii_tx_er_next = frame_error_reg;
s_tdata_next = 8'd0;
stat_tx_byte_next = 1'b1;
if (frame_min_count_reg != 0) begin
state_next = STATE_PAD;
end else begin
state_next = STATE_FCS;
end
end
STATE_FCS: begin
// send FCS
s_axis_tx_tready_next = frame_next; // drop frame
mii_odd_next = 1'b1;
ifg_cnt_next = cfg_tx_ifg;
case (fcs_ptr_reg)
2'd0: gmii_txd_next = ~crc_state[7:0];
2'd1: gmii_txd_next = ~crc_state[15:8];
2'd2: gmii_txd_next = ~crc_state[23:16];
2'd3: gmii_txd_next = ~crc_state[31:24];
endcase
gmii_tx_en_next = 1'b1;
gmii_tx_er_next = frame_error_reg;
stat_tx_byte_next = 1'b1;
if (&fcs_ptr_reg == 0) begin
state_next = STATE_FCS;
end else begin
stat_tx_pkt_len_next = frame_len_reg;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_len_lim_reg == 0;
state_next = STATE_IFG;
end
end
STATE_IFG: begin
// send IFG
s_axis_tx_tready_next = frame_next; // drop frame
mii_odd_next = 1'b1;
if (ifg_cnt_reg[7:1] != 0 || frame_reg) begin
state_next = STATE_IFG;
end else begin
state_next = STATE_IDLE;
end
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
if (mii_select) begin
mii_msn_next = gmii_txd_next[7:4];
gmii_txd_next[7:4] = 4'd0;
end
end
end
always_ff @(posedge clk) begin
state_reg <= state_next;
frame_reg <= frame_next;
frame_error_reg <= frame_error_next;
frame_min_count_reg <= frame_min_count_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
fcs_ptr_reg <= fcs_ptr_next;
pre_cnt_reg <= pre_cnt_next;
ifg_cnt_reg <= ifg_cnt_next;
m_axis_tx_cpl_ts_reg <= m_axis_tx_cpl_ts_next;
m_axis_tx_cpl_tag_reg <= m_axis_tx_cpl_tag_next;
m_axis_tx_cpl_valid_reg <= m_axis_tx_cpl_valid_next;
mii_odd_reg <= mii_odd_next;
mii_msn_reg <= mii_msn_next;
s_tdata_reg <= s_tdata_next;
s_axis_tx_tready_reg <= s_axis_tx_tready_next;
gmii_txd_reg <= gmii_txd_next;
gmii_tx_en_reg <= gmii_tx_en_next;
gmii_tx_er_reg <= gmii_tx_er_next;
if (reset_crc) begin
crc_state <= '1;
end else if (update_crc) begin
crc_state <= crc_next;
end
start_packet_int_reg <= start_packet_int_next;
start_packet_reg <= start_packet_next;
stat_tx_byte_reg <= stat_tx_byte_next;
stat_tx_pkt_len_reg <= stat_tx_pkt_len_next;
stat_tx_pkt_ucast_reg <= stat_tx_pkt_ucast_next;
stat_tx_pkt_mcast_reg <= stat_tx_pkt_mcast_next;
stat_tx_pkt_bcast_reg <= stat_tx_pkt_bcast_next;
stat_tx_pkt_vlan_reg <= stat_tx_pkt_vlan_next;
stat_tx_pkt_good_reg <= stat_tx_pkt_good_next;
stat_tx_pkt_bad_reg <= stat_tx_pkt_bad_next;
stat_tx_err_oversize_reg <= stat_tx_err_oversize_next;
stat_tx_err_user_reg <= stat_tx_err_user_next;
stat_tx_err_underflow_reg <= stat_tx_err_underflow_next;
if (rst) begin
state_reg <= STATE_IDLE;
frame_reg <= 1'b0;
s_axis_tx_tready_reg <= 1'b0;
m_axis_tx_cpl_valid_reg <= 1'b0;
gmii_tx_en_reg <= 1'b0;
gmii_tx_er_reg <= 1'b0;
start_packet_int_reg <= 1'b0;
start_packet_reg <= 1'b0;
stat_tx_byte_reg <= 1'b0;
stat_tx_pkt_len_reg <= '0;
stat_tx_pkt_ucast_reg <= 1'b0;
stat_tx_pkt_mcast_reg <= 1'b0;
stat_tx_pkt_bcast_reg <= 1'b0;
stat_tx_pkt_vlan_reg <= 1'b0;
stat_tx_pkt_good_reg <= 1'b0;
stat_tx_pkt_bad_reg <= 1'b0;
stat_tx_err_oversize_reg <= 1'b0;
stat_tx_err_user_reg <= 1'b0;
stat_tx_err_underflow_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,565 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream XGMII frame receiver (XGMII in, AXI out)
*/
module taxi_axis_xgmii_rx_32 #
(
parameter DATA_W = 32,
parameter CTRL_W = (DATA_W/8),
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96
)
(
input wire logic clk,
input wire logic rst,
/*
* XGMII input
*/
input wire logic [DATA_W-1:0] xgmii_rxd,
input wire logic [CTRL_W-1:0] xgmii_rxc,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Configuration
*/
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable,
/*
* Status
*/
output wire logic rx_start_packet,
output wire logic [2:0] stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble
);
// extract parameters
localparam KEEP_W = DATA_W/8;
localparam USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
// check configuration
if (DATA_W != 32)
$fatal(0, "Error: Interface width must be 32 (instance %m)");
if (KEEP_W*8 != DATA_W || CTRL_W*8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity (instance %m)");
if (m_axis_rx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (m_axis_rx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [7:0]
XGMII_IDLE = 8'h07,
XGMII_START = 8'hfb,
XGMII_TERM = 8'hfd,
XGMII_ERROR = 8'hfe;
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_PREAMBLE = 2'd1,
STATE_PAYLOAD = 2'd2,
STATE_LAST = 2'd3;
logic [1:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic [1:0] term_lane_reg = 0, term_lane_d0_reg = 0;
logic term_present_reg = 1'b0;
logic framing_error_reg = 1'b0;
logic [DATA_W-1:0] xgmii_rxd_d0 = '0;
logic [DATA_W-1:0] xgmii_rxd_d1 = '0;
logic [DATA_W-1:0] xgmii_rxd_d2 = '0;
logic [CTRL_W-1:0] xgmii_rxc_d0 = '0;
logic xgmii_start_d0 = 1'b0;
logic xgmii_start_d1 = 1'b0;
logic xgmii_start_d2 = 1'b0;
logic frame_oversize_reg = 1'b0, frame_oversize_next;
logic pre_ok_reg = 1'b0, pre_ok_next;
logic [2:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [DATA_W-1:0] m_axis_rx_tdata_reg = '0, m_axis_rx_tdata_next;
logic [KEEP_W-1:0] m_axis_rx_tkeep_reg = '0, m_axis_rx_tkeep_next;
logic m_axis_rx_tvalid_reg = 1'b0, m_axis_rx_tvalid_next;
logic m_axis_rx_tlast_reg = 1'b0, m_axis_rx_tlast_next;
logic m_axis_rx_tuser_reg = 1'b0, m_axis_rx_tuser_next;
logic start_packet_reg = 1'b0, start_packet_next;
logic [2:0] stat_rx_byte_reg = '0, stat_rx_byte_next;
logic [15:0] stat_rx_pkt_len_reg = '0, stat_rx_pkt_len_next;
logic stat_rx_pkt_fragment_reg = 1'b0, stat_rx_pkt_fragment_next;
logic stat_rx_pkt_jabber_reg = 1'b0, stat_rx_pkt_jabber_next;
logic stat_rx_pkt_ucast_reg = 1'b0, stat_rx_pkt_ucast_next;
logic stat_rx_pkt_mcast_reg = 1'b0, stat_rx_pkt_mcast_next;
logic stat_rx_pkt_bcast_reg = 1'b0, stat_rx_pkt_bcast_next;
logic stat_rx_pkt_vlan_reg = 1'b0, stat_rx_pkt_vlan_next;
logic stat_rx_pkt_good_reg = 1'b0, stat_rx_pkt_good_next;
logic stat_rx_pkt_bad_reg = 1'b0, stat_rx_pkt_bad_next;
logic stat_rx_err_oversize_reg = 1'b0, stat_rx_err_oversize_next;
logic stat_rx_err_bad_fcs_reg = 1'b0, stat_rx_err_bad_fcs_next;
logic stat_rx_err_bad_block_reg = 1'b0, stat_rx_err_bad_block_next;
logic stat_rx_err_framing_reg = 1'b0, stat_rx_err_framing_next;
logic stat_rx_err_preamble_reg = 1'b0, stat_rx_err_preamble_next;
logic [PTP_TS_W-1:0] ptp_ts_out_reg = '0, ptp_ts_out_next;
logic [31:0] crc_state = '1;
wire [31:0] crc_next;
wire [3:0] crc_valid;
logic [3:0] crc_valid_save;
assign crc_valid[3] = crc_next == ~32'h2144df1c;
assign crc_valid[2] = crc_next == ~32'hc622f71d;
assign crc_valid[1] = crc_next == ~32'hb1c2a1a3;
assign crc_valid[0] = crc_next == ~32'h9d6cdf7e;
assign m_axis_rx.tdata = m_axis_rx_tdata_reg;
assign m_axis_rx.tkeep = m_axis_rx_tkeep_reg;
assign m_axis_rx.tstrb = m_axis_rx.tkeep;
assign m_axis_rx.tvalid = m_axis_rx_tvalid_reg;
assign m_axis_rx.tlast = m_axis_rx_tlast_reg;
assign m_axis_rx.tid = '0;
assign m_axis_rx.tdest = '0;
assign m_axis_rx.tuser[0] = m_axis_rx_tuser_reg;
if (PTP_TS_EN) begin
assign m_axis_rx.tuser[1 +: PTP_TS_W] = ptp_ts_out_reg;
end
assign rx_start_packet = start_packet_reg;
assign stat_rx_byte = stat_rx_byte_reg;
assign stat_rx_pkt_len = stat_rx_pkt_len_reg;
assign stat_rx_pkt_fragment = stat_rx_pkt_fragment_reg;
assign stat_rx_pkt_jabber = stat_rx_pkt_jabber_reg;
assign stat_rx_pkt_ucast = stat_rx_pkt_ucast_reg;
assign stat_rx_pkt_mcast = stat_rx_pkt_mcast_reg;
assign stat_rx_pkt_bcast = stat_rx_pkt_bcast_reg;
assign stat_rx_pkt_vlan = stat_rx_pkt_vlan_reg;
assign stat_rx_pkt_good = stat_rx_pkt_good_reg;
assign stat_rx_pkt_bad = stat_rx_pkt_bad_reg;
assign stat_rx_err_oversize = stat_rx_err_oversize_reg;
assign stat_rx_err_bad_fcs = stat_rx_err_bad_fcs_reg;
assign stat_rx_err_bad_block = stat_rx_err_bad_block_reg;
assign stat_rx_err_framing = stat_rx_err_framing_reg;
assign stat_rx_err_preamble = stat_rx_err_preamble_reg;
wire last_cycle = state_reg == STATE_LAST;
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(32)
)
eth_crc (
.data_in(xgmii_rxd_d0),
.state_in(crc_state),
.data_out(),
.state_out(crc_next)
);
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
frame_oversize_next = frame_oversize_reg;
pre_ok_next = pre_ok_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
m_axis_rx_tdata_next = xgmii_rxd_d2;
m_axis_rx_tkeep_next = {KEEP_W{1'b1}};
m_axis_rx_tvalid_next = 1'b0;
m_axis_rx_tlast_next = 1'b0;
m_axis_rx_tuser_next = 1'b0;
ptp_ts_out_next = ptp_ts_out_reg;
start_packet_next = 1'b0;
stat_rx_byte_next = '0;
stat_rx_pkt_len_next = '0;
stat_rx_pkt_fragment_next = 1'b0;
stat_rx_pkt_jabber_next = 1'b0;
stat_rx_pkt_ucast_next = 1'b0;
stat_rx_pkt_mcast_next = 1'b0;
stat_rx_pkt_bcast_next = 1'b0;
stat_rx_pkt_vlan_next = 1'b0;
stat_rx_pkt_good_next = 1'b0;
stat_rx_pkt_bad_next = 1'b0;
stat_rx_err_oversize_next = 1'b0;
stat_rx_err_bad_fcs_next = 1'b0;
stat_rx_err_bad_block_next = 1'b0;
stat_rx_err_framing_next = 1'b0;
stat_rx_err_preamble_next = 1'b0;
// counter to measure frame length
if (&frame_len_reg[15:2] == 0) begin
if (term_present_reg) begin
frame_len_next = frame_len_reg + 16'(term_lane_reg);
end else begin
frame_len_next = frame_len_reg + 16'(CTRL_W);
end
end else begin
frame_len_next = '1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg[15:2] != 0) begin
frame_len_lim_next = frame_len_lim_reg - 16'(CTRL_W);
end else begin
frame_len_lim_next = '0;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
3'd0: begin
is_mcast_next = xgmii_rxd_d2[0];
is_bcast_next = &xgmii_rxd_d2;
end
3'd1: is_bcast_next = is_bcast_reg && &xgmii_rxd_d2[15:0];
3'd3: is_8021q_next = {xgmii_rxd_d2[7:0], xgmii_rxd_d2[15:8]} == 16'h8100;
default: begin
// do nothing
end
endcase
case (state_reg)
STATE_IDLE: begin
// idle state - wait for packet
reset_crc = 1'b1;
frame_len_next = 16'(CTRL_W);
frame_len_lim_next = cfg_rx_max_pkt_len;
hdr_ptr_next = 0;
pre_ok_next = xgmii_rxd_d2[31:8] == 24'h555555;
if (xgmii_start_d2 && cfg_rx_enable) begin
// start condition
if (framing_error_reg) begin
// control or error characters in first data word
stat_rx_err_framing_next = 1'b1;
state_next = STATE_IDLE;
end else begin
reset_crc = 1'b0;
stat_rx_byte_next = 3'(CTRL_W);
state_next = STATE_PREAMBLE;
end
end else begin
if (PTP_TS_EN) begin
ptp_ts_out_next = ptp_ts;
end
state_next = STATE_IDLE;
end
end
STATE_PREAMBLE: begin
// drop preamble
frame_len_lim_next = cfg_rx_max_pkt_len;
hdr_ptr_next = 0;
pre_ok_next = pre_ok_reg && xgmii_rxd_d2 == 32'hD5555555;
if (framing_error_reg) begin
// control or error characters in packet
stat_rx_err_framing_next = 1'b1;
state_next = STATE_IDLE;
end else begin
start_packet_next = 1'b1;
stat_rx_byte_next = 3'(CTRL_W);
state_next = STATE_PAYLOAD;
end
end
STATE_PAYLOAD: begin
// read payload
m_axis_rx_tdata_next = xgmii_rxd_d2;
m_axis_rx_tkeep_next = {KEEP_W{1'b1}};
m_axis_rx_tvalid_next = 1'b1;
m_axis_rx_tlast_next = 1'b0;
m_axis_rx_tuser_next = 1'b0;
if (term_present_reg) begin
stat_rx_byte_next = 3'(term_lane_reg);
frame_oversize_next = frame_len_lim_reg < 16'(4+4+term_lane_reg);
end else begin
stat_rx_byte_next = 3'(CTRL_W);
frame_oversize_next = frame_len_lim_reg < 4+4;
end
if (framing_error_reg) begin
// control or error characters in packet
m_axis_rx_tlast_next = 1'b1;
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_pkt_len_next = frame_len_next;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_next;
stat_rx_err_framing_next = 1'b1;
stat_rx_err_preamble_next = !pre_ok_reg;
stat_rx_pkt_fragment_next = frame_len_next[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_next;
reset_crc = 1'b1;
state_next = STATE_IDLE;
end else if (term_present_reg) begin
reset_crc = 1'b1;
if (term_lane_reg == 0) begin
// end this cycle
m_axis_rx_tkeep_next = 4'b1111;
m_axis_rx_tlast_next = 1'b1;
if (term_lane_reg == 0 && crc_valid_save[3]) begin
// CRC valid
if (frame_oversize_next) begin
// too long
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
end else begin
// length OK
m_axis_rx_tuser_next = 1'b0;
stat_rx_pkt_good_next = 1'b1;
end
end else begin
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_next[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_next;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_err_bad_fcs_next = 1'b1;
end
stat_rx_pkt_len_next = frame_len_next;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_next;
stat_rx_err_preamble_next = !pre_ok_reg;
state_next = STATE_IDLE;
end else begin
// need extra cycle
state_next = STATE_LAST;
end
end else begin
state_next = STATE_PAYLOAD;
end
end
STATE_LAST: begin
// last cycle of packet
m_axis_rx_tdata_next = xgmii_rxd_d2;
m_axis_rx_tkeep_next = {KEEP_W{1'b1}} >> 2'(CTRL_W-term_lane_d0_reg);
m_axis_rx_tvalid_next = 1'b1;
m_axis_rx_tlast_next = 1'b1;
m_axis_rx_tuser_next = 1'b0;
reset_crc = 1'b1;
if ((term_lane_d0_reg == 1 && crc_valid_save[0]) ||
(term_lane_d0_reg == 2 && crc_valid_save[1]) ||
(term_lane_d0_reg == 3 && crc_valid_save[2])) begin
// CRC valid
if (frame_oversize_reg) begin
// too long
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
end else begin
// length OK
m_axis_rx_tuser_next = 1'b0;
stat_rx_pkt_good_next = 1'b1;
end
end else begin
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_reg[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_reg;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_err_bad_fcs_next = 1'b1;
end
stat_rx_pkt_len_next = frame_len_reg;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_reg;
stat_rx_err_preamble_next = !pre_ok_reg;
state_next = STATE_IDLE;
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
end
always_ff @(posedge clk) begin
state_reg <= state_next;
frame_oversize_reg <= frame_oversize_next;
pre_ok_reg <= pre_ok_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
m_axis_rx_tdata_reg <= m_axis_rx_tdata_next;
m_axis_rx_tkeep_reg <= m_axis_rx_tkeep_next;
m_axis_rx_tvalid_reg <= m_axis_rx_tvalid_next;
m_axis_rx_tlast_reg <= m_axis_rx_tlast_next;
m_axis_rx_tuser_reg <= m_axis_rx_tuser_next;
ptp_ts_out_reg <= ptp_ts_out_next;
start_packet_reg <= start_packet_next;
stat_rx_byte_reg <= stat_rx_byte_next;
stat_rx_pkt_len_reg <= stat_rx_pkt_len_next;
stat_rx_pkt_fragment_reg <= stat_rx_pkt_fragment_next;
stat_rx_pkt_jabber_reg <= stat_rx_pkt_jabber_next;
stat_rx_pkt_ucast_reg <= stat_rx_pkt_ucast_next;
stat_rx_pkt_mcast_reg <= stat_rx_pkt_mcast_next;
stat_rx_pkt_bcast_reg <= stat_rx_pkt_bcast_next;
stat_rx_pkt_vlan_reg <= stat_rx_pkt_vlan_next;
stat_rx_pkt_good_reg <= stat_rx_pkt_good_next;
stat_rx_pkt_bad_reg <= stat_rx_pkt_bad_next;
stat_rx_err_oversize_reg <= stat_rx_err_oversize_next;
stat_rx_err_bad_fcs_reg <= stat_rx_err_bad_fcs_next;
stat_rx_err_bad_block_reg <= stat_rx_err_bad_block_next;
stat_rx_err_framing_reg <= stat_rx_err_framing_next;
stat_rx_err_preamble_reg <= stat_rx_err_preamble_next;
term_lane_reg <= 0;
term_present_reg <= 1'b0;
framing_error_reg <= xgmii_rxc != 0;
for (integer i = CTRL_W-1; i >= 0; i = i - 1) begin
if (xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM)) begin
term_lane_reg <= 2'(i);
term_present_reg <= 1'b1;
framing_error_reg <= (xgmii_rxc & ({CTRL_W{1'b1}} >> (CTRL_W-i))) != 0;
end
end
term_lane_d0_reg <= term_lane_reg;
if (reset_crc) begin
crc_state <= '1;
end else begin
crc_state <= crc_next;
end
crc_valid_save <= crc_valid;
for (integer i = 0; i < CTRL_W; i = i + 1) begin
xgmii_rxd_d0[i*8 +: 8] <= xgmii_rxc[i] ? 8'd0 : xgmii_rxd[i*8 +: 8];
end
xgmii_rxc_d0 <= xgmii_rxc;
xgmii_rxd_d1 <= xgmii_rxd_d0;
xgmii_rxd_d2 <= xgmii_rxd_d1;
xgmii_start_d0 <= xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START;
xgmii_start_d1 <= xgmii_start_d0;
xgmii_start_d2 <= xgmii_start_d1;
if (rst) begin
state_reg <= STATE_IDLE;
m_axis_rx_tvalid_reg <= 1'b0;
start_packet_reg <= 1'b0;
stat_rx_byte_reg <= '0;
stat_rx_pkt_len_reg <= '0;
stat_rx_pkt_fragment_reg <= 1'b0;
stat_rx_pkt_jabber_reg <= 1'b0;
stat_rx_pkt_ucast_reg <= 1'b0;
stat_rx_pkt_mcast_reg <= 1'b0;
stat_rx_pkt_bcast_reg <= 1'b0;
stat_rx_pkt_vlan_reg <= 1'b0;
stat_rx_pkt_good_reg <= 1'b0;
stat_rx_pkt_bad_reg <= 1'b0;
stat_rx_err_oversize_reg <= 1'b0;
stat_rx_err_bad_fcs_reg <= 1'b0;
stat_rx_err_bad_block_reg <= 1'b0;
stat_rx_err_framing_reg <= 1'b0;
stat_rx_err_preamble_reg <= 1'b0;
xgmii_rxc_d0 <= '0;
xgmii_start_d0 <= 1'b0;
xgmii_start_d1 <= 1'b0;
xgmii_start_d2 <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,640 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream XGMII frame receiver (XGMII in, AXI out)
*/
module taxi_axis_xgmii_rx_64 #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
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
)
(
input wire logic clk,
input wire logic rst,
/*
* XGMII input
*/
input wire logic [DATA_W-1:0] xgmii_rxd,
input wire logic [CTRL_W-1:0] xgmii_rxc,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Configuration
*/
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable,
/*
* Status
*/
output wire logic [1:0] rx_start_packet,
output wire logic [3:0] stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble
);
// extract parameters
localparam KEEP_W = DATA_W/8;
localparam USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64 (instance %m)");
if (KEEP_W*8 != DATA_W || CTRL_W*8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity (instance %m)");
if (m_axis_rx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (m_axis_rx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [7:0]
XGMII_IDLE = 8'h07,
XGMII_START = 8'hfb,
XGMII_TERM = 8'hfd,
XGMII_ERROR = 8'hfe;
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_PAYLOAD = 2'd1,
STATE_LAST = 2'd2;
logic [1:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic lanes_swapped = 1'b0;
logic [31:0] swap_rxd = 32'd0;
logic [3:0] swap_rxc = 4'd0;
logic [3:0] swap_rxc_term = 4'd0;
logic [2:0] term_lane_reg = 0, term_lane_d0_reg = 0;
logic term_present_reg = 1'b0;
logic framing_error_reg = 1'b0, framing_error_d0_reg = 1'b0;
logic [DATA_W-1:0] xgmii_rxd_d0 = '0;
logic [DATA_W-1:0] xgmii_rxd_d1 = '0;
logic [CTRL_W-1:0] xgmii_rxc_d0 = '0;
logic xgmii_start_swap = 1'b0;
logic xgmii_start_d0 = 1'b0;
logic xgmii_start_d1 = 1'b0;
logic frame_oversize_reg = 1'b0, frame_oversize_next;
logic pre_ok_reg = 1'b0, pre_ok_next;
logic [1:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [DATA_W-1:0] m_axis_rx_tdata_reg = '0, m_axis_rx_tdata_next;
logic [KEEP_W-1:0] m_axis_rx_tkeep_reg = '0, m_axis_rx_tkeep_next;
logic m_axis_rx_tvalid_reg = 1'b0, m_axis_rx_tvalid_next;
logic m_axis_rx_tlast_reg = 1'b0, m_axis_rx_tlast_next;
logic m_axis_rx_tuser_reg = 1'b0, m_axis_rx_tuser_next;
logic [1:0] start_packet_reg = 2'b00;
logic [3:0] stat_rx_byte_reg = '0, stat_rx_byte_next;
logic [15:0] stat_rx_pkt_len_reg = '0, stat_rx_pkt_len_next;
logic stat_rx_pkt_fragment_reg = 1'b0, stat_rx_pkt_fragment_next;
logic stat_rx_pkt_jabber_reg = 1'b0, stat_rx_pkt_jabber_next;
logic stat_rx_pkt_ucast_reg = 1'b0, stat_rx_pkt_ucast_next;
logic stat_rx_pkt_mcast_reg = 1'b0, stat_rx_pkt_mcast_next;
logic stat_rx_pkt_bcast_reg = 1'b0, stat_rx_pkt_bcast_next;
logic stat_rx_pkt_vlan_reg = 1'b0, stat_rx_pkt_vlan_next;
logic stat_rx_pkt_good_reg = 1'b0, stat_rx_pkt_good_next;
logic stat_rx_pkt_bad_reg = 1'b0, stat_rx_pkt_bad_next;
logic stat_rx_err_oversize_reg = 1'b0, stat_rx_err_oversize_next;
logic stat_rx_err_bad_fcs_reg = 1'b0, stat_rx_err_bad_fcs_next;
logic stat_rx_err_bad_block_reg = 1'b0, stat_rx_err_bad_block_next;
logic stat_rx_err_framing_reg = 1'b0, stat_rx_err_framing_next;
logic stat_rx_err_preamble_reg = 1'b0, stat_rx_err_preamble_next;
logic [PTP_TS_W-1:0] ptp_ts_reg = '0;
logic [PTP_TS_W-1:0] ptp_ts_out_reg = '0, ptp_ts_out_next;
logic [PTP_TS_W-1:0] ptp_ts_adj_reg = '0;
logic ptp_ts_borrow_reg = '0;
logic [31:0] crc_state = '1;
wire [31:0] crc_next;
wire [7:0] crc_valid;
logic [7:0] crc_valid_save;
assign crc_valid[7] = crc_next == ~32'h2144df1c;
assign crc_valid[6] = crc_next == ~32'hc622f71d;
assign crc_valid[5] = crc_next == ~32'hb1c2a1a3;
assign crc_valid[4] = crc_next == ~32'h9d6cdf7e;
assign crc_valid[3] = crc_next == ~32'h6522df69;
assign crc_valid[2] = crc_next == ~32'he60914ae;
assign crc_valid[1] = crc_next == ~32'he38a6876;
assign crc_valid[0] = crc_next == ~32'h6b87b1ec;
logic [4+16-1:0] last_ts_reg = '0;
logic [4+16-1:0] ts_inc_reg = '0;
assign m_axis_rx.tdata = m_axis_rx_tdata_reg;
assign m_axis_rx.tkeep = m_axis_rx_tkeep_reg;
assign m_axis_rx.tstrb = m_axis_rx.tkeep;
assign m_axis_rx.tvalid = m_axis_rx_tvalid_reg;
assign m_axis_rx.tlast = m_axis_rx_tlast_reg;
assign m_axis_rx.tid = '0;
assign m_axis_rx.tdest = '0;
assign m_axis_rx.tuser[0] = m_axis_rx_tuser_reg;
if (PTP_TS_EN) begin
assign m_axis_rx.tuser[1 +: PTP_TS_W] = ptp_ts_out_reg;
end
assign rx_start_packet = start_packet_reg;
assign stat_rx_byte = stat_rx_byte_reg;
assign stat_rx_pkt_len = stat_rx_pkt_len_reg;
assign stat_rx_pkt_fragment = stat_rx_pkt_fragment_reg;
assign stat_rx_pkt_jabber = stat_rx_pkt_jabber_reg;
assign stat_rx_pkt_ucast = stat_rx_pkt_ucast_reg;
assign stat_rx_pkt_mcast = stat_rx_pkt_mcast_reg;
assign stat_rx_pkt_bcast = stat_rx_pkt_bcast_reg;
assign stat_rx_pkt_vlan = stat_rx_pkt_vlan_reg;
assign stat_rx_pkt_good = stat_rx_pkt_good_reg;
assign stat_rx_pkt_bad = stat_rx_pkt_bad_reg;
assign stat_rx_err_oversize = stat_rx_err_oversize_reg;
assign stat_rx_err_bad_fcs = stat_rx_err_bad_fcs_reg;
assign stat_rx_err_bad_block = stat_rx_err_bad_block_reg;
assign stat_rx_err_framing = stat_rx_err_framing_reg;
assign stat_rx_err_preamble = stat_rx_err_preamble_reg;
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(64)
)
eth_crc (
.data_in(xgmii_rxd_d0),
.state_in(crc_state),
.data_out(),
.state_out(crc_next)
);
// Mask input data
wire [DATA_W-1:0] xgmii_rxd_masked;
wire [CTRL_W-1:0] xgmii_term;
for (genvar n = 0; n < CTRL_W; n = n + 1) begin
assign xgmii_rxd_masked[n*8 +: 8] = xgmii_rxc[n] ? 8'd0 : xgmii_rxd[n*8 +: 8];
assign xgmii_term[n] = xgmii_rxc[n] && (xgmii_rxd[n*8 +: 8] == XGMII_TERM);
end
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
frame_oversize_next = frame_oversize_reg;
pre_ok_next = pre_ok_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
m_axis_rx_tdata_next = xgmii_rxd_d1;
m_axis_rx_tkeep_next = {KEEP_W{1'b1}};
m_axis_rx_tvalid_next = 1'b0;
m_axis_rx_tlast_next = 1'b0;
m_axis_rx_tuser_next = 1'b0;
ptp_ts_out_next = ptp_ts_out_reg;
stat_rx_byte_next = '0;
stat_rx_pkt_len_next = '0;
stat_rx_pkt_fragment_next = 1'b0;
stat_rx_pkt_jabber_next = 1'b0;
stat_rx_pkt_ucast_next = 1'b0;
stat_rx_pkt_mcast_next = 1'b0;
stat_rx_pkt_bcast_next = 1'b0;
stat_rx_pkt_vlan_next = 1'b0;
stat_rx_pkt_good_next = 1'b0;
stat_rx_pkt_bad_next = 1'b0;
stat_rx_err_oversize_next = 1'b0;
stat_rx_err_bad_fcs_next = 1'b0;
stat_rx_err_bad_block_next = 1'b0;
stat_rx_err_framing_next = 1'b0;
stat_rx_err_preamble_next = 1'b0;
// counter to measure frame length
if (&frame_len_reg[15:3] == 0) begin
if (term_present_reg) begin
frame_len_next = frame_len_reg + 16'(term_lane_reg);
end else begin
frame_len_next = frame_len_reg + 16'(CTRL_W);
end
end else begin
frame_len_next = '1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg[15:3] != 0) begin
frame_len_lim_next = frame_len_lim_reg - 16'(CTRL_W);
end else begin
frame_len_lim_next = '0;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
2'd0: begin
is_mcast_next = xgmii_rxd_d1[0];
is_bcast_next = &xgmii_rxd_d1[47:0];
end
2'd1: is_8021q_next = {xgmii_rxd_d1[39:32], xgmii_rxd_d1[47:40]} == 16'h8100;
default: begin
// do nothing
end
endcase
case (state_reg)
STATE_IDLE: begin
// idle state - wait for packet
reset_crc = 1'b1;
frame_len_next = 16'(CTRL_W);
frame_len_lim_next = cfg_rx_max_pkt_len;
hdr_ptr_next = 0;
pre_ok_next = xgmii_rxd_d1[63:8] == 56'hD5555555555555;
if (xgmii_start_d1 && cfg_rx_enable) begin
// start condition
reset_crc = 1'b0;
stat_rx_byte_next = 4'(CTRL_W);
state_next = STATE_PAYLOAD;
end else begin
state_next = STATE_IDLE;
end
end
STATE_PAYLOAD: begin
// read payload
m_axis_rx_tdata_next = xgmii_rxd_d1;
m_axis_rx_tkeep_next = {KEEP_W{1'b1}};
m_axis_rx_tvalid_next = 1'b1;
m_axis_rx_tlast_next = 1'b0;
m_axis_rx_tuser_next = 1'b0;
if (PTP_TS_EN) begin
ptp_ts_out_next = (!PTP_TS_FMT_TOD || ptp_ts_borrow_reg) ? ptp_ts_reg : ptp_ts_adj_reg;
end
if (term_present_reg) begin
stat_rx_byte_next = 4'(term_lane_reg);
frame_oversize_next = frame_len_lim_reg < 16'(8+term_lane_reg);
end else begin
stat_rx_byte_next = 4'(CTRL_W);
frame_oversize_next = frame_len_lim_reg < 8;
end
if (framing_error_reg || framing_error_d0_reg) begin
// control or error characters in packet
m_axis_rx_tlast_next = 1'b1;
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_pkt_len_next = frame_len_next;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_next;
stat_rx_err_framing_next = 1'b1;
stat_rx_err_preamble_next = !pre_ok_reg;
stat_rx_pkt_fragment_next = frame_len_next[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_next;
reset_crc = 1'b1;
state_next = STATE_IDLE;
end else if (term_present_reg) begin
reset_crc = 1'b1;
if (term_lane_reg <= 4) begin
// end this cycle
m_axis_rx_tkeep_next = {KEEP_W{1'b1}} >> 3'(CTRL_W-4-term_lane_reg);
m_axis_rx_tlast_next = 1'b1;
if ((term_lane_reg == 0 && crc_valid_save[7]) ||
(term_lane_reg == 1 && crc_valid[0]) ||
(term_lane_reg == 2 && crc_valid[1]) ||
(term_lane_reg == 3 && crc_valid[2]) ||
(term_lane_reg == 4 && crc_valid[3])) begin
// CRC valid
if (frame_oversize_next) begin
// too long
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
end else begin
// length OK
m_axis_rx_tuser_next = 1'b0;
stat_rx_pkt_good_next = 1'b1;
end
end else begin
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_next[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_next;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_err_bad_fcs_next = 1'b1;
end
stat_rx_pkt_len_next = frame_len_next;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_next;
stat_rx_err_preamble_next = !pre_ok_reg;
state_next = STATE_IDLE;
end else begin
// need extra cycle
state_next = STATE_LAST;
end
end else begin
state_next = STATE_PAYLOAD;
end
end
STATE_LAST: begin
// last cycle of packet
m_axis_rx_tdata_next = xgmii_rxd_d1;
m_axis_rx_tkeep_next = {KEEP_W{1'b1}} >> 3'(CTRL_W+4-term_lane_d0_reg);
m_axis_rx_tvalid_next = 1'b1;
m_axis_rx_tlast_next = 1'b1;
m_axis_rx_tuser_next = 1'b0;
reset_crc = 1'b1;
if ((term_lane_d0_reg == 5 && crc_valid_save[4]) ||
(term_lane_d0_reg == 6 && crc_valid_save[5]) ||
(term_lane_d0_reg == 7 && crc_valid_save[6])) begin
// CRC valid
if (frame_oversize_reg) begin
// too long
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_bad_next = 1'b1;
end else begin
// length OK
m_axis_rx_tuser_next = 1'b0;
stat_rx_pkt_good_next = 1'b1;
end
end else begin
m_axis_rx_tuser_next = 1'b1;
stat_rx_pkt_fragment_next = frame_len_reg[15:6] == 0;
stat_rx_pkt_jabber_next = frame_oversize_reg;
stat_rx_pkt_bad_next = 1'b1;
stat_rx_err_bad_fcs_next = 1'b1;
end
stat_rx_pkt_len_next = frame_len_reg;
stat_rx_pkt_ucast_next = !is_mcast_reg;
stat_rx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_rx_pkt_bcast_next = is_bcast_reg;
stat_rx_pkt_vlan_next = is_8021q_reg;
stat_rx_err_oversize_next = frame_oversize_reg;
stat_rx_err_preamble_next = !pre_ok_reg;
if (xgmii_start_d1 && cfg_rx_enable) begin
// start condition
reset_crc = 1'b0;
state_next = STATE_PAYLOAD;
end else begin
state_next = STATE_IDLE;
end
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
end
always_ff @(posedge clk) begin
state_reg <= state_next;
frame_oversize_reg <= frame_oversize_next;
pre_ok_reg <= pre_ok_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
m_axis_rx_tdata_reg <= m_axis_rx_tdata_next;
m_axis_rx_tkeep_reg <= m_axis_rx_tkeep_next;
m_axis_rx_tvalid_reg <= m_axis_rx_tvalid_next;
m_axis_rx_tlast_reg <= m_axis_rx_tlast_next;
m_axis_rx_tuser_reg <= m_axis_rx_tuser_next;
ptp_ts_out_reg <= ptp_ts_out_next;
start_packet_reg <= 2'b00;
stat_rx_byte_reg <= stat_rx_byte_next;
stat_rx_pkt_len_reg <= stat_rx_pkt_len_next;
stat_rx_pkt_fragment_reg <= stat_rx_pkt_fragment_next;
stat_rx_pkt_jabber_reg <= stat_rx_pkt_jabber_next;
stat_rx_pkt_ucast_reg <= stat_rx_pkt_ucast_next;
stat_rx_pkt_mcast_reg <= stat_rx_pkt_mcast_next;
stat_rx_pkt_bcast_reg <= stat_rx_pkt_bcast_next;
stat_rx_pkt_vlan_reg <= stat_rx_pkt_vlan_next;
stat_rx_pkt_good_reg <= stat_rx_pkt_good_next;
stat_rx_pkt_bad_reg <= stat_rx_pkt_bad_next;
stat_rx_err_oversize_reg <= stat_rx_err_oversize_next;
stat_rx_err_bad_fcs_reg <= stat_rx_err_bad_fcs_next;
stat_rx_err_bad_block_reg <= stat_rx_err_bad_block_next;
stat_rx_err_framing_reg <= stat_rx_err_framing_next;
stat_rx_err_preamble_reg <= stat_rx_err_preamble_next;
swap_rxd <= xgmii_rxd_masked[63:32];
swap_rxc <= xgmii_rxc[7:4];
swap_rxc_term <= xgmii_term[7:4];
xgmii_start_swap <= 1'b0;
xgmii_start_d0 <= xgmii_start_swap;
if (PTP_TS_EN && PTP_TS_FMT_TOD) begin
// ns field rollover
ptp_ts_adj_reg[15:0] <= ptp_ts_reg[15:0];
{ptp_ts_borrow_reg, ptp_ts_adj_reg[45:16]} <= $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000);
ptp_ts_adj_reg[47:46] <= 0;
ptp_ts_adj_reg[95:48] <= ptp_ts_reg[95:48] + 1;
end
// lane swapping and termination character detection
if (lanes_swapped) begin
xgmii_rxd_d0 <= {xgmii_rxd_masked[31:0], swap_rxd};
xgmii_rxc_d0 <= {xgmii_rxc[3:0], swap_rxc};
term_lane_reg <= 0;
term_present_reg <= 1'b0;
framing_error_reg <= {xgmii_rxc[3:0], swap_rxc} != 0;
for (integer i = CTRL_W-1; i >= 0; i = i - 1) begin
if ({xgmii_term[3:0], swap_rxc_term}[i]) begin
term_lane_reg <= 3'(i);
term_present_reg <= 1'b1;
framing_error_reg <= ({xgmii_rxc[3:0], swap_rxc} & ({CTRL_W{1'b1}} >> (CTRL_W-i))) != 0;
end
end
end else begin
xgmii_rxd_d0 <= xgmii_rxd_masked;
xgmii_rxc_d0 <= xgmii_rxc;
term_lane_reg <= 0;
term_present_reg <= 1'b0;
framing_error_reg <= xgmii_rxc != 0;
for (integer i = CTRL_W-1; i >= 0; i = i - 1) begin
if (xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM)) begin
term_lane_reg <= 3'(i);
term_present_reg <= 1'b1;
framing_error_reg <= (xgmii_rxc & ({CTRL_W{1'b1}} >> (CTRL_W-i))) != 0;
end
end
end
// start control character detection
if (xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START) begin
lanes_swapped <= 1'b0;
xgmii_start_d0 <= 1'b1;
xgmii_rxd_d0 <= xgmii_rxd_masked;
xgmii_rxc_d0 <= xgmii_rxc;
framing_error_reg <= xgmii_rxc[7:1] != 0;
end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == XGMII_START) begin
lanes_swapped <= 1'b1;
xgmii_start_swap <= 1'b1;
framing_error_reg <= xgmii_rxc[7:5] != 0;
end
// capture timestamps
if (xgmii_start_swap) begin
start_packet_reg <= 2'b10;
if (PTP_TS_FMT_TOD) begin
ptp_ts_reg[45:0] <= ptp_ts[45:0] + 46'(ts_inc_reg >> 1);
ptp_ts_reg[95:48] <= ptp_ts[95:48];
end else begin
ptp_ts_reg <= ptp_ts + PTP_TS_W'(ts_inc_reg >> 1);
end
end
if (xgmii_start_d0) begin
if (!lanes_swapped) begin
start_packet_reg <= 2'b01;
ptp_ts_reg <= ptp_ts;
end
end
term_lane_d0_reg <= term_lane_reg;
framing_error_d0_reg <= framing_error_reg;
if (reset_crc) begin
crc_state <= '1;
end else begin
crc_state <= crc_next;
end
crc_valid_save <= crc_valid;
xgmii_rxd_d1 <= xgmii_rxd_d0;
xgmii_start_d1 <= xgmii_start_d0;
last_ts_reg <= (4+16)'(ptp_ts);
ts_inc_reg <= (4+16)'(ptp_ts) - last_ts_reg;
if (rst) begin
state_reg <= STATE_IDLE;
m_axis_rx_tvalid_reg <= 1'b0;
start_packet_reg <= 2'b00;
stat_rx_byte_reg <= '0;
stat_rx_pkt_len_reg <= '0;
stat_rx_pkt_fragment_reg <= 1'b0;
stat_rx_pkt_jabber_reg <= 1'b0;
stat_rx_pkt_ucast_reg <= 1'b0;
stat_rx_pkt_mcast_reg <= 1'b0;
stat_rx_pkt_bcast_reg <= 1'b0;
stat_rx_pkt_vlan_reg <= 1'b0;
stat_rx_pkt_good_reg <= 1'b0;
stat_rx_pkt_bad_reg <= 1'b0;
stat_rx_err_oversize_reg <= 1'b0;
stat_rx_err_bad_fcs_reg <= 1'b0;
stat_rx_err_bad_block_reg <= 1'b0;
stat_rx_err_framing_reg <= 1'b0;
stat_rx_err_preamble_reg <= 1'b0;
xgmii_rxc_d0 <= '0;
xgmii_start_swap <= 1'b0;
xgmii_start_d0 <= 1'b0;
xgmii_start_d1 <= 1'b0;
lanes_swapped <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,717 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream XGMII frame transmitter (AXI in, XGMII out)
*/
module taxi_axis_xgmii_tx_32 #
(
parameter DATA_W = 32,
parameter CTRL_W = (DATA_W/8),
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96,
parameter logic TX_CPL_CTRL_IN_TUSER = 1'b1
)
(
input wire logic clk,
input wire logic rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* XGMII output
*/
output wire logic [DATA_W-1:0] xgmii_txd,
output wire logic [CTRL_W-1:0] xgmii_txc,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable,
/*
* Status
*/
output wire logic tx_start_packet,
output wire logic [2:0] stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow
);
// extract parameters
localparam KEEP_W = DATA_W/8;
localparam USER_W = TX_CPL_CTRL_IN_TUSER ? 2 : 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
localparam EMPTY_W = $clog2(KEEP_W);
localparam MIN_LEN_W = $clog2(MIN_FRAME_LEN-4-CTRL_W+1);
// check configuration
if (DATA_W != 32)
$fatal(0, "Error: Interface width must be 32 (instance %m)");
if (KEEP_W*8 != DATA_W || CTRL_W*8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity (instance %m)");
if (s_axis_tx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (s_axis_tx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [7:0]
XGMII_IDLE = 8'h07,
XGMII_START = 8'hfb,
XGMII_TERM = 8'hfd,
XGMII_ERROR = 8'hfe;
localparam [3:0]
STATE_IDLE = 4'd0,
STATE_PREAMBLE = 4'd1,
STATE_PAYLOAD = 4'd2,
STATE_PAD = 4'd3,
STATE_FCS_1 = 4'd4,
STATE_FCS_2 = 4'd5,
STATE_FCS_3 = 4'd6,
STATE_ERR = 4'd7,
STATE_IFG = 4'd8;
logic [3:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic update_crc;
logic [DATA_W-1:0] s_tdata_reg = '0, s_tdata_next;
logic [EMPTY_W-1:0] s_empty_reg = '0, s_empty_next;
logic [DATA_W-1:0] fcs_output_txd_0;
logic [DATA_W-1:0] fcs_output_txd_1;
logic [CTRL_W-1:0] fcs_output_txc_0;
logic [CTRL_W-1:0] fcs_output_txc_1;
logic [7:0] ifg_offset;
logic extra_cycle;
logic frame_reg = 1'b0, frame_next;
logic frame_error_reg = 1'b0, frame_error_next;
logic frame_oversize_reg = 1'b0, frame_oversize_next;
logic [MIN_LEN_W-1:0] frame_min_count_reg = '0, frame_min_count_next;
logic [2:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [7:0] ifg_cnt_reg = '0, ifg_cnt_next;
logic [7:0] ifg_count_reg = 8'd0, ifg_count_next;
logic [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next;
logic s_axis_tx_tready_reg = 1'b0, s_axis_tx_tready_next;
logic [PTP_TS_W-1:0] m_axis_tx_cpl_ts_reg = '0, m_axis_tx_cpl_ts_next;
logic [TX_TAG_W-1:0] m_axis_tx_cpl_tag_reg = '0, m_axis_tx_cpl_tag_next;
logic m_axis_tx_cpl_valid_reg = 1'b0, m_axis_tx_cpl_valid_next;
logic [31:0] crc_state_reg[3:0];
wire [31:0] crc_state_next[3:0];
logic [DATA_W-1:0] xgmii_txd_reg = {CTRL_W{XGMII_IDLE}}, xgmii_txd_next;
logic [CTRL_W-1:0] xgmii_txc_reg = {CTRL_W{1'b1}}, xgmii_txc_next;
logic start_packet_reg = 1'b0, start_packet_next;
logic [2:0] stat_tx_byte_reg = '0, stat_tx_byte_next;
logic [15:0] stat_tx_pkt_len_reg = '0, stat_tx_pkt_len_next;
logic stat_tx_pkt_ucast_reg = 1'b0, stat_tx_pkt_ucast_next;
logic stat_tx_pkt_mcast_reg = 1'b0, stat_tx_pkt_mcast_next;
logic stat_tx_pkt_bcast_reg = 1'b0, stat_tx_pkt_bcast_next;
logic stat_tx_pkt_vlan_reg = 1'b0, stat_tx_pkt_vlan_next;
logic stat_tx_pkt_good_reg = 1'b0, stat_tx_pkt_good_next;
logic stat_tx_pkt_bad_reg = 1'b0, stat_tx_pkt_bad_next;
logic stat_tx_err_oversize_reg = 1'b0, stat_tx_err_oversize_next;
logic stat_tx_err_user_reg = 1'b0, stat_tx_err_user_next;
logic stat_tx_err_underflow_reg = 1'b0, stat_tx_err_underflow_next;
assign s_axis_tx.tready = s_axis_tx_tready_reg;
assign xgmii_txd = xgmii_txd_reg;
assign xgmii_txc = xgmii_txc_reg;
assign m_axis_tx_cpl.tdata = PTP_TS_EN ? m_axis_tx_cpl_ts_reg : '0;
assign m_axis_tx_cpl.tkeep = 1'b1;
assign m_axis_tx_cpl.tstrb = m_axis_tx_cpl.tkeep;
assign m_axis_tx_cpl.tvalid = m_axis_tx_cpl_valid_reg;
assign m_axis_tx_cpl.tlast = 1'b1;
assign m_axis_tx_cpl.tid = m_axis_tx_cpl_tag_reg;
assign m_axis_tx_cpl.tdest = '0;
assign m_axis_tx_cpl.tuser = '0;
assign tx_start_packet = start_packet_reg;
assign stat_tx_byte = stat_tx_byte_reg;
assign stat_tx_pkt_len = stat_tx_pkt_len_reg;
assign stat_tx_pkt_ucast = stat_tx_pkt_ucast_reg;
assign stat_tx_pkt_mcast = stat_tx_pkt_mcast_reg;
assign stat_tx_pkt_bcast = stat_tx_pkt_bcast_reg;
assign stat_tx_pkt_vlan = stat_tx_pkt_vlan_reg;
assign stat_tx_pkt_good = stat_tx_pkt_good_reg;
assign stat_tx_pkt_bad = stat_tx_pkt_bad_reg;
assign stat_tx_err_oversize = stat_tx_err_oversize_reg;
assign stat_tx_err_user = stat_tx_err_user_reg;
assign stat_tx_err_underflow = stat_tx_err_underflow_reg;
for (genvar n = 0; n < 4; n = n + 1) begin : crc
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(8*(n+1))
)
eth_crc (
.data_in(s_tdata_reg[0 +: 8*(n+1)]),
.state_in(crc_state_reg[3]),
.data_out(),
.state_out(crc_state_next[n])
);
end
function [1:0] keep2empty(input [3:0] k);
casez (k)
4'bzzz0: keep2empty = 2'd3;
4'bzz01: keep2empty = 2'd3;
4'bz011: keep2empty = 2'd2;
4'b0111: keep2empty = 2'd1;
4'b1111: keep2empty = 2'd0;
endcase
endfunction
// Mask input data
wire [DATA_W-1:0] s_axis_tx_tdata_masked;
for (genvar n = 0; n < CTRL_W; n = n + 1) begin
assign s_axis_tx_tdata_masked[n*8 +: 8] = s_axis_tx.tkeep[n] ? s_axis_tx.tdata[n*8 +: 8] : 8'd0;
end
// FCS cycle calculation
always_comb begin
casez (s_empty_reg)
2'd3: begin
fcs_output_txd_0 = {~crc_state_next[0][23:0], s_tdata_reg[7:0]};
fcs_output_txd_1 = {{2{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[0][31:24]};
fcs_output_txc_0 = 4'b0000;
fcs_output_txc_1 = 4'b1110;
ifg_offset = 8'd3;
extra_cycle = 1'b0;
end
2'd2: begin
fcs_output_txd_0 = {~crc_state_next[1][15:0], s_tdata_reg[15:0]};
fcs_output_txd_1 = {XGMII_IDLE, XGMII_TERM, ~crc_state_reg[1][31:16]};
fcs_output_txc_0 = 4'b0000;
fcs_output_txc_1 = 4'b1100;
ifg_offset = 8'd2;
extra_cycle = 1'b0;
end
2'd1: begin
fcs_output_txd_0 = {~crc_state_next[2][7:0], s_tdata_reg[23:0]};
fcs_output_txd_1 = {XGMII_TERM, ~crc_state_reg[2][31:8]};
fcs_output_txc_0 = 4'b0000;
fcs_output_txc_1 = 4'b1000;
ifg_offset = 8'd1;
extra_cycle = 1'b0;
end
2'd0: begin
fcs_output_txd_0 = s_tdata_reg;
fcs_output_txd_1 = ~crc_state_reg[3];
fcs_output_txc_0 = 4'b0000;
fcs_output_txc_1 = 4'b0000;
ifg_offset = 8'd4;
extra_cycle = 1'b1;
end
endcase
end
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
update_crc = 1'b0;
frame_next = frame_reg;
frame_error_next = frame_error_reg;
frame_oversize_next = frame_oversize_reg;
frame_min_count_next = frame_min_count_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
ifg_cnt_next = ifg_cnt_reg;
ifg_count_next = ifg_count_reg;
deficit_idle_count_next = deficit_idle_count_reg;
s_axis_tx_tready_next = 1'b0;
s_tdata_next = s_tdata_reg;
s_empty_next = s_empty_reg;
m_axis_tx_cpl_ts_next = m_axis_tx_cpl_ts_reg;
m_axis_tx_cpl_tag_next = m_axis_tx_cpl_tag_reg;
m_axis_tx_cpl_valid_next = 1'b0;
if (start_packet_reg) begin
if (PTP_TS_EN) begin
m_axis_tx_cpl_ts_next = ptp_ts;
end
m_axis_tx_cpl_tag_next = s_axis_tx.tid;
if (TX_CPL_CTRL_IN_TUSER) begin
m_axis_tx_cpl_valid_next = (s_axis_tx.tuser >> 1) == 0;
end else begin
m_axis_tx_cpl_valid_next = 1'b1;
end
end
// XGMII idle
xgmii_txd_next = {CTRL_W{XGMII_IDLE}};
xgmii_txc_next = {CTRL_W{1'b1}};
start_packet_next = 1'b0;
stat_tx_byte_next = '0;
stat_tx_pkt_len_next = '0;
stat_tx_pkt_ucast_next = 1'b0;
stat_tx_pkt_mcast_next = 1'b0;
stat_tx_pkt_bcast_next = 1'b0;
stat_tx_pkt_vlan_next = 1'b0;
stat_tx_pkt_good_next = 1'b0;
stat_tx_pkt_bad_next = 1'b0;
stat_tx_err_oversize_next = 1'b0;
stat_tx_err_user_next = 1'b0;
stat_tx_err_underflow_next = 1'b0;
if (s_axis_tx.tvalid && s_axis_tx.tready) begin
frame_next = !s_axis_tx.tlast;
end
// counter for min frame length enforcement
if (frame_min_count_reg > MIN_LEN_W'(CTRL_W)) begin
frame_min_count_next = MIN_LEN_W'(frame_min_count_reg - CTRL_W);
end else begin
frame_min_count_next = 0;
end
// counter to measure frame length
if (&frame_len_reg[15:2] == 0) begin
frame_len_next = frame_len_reg + 16'(CTRL_W);
end else begin
frame_len_next = '1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg[15:2] != 0) begin
frame_len_lim_next = frame_len_lim_reg - 16'(CTRL_W);
end else begin
frame_len_lim_next = '0;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
3'd0: begin
is_mcast_next = s_tdata_reg[0];
is_bcast_next = &s_tdata_reg;
end
3'd1: is_bcast_next = is_bcast_reg && &s_tdata_reg[15:0];
3'd3: is_8021q_next = {s_tdata_reg[7:0], s_tdata_reg[15:8]} == 16'h8100;
default: begin
// do nothing
end
endcase
if (ifg_cnt_reg[7:2] != 0) begin
ifg_cnt_next = ifg_cnt_reg - 8'(CTRL_W);
end else begin
ifg_cnt_next = '0;
end
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_error_next = 1'b0;
frame_min_count_next = MIN_LEN_W'(MIN_FRAME_LEN-4-CTRL_W);
hdr_ptr_next = 0;
frame_len_next = 0;
frame_len_lim_next = cfg_tx_max_pkt_len;
reset_crc = 1'b1;
// XGMII idle
xgmii_txd_next = {CTRL_W{XGMII_IDLE}};
xgmii_txc_next = {CTRL_W{1'b1}};
s_tdata_next = s_axis_tx_tdata_masked;
s_empty_next = keep2empty(s_axis_tx.tkeep);
if (s_axis_tx.tvalid && cfg_tx_enable) begin
// XGMII start and preamble
xgmii_txd_next = {{3{ETH_PRE}}, XGMII_START};
xgmii_txc_next = 4'b0001;
s_axis_tx_tready_next = 1'b1;
state_next = STATE_PREAMBLE;
end else begin
ifg_count_next = 8'd0;
deficit_idle_count_next = 2'd0;
state_next = STATE_IDLE;
end
end
STATE_PREAMBLE: begin
// send preamble
reset_crc = 1'b1;
hdr_ptr_next = 0;
frame_len_next = 0;
frame_len_lim_next = cfg_tx_max_pkt_len;
s_tdata_next = s_axis_tx_tdata_masked;
s_empty_next = keep2empty(s_axis_tx.tkeep);
xgmii_txd_next = {ETH_SFD, {3{ETH_PRE}}};
xgmii_txc_next = {CTRL_W{1'b0}};
s_axis_tx_tready_next = 1'b1;
start_packet_next = 1'b1;
state_next = STATE_PAYLOAD;
end
STATE_PAYLOAD: begin
// transfer payload
update_crc = 1'b1;
s_axis_tx_tready_next = 1'b1;
xgmii_txd_next = s_tdata_reg;
xgmii_txc_next = {CTRL_W{1'b0}};
s_tdata_next = s_axis_tx_tdata_masked;
s_empty_next = keep2empty(s_axis_tx.tkeep);
stat_tx_byte_next = 3'(CTRL_W);
if (s_axis_tx.tvalid && s_axis_tx.tlast) begin
frame_oversize_next = frame_len_lim_reg < 16'(4+4+4-keep2empty(s_axis_tx.tkeep));
end else begin
frame_oversize_next = frame_len_lim_reg < 4+4;
end
if (!s_axis_tx.tvalid || s_axis_tx.tlast || frame_oversize_next) begin
s_axis_tx_tready_next = frame_next; // drop frame
frame_error_next = !s_axis_tx.tvalid || s_axis_tx.tuser[0] || frame_oversize_next;
stat_tx_err_user_next = s_axis_tx.tuser[0];
stat_tx_err_underflow_next = !s_axis_tx.tvalid;
if (PADDING_EN && frame_min_count_reg != 0) begin
if (frame_min_count_reg > MIN_LEN_W'(CTRL_W)) begin
s_empty_next = 0;
state_next = STATE_PAD;
end else begin
if (keep2empty(s_axis_tx.tkeep) > 2'(CTRL_W-frame_min_count_reg)) begin
s_empty_next = 2'(CTRL_W-frame_min_count_reg);
end
state_next = STATE_FCS_1;
end
end else begin
state_next = STATE_FCS_1;
end
end else begin
state_next = STATE_PAYLOAD;
end
end
STATE_PAD: begin
// pad frame to MIN_FRAME_LEN
s_axis_tx_tready_next = frame_next; // drop frame
xgmii_txd_next = s_tdata_reg;
xgmii_txc_next = {CTRL_W{1'b0}};
s_tdata_next = 32'd0;
s_empty_next = 0;
stat_tx_byte_next = 3'(CTRL_W);
update_crc = 1'b1;
if (frame_min_count_reg > MIN_LEN_W'(CTRL_W)) begin
state_next = STATE_PAD;
end else begin
s_empty_next = 2'(CTRL_W-frame_min_count_reg);
state_next = STATE_FCS_1;
end
end
STATE_FCS_1: begin
// last cycle
s_axis_tx_tready_next = frame_next; // drop frame
xgmii_txd_next = fcs_output_txd_0;
xgmii_txc_next = fcs_output_txc_0;
stat_tx_byte_next = 3'(CTRL_W);
update_crc = 1'b1;
ifg_count_next = (cfg_tx_ifg > 8'd12 ? cfg_tx_ifg : 8'd12) - ifg_offset + 8'(deficit_idle_count_reg);
if (frame_error_reg) begin
state_next = STATE_ERR;
end else begin
state_next = STATE_FCS_2;
end
end
STATE_FCS_2: begin
// last cycle
s_axis_tx_tready_next = frame_next; // drop frame
xgmii_txd_next = fcs_output_txd_1;
xgmii_txc_next = fcs_output_txc_1;
stat_tx_byte_next = 4-s_empty_reg;
frame_len_next = frame_len_reg + 16'(4-s_empty_reg);
if (extra_cycle) begin
state_next = STATE_FCS_3;
end else begin
stat_tx_pkt_len_next = frame_len_next;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
state_next = STATE_IFG;
end
end
STATE_FCS_3: begin
// last cycle
s_axis_tx_tready_next = frame_next; // drop frame
xgmii_txd_next = {{3{XGMII_IDLE}}, XGMII_TERM};
xgmii_txc_next = {CTRL_W{1'b1}};
stat_tx_pkt_len_next = frame_len_reg;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
if (DIC_EN) begin
if (ifg_count_next > 8'd3) begin
state_next = STATE_IFG;
end else begin
deficit_idle_count_next = 2'(ifg_count_next);
ifg_count_next = 8'd0;
s_axis_tx_tready_next = 1'b1;
state_next = STATE_IDLE;
end
end else begin
if (ifg_count_next > 8'd0) begin
state_next = STATE_IFG;
end else begin
state_next = STATE_IDLE;
end
end
end
STATE_ERR: begin
// terminate packet with error
s_axis_tx_tready_next = frame_next; // drop frame
// XGMII error
xgmii_txd_next = {XGMII_TERM, {3{XGMII_ERROR}}};
xgmii_txc_next = {CTRL_W{1'b1}};
ifg_count_next = cfg_tx_ifg > 8'd12 ? cfg_tx_ifg : 8'd12;
stat_tx_pkt_len_next = frame_len_reg;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
state_next = STATE_IFG;
end
STATE_IFG: begin
// send IFG
s_axis_tx_tready_next = frame_next; // drop frame
// XGMII idle
xgmii_txd_next = {CTRL_W{XGMII_IDLE}};
xgmii_txc_next = {CTRL_W{1'b1}};
if (ifg_count_reg > 8'd4) begin
ifg_count_next = ifg_count_reg - 8'd4;
end else begin
ifg_count_next = 8'd0;
end
if (DIC_EN) begin
if (ifg_count_next > 8'd3 || frame_reg) begin
state_next = STATE_IFG;
end else begin
deficit_idle_count_next = 2'(ifg_count_next);
ifg_count_next = 8'd0;
state_next = STATE_IDLE;
end
end else begin
if (ifg_count_next > 8'd0 || frame_reg) begin
state_next = STATE_IFG;
end else begin
state_next = STATE_IDLE;
end
end
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
end
always_ff @(posedge clk) begin
state_reg <= state_next;
frame_reg <= frame_next;
frame_error_reg <= frame_error_next;
frame_oversize_reg <= frame_oversize_next;
frame_min_count_reg <= frame_min_count_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
ifg_cnt_reg <= ifg_cnt_next;
ifg_count_reg <= ifg_count_next;
deficit_idle_count_reg <= deficit_idle_count_next;
s_tdata_reg <= s_tdata_next;
s_empty_reg <= s_empty_next;
s_axis_tx_tready_reg <= s_axis_tx_tready_next;
m_axis_tx_cpl_ts_reg <= m_axis_tx_cpl_ts_next;
m_axis_tx_cpl_tag_reg <= m_axis_tx_cpl_tag_next;
m_axis_tx_cpl_valid_reg <= m_axis_tx_cpl_valid_next;
for (integer i = 0; i < 3; i = i + 1) begin
crc_state_reg[i] <= crc_state_next[i];
end
if (update_crc) begin
crc_state_reg[3] <= crc_state_next[3];
end
if (reset_crc) begin
crc_state_reg[3] <= '1;
end
xgmii_txd_reg <= xgmii_txd_next;
xgmii_txc_reg <= xgmii_txc_next;
start_packet_reg <= start_packet_next;
stat_tx_byte_reg <= stat_tx_byte_next;
stat_tx_pkt_len_reg <= stat_tx_pkt_len_next;
stat_tx_pkt_ucast_reg <= stat_tx_pkt_ucast_next;
stat_tx_pkt_mcast_reg <= stat_tx_pkt_mcast_next;
stat_tx_pkt_bcast_reg <= stat_tx_pkt_bcast_next;
stat_tx_pkt_vlan_reg <= stat_tx_pkt_vlan_next;
stat_tx_pkt_good_reg <= stat_tx_pkt_good_next;
stat_tx_pkt_bad_reg <= stat_tx_pkt_bad_next;
stat_tx_err_oversize_reg <= stat_tx_err_oversize_next;
stat_tx_err_user_reg <= stat_tx_err_user_next;
stat_tx_err_underflow_reg <= stat_tx_err_underflow_next;
if (rst) begin
state_reg <= STATE_IDLE;
frame_reg <= 1'b0;
ifg_count_reg <= 8'd0;
deficit_idle_count_reg <= 2'd0;
s_axis_tx_tready_reg <= 1'b0;
m_axis_tx_cpl_valid_reg <= 1'b0;
xgmii_txd_reg <= {CTRL_W{XGMII_IDLE}};
xgmii_txc_reg <= {CTRL_W{1'b1}};
start_packet_reg <= 1'b0;
stat_tx_byte_reg <= '0;
stat_tx_pkt_len_reg <= '0;
stat_tx_pkt_ucast_reg <= 1'b0;
stat_tx_pkt_mcast_reg <= 1'b0;
stat_tx_pkt_bcast_reg <= 1'b0;
stat_tx_pkt_vlan_reg <= 1'b0;
stat_tx_pkt_good_reg <= 1'b0;
stat_tx_pkt_bad_reg <= 1'b0;
stat_tx_err_oversize_reg <= 1'b0;
stat_tx_err_user_reg <= 1'b0;
stat_tx_err_underflow_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,798 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* AXI4-Stream XGMII frame transmitter (AXI in, XGMII out)
*/
module taxi_axis_xgmii_tx_64 #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic TX_CPL_CTRL_IN_TUSER = 1'b1
)
(
input wire logic clk,
input wire logic rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* XGMII output
*/
output wire logic [DATA_W-1:0] xgmii_txd,
output wire logic [CTRL_W-1:0] xgmii_txc,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable,
/*
* Status
*/
output wire logic [1:0] tx_start_packet,
output wire logic [3:0] stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow
);
// extract parameters
localparam KEEP_W = DATA_W/8;
localparam USER_W = TX_CPL_CTRL_IN_TUSER ? 2 : 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
localparam EMPTY_W = $clog2(KEEP_W);
localparam MIN_LEN_W = $clog2(MIN_FRAME_LEN-4-CTRL_W+1);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64 (instance %m)");
if (KEEP_W*8 != DATA_W || CTRL_W*8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity (instance %m)");
if (s_axis_tx.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (s_axis_tx.USER_W != USER_W)
$fatal(0, "Error: Interface USER_W parameter mismatch (instance %m)");
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [7:0]
XGMII_IDLE = 8'h07,
XGMII_START = 8'hfb,
XGMII_TERM = 8'hfd,
XGMII_ERROR = 8'hfe;
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_PAYLOAD = 3'd1,
STATE_PAD = 3'd2,
STATE_FCS_1 = 3'd3,
STATE_FCS_2 = 3'd4,
STATE_ERR = 3'd5,
STATE_IFG = 3'd6;
logic [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
logic reset_crc;
logic update_crc;
logic swap_lanes_reg = 1'b0, swap_lanes_next;
logic [31:0] swap_txd = 32'd0;
logic [3:0] swap_txc = 4'd0;
logic [DATA_W-1:0] s_tdata_reg = '0, s_tdata_next;
logic [EMPTY_W-1:0] s_empty_reg = '0, s_empty_next;
logic [DATA_W-1:0] fcs_output_txd_0;
logic [DATA_W-1:0] fcs_output_txd_1;
logic [CTRL_W-1:0] fcs_output_txc_0;
logic [CTRL_W-1:0] fcs_output_txc_1;
logic [7:0] ifg_offset;
logic frame_start_reg = 1'b0, frame_start_next;
logic frame_reg = 1'b0, frame_next;
logic frame_error_reg = 1'b0, frame_error_next;
logic frame_oversize_reg = 1'b0, frame_oversize_next;
logic [MIN_LEN_W-1:0] frame_min_count_reg = '0, frame_min_count_next;
logic [1:0] hdr_ptr_reg = '0, hdr_ptr_next;
logic is_mcast_reg = 1'b0, is_mcast_next;
logic is_bcast_reg = 1'b0, is_bcast_next;
logic is_8021q_reg = 1'b0, is_8021q_next;
logic [15:0] frame_len_reg = '0, frame_len_next;
logic [15:0] frame_len_lim_reg = '0, frame_len_lim_next;
logic [7:0] ifg_cnt_reg = '0, ifg_cnt_next;
logic [7:0] ifg_count_reg = 8'd0, ifg_count_next;
logic [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next;
logic s_axis_tx_tready_reg = 1'b0, s_axis_tx_tready_next;
logic [PTP_TS_W-1:0] m_axis_tx_cpl_ts_reg = '0;
logic [PTP_TS_W-1:0] m_axis_tx_cpl_ts_adj_reg = '0;
logic [TX_TAG_W-1:0] m_axis_tx_cpl_tag_reg = '0;
logic m_axis_tx_cpl_valid_reg = 1'b0;
logic m_axis_tx_cpl_valid_int_reg = 1'b0;
logic m_axis_tx_cpl_ts_borrow_reg = 1'b0;
logic [31:0] crc_state_reg[7:0];
wire [31:0] crc_state_next[7:0];
logic [4+16-1:0] last_ts_reg = '0;
logic [4+16-1:0] ts_inc_reg = '0;
logic [DATA_W-1:0] xgmii_txd_reg = {CTRL_W{XGMII_IDLE}}, xgmii_txd_next;
logic [CTRL_W-1:0] xgmii_txc_reg = {CTRL_W{1'b1}}, xgmii_txc_next;
logic [1:0] start_packet_reg = 2'b00;
logic [3:0] stat_tx_byte_reg = '0, stat_tx_byte_next;
logic [15:0] stat_tx_pkt_len_reg = '0, stat_tx_pkt_len_next;
logic stat_tx_pkt_ucast_reg = 1'b0, stat_tx_pkt_ucast_next;
logic stat_tx_pkt_mcast_reg = 1'b0, stat_tx_pkt_mcast_next;
logic stat_tx_pkt_bcast_reg = 1'b0, stat_tx_pkt_bcast_next;
logic stat_tx_pkt_vlan_reg = 1'b0, stat_tx_pkt_vlan_next;
logic stat_tx_pkt_good_reg = 1'b0, stat_tx_pkt_good_next;
logic stat_tx_pkt_bad_reg = 1'b0, stat_tx_pkt_bad_next;
logic stat_tx_err_oversize_reg = 1'b0, stat_tx_err_oversize_next;
logic stat_tx_err_user_reg = 1'b0, stat_tx_err_user_next;
logic stat_tx_err_underflow_reg = 1'b0, stat_tx_err_underflow_next;
assign s_axis_tx.tready = s_axis_tx_tready_reg;
assign xgmii_txd = xgmii_txd_reg;
assign xgmii_txc = xgmii_txc_reg;
assign m_axis_tx_cpl.tdata = PTP_TS_EN ? ((!PTP_TS_FMT_TOD || m_axis_tx_cpl_ts_borrow_reg) ? m_axis_tx_cpl_ts_reg : m_axis_tx_cpl_ts_adj_reg) : '0;
assign m_axis_tx_cpl.tkeep = 1'b1;
assign m_axis_tx_cpl.tstrb = m_axis_tx_cpl.tkeep;
assign m_axis_tx_cpl.tvalid = m_axis_tx_cpl_valid_reg;
assign m_axis_tx_cpl.tlast = 1'b1;
assign m_axis_tx_cpl.tid = m_axis_tx_cpl_tag_reg;
assign m_axis_tx_cpl.tdest = '0;
assign m_axis_tx_cpl.tuser = '0;
assign tx_start_packet = start_packet_reg;
assign stat_tx_byte = stat_tx_byte_reg;
assign stat_tx_pkt_len = stat_tx_pkt_len_reg;
assign stat_tx_pkt_ucast = stat_tx_pkt_ucast_reg;
assign stat_tx_pkt_mcast = stat_tx_pkt_mcast_reg;
assign stat_tx_pkt_bcast = stat_tx_pkt_bcast_reg;
assign stat_tx_pkt_vlan = stat_tx_pkt_vlan_reg;
assign stat_tx_pkt_good = stat_tx_pkt_good_reg;
assign stat_tx_pkt_bad = stat_tx_pkt_bad_reg;
assign stat_tx_err_oversize = stat_tx_err_oversize_reg;
assign stat_tx_err_user = stat_tx_err_user_reg;
assign stat_tx_err_underflow = stat_tx_err_underflow_reg;
for (genvar n = 0; n < 8; n = n + 1) begin : crc
taxi_lfsr #(
.LFSR_W(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_GALOIS(1),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(8*(n+1))
)
eth_crc (
.data_in(s_tdata_reg[0 +: 8*(n+1)]),
.state_in(crc_state_reg[7]),
.data_out(),
.state_out(crc_state_next[n])
);
end
function [2:0] keep2empty(input [7:0] k);
casez (k)
8'bzzzzzzz0: keep2empty = 3'd7;
8'bzzzzzz01: keep2empty = 3'd7;
8'bzzzzz011: keep2empty = 3'd6;
8'bzzzz0111: keep2empty = 3'd5;
8'bzzz01111: keep2empty = 3'd4;
8'bzz011111: keep2empty = 3'd3;
8'bz0111111: keep2empty = 3'd2;
8'b01111111: keep2empty = 3'd1;
8'b11111111: keep2empty = 3'd0;
endcase
endfunction
// Mask input data
wire [DATA_W-1:0] s_axis_tx_tdata_masked;
for (genvar n = 0; n < CTRL_W; n = n + 1) begin
assign s_axis_tx_tdata_masked[n*8 +: 8] = s_axis_tx.tkeep[n] ? s_axis_tx.tdata[n*8 +: 8] : 8'd0;
end
// FCS cycle calculation
always_comb begin
casez (s_empty_reg)
3'd7: begin
fcs_output_txd_0 = {{2{XGMII_IDLE}}, XGMII_TERM, ~crc_state_next[0][31:0], s_tdata_reg[7:0]};
fcs_output_txd_1 = {8{XGMII_IDLE}};
fcs_output_txc_0 = 8'b11100000;
fcs_output_txc_1 = 8'b11111111;
ifg_offset = 8'd3;
end
3'd6: begin
fcs_output_txd_0 = {XGMII_IDLE, XGMII_TERM, ~crc_state_next[1][31:0], s_tdata_reg[15:0]};
fcs_output_txd_1 = {8{XGMII_IDLE}};
fcs_output_txc_0 = 8'b11000000;
fcs_output_txc_1 = 8'b11111111;
ifg_offset = 8'd2;
end
3'd5: begin
fcs_output_txd_0 = {XGMII_TERM, ~crc_state_next[2][31:0], s_tdata_reg[23:0]};
fcs_output_txd_1 = {8{XGMII_IDLE}};
fcs_output_txc_0 = 8'b10000000;
fcs_output_txc_1 = 8'b11111111;
ifg_offset = 8'd1;
end
3'd4: begin
fcs_output_txd_0 = {~crc_state_next[3][31:0], s_tdata_reg[31:0]};
fcs_output_txd_1 = {{7{XGMII_IDLE}}, XGMII_TERM};
fcs_output_txc_0 = 8'b00000000;
fcs_output_txc_1 = 8'b11111111;
ifg_offset = 8'd8;
end
3'd3: begin
fcs_output_txd_0 = {~crc_state_next[4][23:0], s_tdata_reg[39:0]};
fcs_output_txd_1 = {{6{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[4][31:24]};
fcs_output_txc_0 = 8'b00000000;
fcs_output_txc_1 = 8'b11111110;
ifg_offset = 8'd7;
end
3'd2: begin
fcs_output_txd_0 = {~crc_state_next[5][15:0], s_tdata_reg[47:0]};
fcs_output_txd_1 = {{5{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[5][31:16]};
fcs_output_txc_0 = 8'b00000000;
fcs_output_txc_1 = 8'b11111100;
ifg_offset = 8'd6;
end
3'd1: begin
fcs_output_txd_0 = {~crc_state_next[6][7:0], s_tdata_reg[55:0]};
fcs_output_txd_1 = {{4{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[6][31:8]};
fcs_output_txc_0 = 8'b00000000;
fcs_output_txc_1 = 8'b11111000;
ifg_offset = 8'd5;
end
3'd0: begin
fcs_output_txd_0 = s_tdata_reg;
fcs_output_txd_1 = {{3{XGMII_IDLE}}, XGMII_TERM, ~crc_state_reg[7][31:0]};
fcs_output_txc_0 = 8'b00000000;
fcs_output_txc_1 = 8'b11110000;
ifg_offset = 8'd4;
end
endcase
end
always_comb begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
update_crc = 1'b0;
swap_lanes_next = swap_lanes_reg;
frame_start_next = 1'b0;
frame_next = frame_reg;
frame_error_next = frame_error_reg;
frame_oversize_next = frame_oversize_reg;
frame_min_count_next = frame_min_count_reg;
hdr_ptr_next = hdr_ptr_reg;
is_mcast_next = is_mcast_reg;
is_bcast_next = is_bcast_reg;
is_8021q_next = is_8021q_reg;
frame_len_next = frame_len_reg;
frame_len_lim_next = frame_len_lim_reg;
ifg_cnt_next = ifg_cnt_reg;
ifg_count_next = ifg_count_reg;
deficit_idle_count_next = deficit_idle_count_reg;
s_axis_tx_tready_next = 1'b0;
s_tdata_next = s_tdata_reg;
s_empty_next = s_empty_reg;
// XGMII idle
xgmii_txd_next = {CTRL_W{XGMII_IDLE}};
xgmii_txc_next = {CTRL_W{1'b1}};
stat_tx_byte_next = '0;
stat_tx_pkt_len_next = '0;
stat_tx_pkt_ucast_next = 1'b0;
stat_tx_pkt_mcast_next = 1'b0;
stat_tx_pkt_bcast_next = 1'b0;
stat_tx_pkt_vlan_next = 1'b0;
stat_tx_pkt_good_next = 1'b0;
stat_tx_pkt_bad_next = 1'b0;
stat_tx_err_oversize_next = 1'b0;
stat_tx_err_user_next = 1'b0;
stat_tx_err_underflow_next = 1'b0;
if (s_axis_tx.tvalid && s_axis_tx.tready) begin
frame_next = !s_axis_tx.tlast;
end
// counter for min frame length enforcement
if (frame_min_count_reg > MIN_LEN_W'(CTRL_W)) begin
frame_min_count_next = MIN_LEN_W'(frame_min_count_reg - CTRL_W);
end else begin
frame_min_count_next = 0;
end
// counter to measure frame length
if (&frame_len_reg[15:3] == 0) begin
frame_len_next = frame_len_reg + 16'(CTRL_W);
end else begin
frame_len_next = '1;
end
// counter for max frame length enforcement
if (frame_len_lim_reg[15:3] != 0) begin
frame_len_lim_next = frame_len_lim_reg - 16'(CTRL_W);
end else begin
frame_len_lim_next = '0;
end
// address and ethertype checks
if (&hdr_ptr_reg == 0) begin
hdr_ptr_next = hdr_ptr_reg + 1;
end
case (hdr_ptr_reg)
2'd0: begin
is_mcast_next = s_tdata_reg[0];
is_bcast_next = &s_tdata_reg[47:0];
end
2'd1: is_8021q_next = {s_tdata_reg[39:32], s_tdata_reg[47:40]} == 16'h8100;
default: begin
// do nothing
end
endcase
if (ifg_cnt_reg[7:3] != 0) begin
ifg_cnt_next = ifg_cnt_reg - 8'(CTRL_W);
end else begin
ifg_cnt_next = '0;
end
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_error_next = 1'b0;
frame_min_count_next = MIN_LEN_W'(MIN_FRAME_LEN-4-CTRL_W);
hdr_ptr_next = 0;
frame_len_next = 0;
frame_len_lim_next = cfg_tx_max_pkt_len;
reset_crc = 1'b1;
s_axis_tx_tready_next = cfg_tx_enable;
// XGMII idle
xgmii_txd_next = {CTRL_W{XGMII_IDLE}};
xgmii_txc_next = {CTRL_W{1'b1}};
s_tdata_next = s_axis_tx_tdata_masked;
s_empty_next = keep2empty(s_axis_tx.tkeep);
if (s_axis_tx.tvalid && s_axis_tx.tready) begin
// XGMII start, preamble, and SFD
xgmii_txd_next = {ETH_SFD, {6{ETH_PRE}}, XGMII_START};
xgmii_txc_next = 8'b00000001;
frame_start_next = 1'b1;
s_axis_tx_tready_next = 1'b1;
state_next = STATE_PAYLOAD;
end else begin
swap_lanes_next = 1'b0;
ifg_count_next = 8'd0;
deficit_idle_count_next = 2'd0;
state_next = STATE_IDLE;
end
end
STATE_PAYLOAD: begin
// transfer payload
update_crc = 1'b1;
s_axis_tx_tready_next = 1'b1;
xgmii_txd_next = s_tdata_reg;
xgmii_txc_next = {CTRL_W{1'b0}};
s_tdata_next = s_axis_tx_tdata_masked;
s_empty_next = keep2empty(s_axis_tx.tkeep);
stat_tx_byte_next = 4'(CTRL_W);
if (s_axis_tx.tvalid && s_axis_tx.tlast) begin
frame_oversize_next = frame_len_lim_reg < 16'(8+8+4-keep2empty(s_axis_tx.tkeep));
end else begin
frame_oversize_next = frame_len_lim_reg < 8+8;
end
if (!s_axis_tx.tvalid || s_axis_tx.tlast || frame_oversize_next) begin
s_axis_tx_tready_next = frame_next; // drop frame
frame_error_next = !s_axis_tx.tvalid || s_axis_tx.tuser[0] || frame_oversize_next;
stat_tx_err_user_next = s_axis_tx.tuser[0];
stat_tx_err_underflow_next = !s_axis_tx.tvalid;
if (PADDING_EN && frame_min_count_reg != 0) begin
if (frame_min_count_reg > MIN_LEN_W'(CTRL_W)) begin
s_empty_next = 0;
state_next = STATE_PAD;
end else begin
if (keep2empty(s_axis_tx.tkeep) > 3'(CTRL_W-frame_min_count_reg)) begin
s_empty_next = 3'(CTRL_W-frame_min_count_reg);
end
if (frame_error_next) begin
state_next = STATE_ERR;
end else begin
state_next = STATE_FCS_1;
end
end
end else begin
if (frame_error_next) begin
state_next = STATE_ERR;
end else begin
state_next = STATE_FCS_1;
end
end
end else begin
state_next = STATE_PAYLOAD;
end
end
STATE_PAD: begin
// pad frame to MIN_FRAME_LEN
s_axis_tx_tready_next = frame_next; // drop frame
xgmii_txd_next = s_tdata_reg;
xgmii_txc_next = {CTRL_W{1'b0}};
s_tdata_next = 64'd0;
s_empty_next = 0;
stat_tx_byte_next = 4'(CTRL_W);
update_crc = 1'b1;
if (frame_min_count_reg > MIN_LEN_W'(CTRL_W)) begin
state_next = STATE_PAD;
end else begin
s_empty_next = 3'(CTRL_W-frame_min_count_reg);
if (frame_error_reg) begin
state_next = STATE_ERR;
end else begin
state_next = STATE_FCS_1;
end
end
end
STATE_FCS_1: begin
// last cycle
s_axis_tx_tready_next = frame_next; // drop frame
xgmii_txd_next = fcs_output_txd_0;
xgmii_txc_next = fcs_output_txc_0;
update_crc = 1'b1;
ifg_count_next = (cfg_tx_ifg > 8'd12 ? cfg_tx_ifg : 8'd12) - ifg_offset + (swap_lanes_reg ? 8'd4 : 8'd0) + 8'(deficit_idle_count_reg);
if (s_empty_reg <= 4) begin
stat_tx_byte_next = 4'(CTRL_W);
state_next = STATE_FCS_2;
end else begin
stat_tx_byte_next = 12-s_empty_reg;
frame_len_next = frame_len_reg + 16'(12-s_empty_reg);
stat_tx_pkt_len_next = frame_len_next;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
state_next = STATE_IFG;
end
end
STATE_FCS_2: begin
// last cycle
s_axis_tx_tready_next = frame_next; // drop frame
xgmii_txd_next = fcs_output_txd_1;
xgmii_txc_next = fcs_output_txc_1;
stat_tx_byte_next = 4-s_empty_reg;
frame_len_next = frame_len_reg + 16'(4-s_empty_reg);
stat_tx_pkt_len_next = frame_len_next;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
if (DIC_EN) begin
if (ifg_count_next > 8'd7) begin
state_next = STATE_IFG;
end else begin
if (ifg_count_next >= 8'd4) begin
deficit_idle_count_next = 2'(ifg_count_next - 8'd4);
swap_lanes_next = 1'b1;
end else begin
deficit_idle_count_next = 2'(ifg_count_next);
ifg_count_next = 8'd0;
swap_lanes_next = 1'b0;
end
s_axis_tx_tready_next = cfg_tx_enable;
state_next = STATE_IDLE;
end
end else begin
if (ifg_count_next > 8'd4) begin
state_next = STATE_IFG;
end else begin
s_axis_tx_tready_next = cfg_tx_enable;
swap_lanes_next = ifg_count_next != 0;
state_next = STATE_IDLE;
end
end
end
STATE_ERR: begin
// terminate packet with error
s_axis_tx_tready_next = frame_next; // drop frame
// XGMII error
xgmii_txd_next = {XGMII_TERM, {7{XGMII_ERROR}}};
xgmii_txc_next = {CTRL_W{1'b1}};
ifg_count_next = cfg_tx_ifg > 8'd12 ? cfg_tx_ifg : 8'd12;
stat_tx_pkt_len_next = frame_len_reg;
stat_tx_pkt_good_next = !frame_error_reg;
stat_tx_pkt_bad_next = frame_error_reg;
stat_tx_pkt_ucast_next = !is_mcast_reg;
stat_tx_pkt_mcast_next = is_mcast_reg && !is_bcast_reg;
stat_tx_pkt_bcast_next = is_bcast_reg;
stat_tx_pkt_vlan_next = is_8021q_reg;
stat_tx_err_oversize_next = frame_oversize_reg;
state_next = STATE_IFG;
end
STATE_IFG: begin
// send IFG
s_axis_tx_tready_next = frame_next; // drop frame
// XGMII idle
xgmii_txd_next = {CTRL_W{XGMII_IDLE}};
xgmii_txc_next = {CTRL_W{1'b1}};
if (ifg_count_reg > 8'd8) begin
ifg_count_next = ifg_count_reg - 8'd8;
end else begin
ifg_count_next = 8'd0;
end
if (DIC_EN) begin
if (ifg_count_next > 8'd7 || frame_reg) begin
state_next = STATE_IFG;
end else begin
if (ifg_count_next >= 8'd4) begin
deficit_idle_count_next = 2'(ifg_count_next - 8'd4);
swap_lanes_next = 1'b1;
end else begin
deficit_idle_count_next = 2'(ifg_count_next);
ifg_count_next = 8'd0;
swap_lanes_next = 1'b0;
end
s_axis_tx_tready_next = cfg_tx_enable;
state_next = STATE_IDLE;
end
end else begin
if (ifg_count_next > 8'd4 || frame_reg) begin
state_next = STATE_IFG;
end else begin
s_axis_tx_tready_next = cfg_tx_enable;
swap_lanes_next = ifg_count_next != 0;
state_next = STATE_IDLE;
end
end
end
default: begin
// invalid state, return to idle
state_next = STATE_IDLE;
end
endcase
end
always_ff @(posedge clk) begin
state_reg <= state_next;
swap_lanes_reg <= swap_lanes_next;
frame_start_reg <= frame_start_next;
frame_reg <= frame_next;
frame_error_reg <= frame_error_next;
frame_oversize_reg <= frame_oversize_next;
frame_min_count_reg <= frame_min_count_next;
hdr_ptr_reg <= hdr_ptr_next;
is_mcast_reg <= is_mcast_next;
is_bcast_reg <= is_bcast_next;
is_8021q_reg <= is_8021q_next;
frame_len_reg <= frame_len_next;
frame_len_lim_reg <= frame_len_lim_next;
ifg_cnt_reg <= ifg_cnt_next;
ifg_count_reg <= ifg_count_next;
deficit_idle_count_reg <= deficit_idle_count_next;
s_tdata_reg <= s_tdata_next;
s_empty_reg <= s_empty_next;
s_axis_tx_tready_reg <= s_axis_tx_tready_next;
m_axis_tx_cpl_valid_reg <= 1'b0;
m_axis_tx_cpl_valid_int_reg <= 1'b0;
start_packet_reg <= 2'b00;
stat_tx_byte_reg <= stat_tx_byte_next;
stat_tx_pkt_len_reg <= stat_tx_pkt_len_next;
stat_tx_pkt_ucast_reg <= stat_tx_pkt_ucast_next;
stat_tx_pkt_mcast_reg <= stat_tx_pkt_mcast_next;
stat_tx_pkt_bcast_reg <= stat_tx_pkt_bcast_next;
stat_tx_pkt_vlan_reg <= stat_tx_pkt_vlan_next;
stat_tx_pkt_good_reg <= stat_tx_pkt_good_next;
stat_tx_pkt_bad_reg <= stat_tx_pkt_bad_next;
stat_tx_err_oversize_reg <= stat_tx_err_oversize_next;
stat_tx_err_user_reg <= stat_tx_err_user_next;
stat_tx_err_underflow_reg <= stat_tx_err_underflow_next;
if (PTP_TS_EN && PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_valid_reg <= m_axis_tx_cpl_valid_int_reg;
m_axis_tx_cpl_ts_adj_reg[15:0] <= m_axis_tx_cpl_ts_reg[15:0];
{m_axis_tx_cpl_ts_borrow_reg, m_axis_tx_cpl_ts_adj_reg[45:16]} <= $signed({1'b0, m_axis_tx_cpl_ts_reg[45:16]}) - $signed(31'd1000000000);
m_axis_tx_cpl_ts_adj_reg[47:46] <= 0;
m_axis_tx_cpl_ts_adj_reg[95:48] <= m_axis_tx_cpl_ts_reg[95:48] + 1;
end
if (frame_start_reg) begin
if (swap_lanes_reg) begin
if (PTP_TS_EN) begin
if (PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_ts_reg[45:0] <= ptp_ts[45:0] + 46'(ts_inc_reg >> 1);
m_axis_tx_cpl_ts_reg[95:48] <= ptp_ts[95:48];
end else begin
m_axis_tx_cpl_ts_reg <= ptp_ts + PTP_TS_W'(ts_inc_reg >> 1);
end
end
start_packet_reg <= 2'b10;
end else begin
if (PTP_TS_EN) begin
m_axis_tx_cpl_ts_reg <= ptp_ts;
end
start_packet_reg <= 2'b01;
end
m_axis_tx_cpl_tag_reg <= s_axis_tx.tid;
if (TX_CPL_CTRL_IN_TUSER) begin
if (PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_valid_int_reg <= (s_axis_tx.tuser >> 1) == 0;
end else begin
m_axis_tx_cpl_valid_reg <= (s_axis_tx.tuser >> 1) == 0;
end
end else begin
if (PTP_TS_FMT_TOD) begin
m_axis_tx_cpl_valid_int_reg <= 1'b1;
end else begin
m_axis_tx_cpl_valid_reg <= 1'b1;
end
end
end
for (integer i = 0; i < 7; i = i + 1) begin
crc_state_reg[i] <= crc_state_next[i];
end
if (update_crc) begin
crc_state_reg[7] <= crc_state_next[7];
end
if (reset_crc) begin
crc_state_reg[7] <= '1;
end
swap_txd <= xgmii_txd_next[63:32];
swap_txc <= xgmii_txc_next[7:4];
if (swap_lanes_reg) begin
xgmii_txd_reg <= {xgmii_txd_next[31:0], swap_txd};
xgmii_txc_reg <= {xgmii_txc_next[3:0], swap_txc};
end else begin
xgmii_txd_reg <= xgmii_txd_next;
xgmii_txc_reg <= xgmii_txc_next;
end
last_ts_reg <= (4+16)'(ptp_ts);
ts_inc_reg <= (4+16)'(ptp_ts) - last_ts_reg;
if (rst) begin
state_reg <= STATE_IDLE;
frame_start_reg <= 1'b0;
frame_reg <= 1'b0;
swap_lanes_reg <= 1'b0;
ifg_count_reg <= 8'd0;
deficit_idle_count_reg <= 2'd0;
s_axis_tx_tready_reg <= 1'b0;
m_axis_tx_cpl_valid_reg <= 1'b0;
m_axis_tx_cpl_valid_int_reg <= 1'b0;
xgmii_txd_reg <= {CTRL_W{XGMII_IDLE}};
xgmii_txc_reg <= {CTRL_W{1'b1}};
start_packet_reg <= 2'b00;
stat_tx_byte_reg <= '0;
stat_tx_pkt_len_reg <= '0;
stat_tx_pkt_ucast_reg <= 1'b0;
stat_tx_pkt_mcast_reg <= 1'b0;
stat_tx_pkt_bcast_reg <= 1'b0;
stat_tx_pkt_vlan_reg <= 1'b0;
stat_tx_pkt_good_reg <= 1'b0;
stat_tx_pkt_bad_reg <= 1'b0;
stat_tx_err_oversize_reg <= 1'b0;
stat_tx_err_user_reg <= 1'b0;
stat_tx_err_underflow_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,13 @@
taxi_eth_mac_10g.sv
taxi_axis_xgmii_rx_64.sv
taxi_axis_xgmii_tx_64.sv
taxi_axis_xgmii_rx_32.sv
taxi_axis_xgmii_tx_32.sv
taxi_eth_mac_stats.f
taxi_mac_ctrl_tx.sv
taxi_mac_ctrl_rx.sv
taxi_mac_pause_ctrl_tx.sv
taxi_mac_pause_ctrl_rx.sv
../lib/taxi/src/lfsr/rtl/taxi_lfsr.sv
../lib/taxi/src/axis/rtl/taxi_axis_if.sv
../lib/taxi/src/sync/rtl/taxi_sync_signal.sv

View File

@@ -0,0 +1,850 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet MAC
*/
module taxi_eth_mac_10g #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic PFC_EN = 1'b0,
parameter logic PAUSE_EN = PFC_EN,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = 1,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC"
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* XGMII interface
*/
input wire logic [DATA_W-1:0] xgmii_rxd,
input wire logic [CTRL_W-1:0] xgmii_rxc,
output wire logic [DATA_W-1:0] xgmii_txd,
output wire logic [CTRL_W-1:0] xgmii_txc,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts = '0,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts = '0,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req = 1'b0,
input wire logic tx_lfc_resend = 1'b0,
input wire logic rx_lfc_en = 1'b0,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack = 1'b0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req = '0,
input wire logic tx_pfc_resend = 1'b0,
input wire logic [7:0] rx_pfc_en = '0,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack = '0,
/*
* Pause interface
*/
input wire logic tx_lfc_pause_en = 1'b0,
input wire logic tx_pause_req = 1'b0,
output wire logic tx_pause_ack,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic [1:0] tx_start_packet,
output wire logic [3:0] stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
output wire logic [1:0] rx_start_packet,
output wire logic [3:0] stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop = 1'b0,
output wire logic stat_tx_mcf,
output wire logic stat_rx_mcf,
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused,
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast = 48'h01_80_C2_00_00_01,
input wire logic cfg_mcf_rx_check_eth_dst_mcast = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast = 48'd0,
input wire logic cfg_mcf_rx_check_eth_dst_ucast = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_src = 48'd0,
input wire logic cfg_mcf_rx_check_eth_src = 1'b0,
input wire logic [15:0] cfg_mcf_rx_eth_type = 16'h8808,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc = 16'h0001,
input wire logic cfg_mcf_rx_check_opcode_lfc = 1'b1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc = 16'h0101,
input wire logic cfg_mcf_rx_check_opcode_pfc = 1'b1,
input wire logic cfg_mcf_rx_forward = 1'b0,
input wire logic cfg_mcf_rx_enable = 1'b0,
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0
);
localparam KEEP_W = s_axis_tx.KEEP_W;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
localparam MAC_CTRL_EN = PAUSE_EN || PFC_EN;
localparam TX_USER_W_INT = (MAC_CTRL_EN ? 1 : 0) + TX_USER_W;
// check configuration
if (DATA_W != 32 && DATA_W != 64)
$fatal(0, "Error: Interface width must be 32 or 64 (instance %m)");
if (KEEP_W*8 != DATA_W || CTRL_W*8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity (instance %m)");
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(KEEP_W), .USER_EN(1), .USER_W(TX_USER_W_INT), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(KEEP_W), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
if (DATA_W == 64) begin
taxi_axis_xgmii_rx_64 #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W)
)
axis_xgmii_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* XGMII input
*/
.xgmii_rxd(xgmii_rxd),
.xgmii_rxc(xgmii_rxc),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* PTP
*/
.ptp_ts(rx_ptp_ts),
/*
* Configuration
*/
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
/*
* Status
*/
.rx_start_packet(rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble)
);
taxi_axis_xgmii_tx_64 #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.TX_CPL_CTRL_IN_TUSER(MAC_CTRL_EN)
)
axis_xgmii_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* XGMII output
*/
.xgmii_txd(xgmii_txd),
.xgmii_txc(xgmii_txc),
/*
* PTP
*/
.ptp_ts(tx_ptp_ts),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow)
);
end else if (DATA_W == 32) begin
taxi_axis_xgmii_rx_32 #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W)
)
axis_xgmii_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* XGMII input
*/
.xgmii_rxd(xgmii_rxd),
.xgmii_rxc(xgmii_rxc),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* PTP
*/
.ptp_ts(rx_ptp_ts),
/*
* Configuration
*/
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
/*
* Status
*/
.rx_start_packet(rx_start_packet[0]),
.stat_rx_byte(stat_rx_byte[2:0]),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble)
);
assign rx_start_packet[1] = 1'b0;
assign stat_rx_byte[3] = 1'b0;
taxi_axis_xgmii_tx_32 #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W),
.TX_CPL_CTRL_IN_TUSER(MAC_CTRL_EN)
)
axis_xgmii_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* XGMII output
*/
.xgmii_txd(xgmii_txd),
.xgmii_txc(xgmii_txc),
/*
* PTP
*/
.ptp_ts(tx_ptp_ts),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
/*
* Status
*/
.tx_start_packet(tx_start_packet[0]),
.stat_tx_byte(stat_tx_byte[2:0]),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow)
);
assign tx_start_packet[1] = 1'b0;
assign stat_tx_byte[3] = 1'b0;
end else begin
$fatal(0, "Invalid DATA_W selection (instance %m)");
end
if (STAT_EN) begin : stats
taxi_eth_mac_stats #(
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR),
.INC_W(4)
)
mac_stats_inst (
.rx_clk(rx_clk),
.rx_rst(rx_rst),
.tx_clk(tx_clk),
.tx_rst(tx_rst),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(|tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
.rx_start_packet(|rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(stat_tx_mcf),
.stat_rx_mcf(stat_rx_mcf),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused)
);
end else begin
assign m_axis_stat.tdata = '0;
assign m_axis_stat.tkeep = '0;
assign m_axis_stat.tlast = '0;
assign m_axis_stat.tvalid = '0;
assign m_axis_stat.tid = '0;
assign m_axis_stat.tdest = '0;
assign m_axis_stat.tuser = '0;
end
if (MAC_CTRL_EN) begin : mac_ctrl
localparam MCF_PARAMS_SIZE = PFC_EN ? 18 : 2;
wire tx_mcf_valid;
wire tx_mcf_ready;
wire [47:0] tx_mcf_eth_dst;
wire [47:0] tx_mcf_eth_src;
wire [15:0] tx_mcf_eth_type;
wire [15:0] tx_mcf_opcode;
wire [MCF_PARAMS_SIZE*8-1:0] tx_mcf_params;
wire rx_mcf_valid;
wire [47:0] rx_mcf_eth_dst;
wire [47:0] rx_mcf_eth_src;
wire [15:0] rx_mcf_eth_type;
wire [15:0] rx_mcf_opcode;
wire [MCF_PARAMS_SIZE*8-1:0] rx_mcf_params;
// terminate LFC pause requests from RX internally on TX side
wire tx_pause_req_int;
wire rx_lfc_ack_int;
wire rx_lfc_req_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
rx_lfc_req_sync_inst (
.clk(tx_clk),
.in(rx_lfc_req),
.out(rx_lfc_req_sync)
);
wire tx_pause_ack_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
tx_pause_ack_sync_inst (
.clk(rx_clk),
.in(tx_lfc_pause_en && tx_pause_ack),
.out(tx_pause_ack_sync)
);
assign tx_pause_req_int = tx_pause_req || (tx_lfc_pause_en && rx_lfc_req_sync);
assign rx_lfc_ack_int = rx_lfc_ack || tx_pause_ack_sync;
taxi_mac_ctrl_tx #(
.ID_W(s_axis_tx.ID_W),
.DEST_W(s_axis_tx.DEST_W),
.USER_W(TX_USER_W_INT),
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE)
)
mac_ctrl_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* AXI stream input
*/
.s_axis(s_axis_tx),
/*
* AXI stream output
*/
.m_axis(axis_tx_int),
/*
* MAC control frame interface
*/
.mcf_valid(tx_mcf_valid),
.mcf_ready(tx_mcf_ready),
.mcf_eth_dst(tx_mcf_eth_dst),
.mcf_eth_src(tx_mcf_eth_src),
.mcf_eth_type(tx_mcf_eth_type),
.mcf_opcode(tx_mcf_opcode),
.mcf_params(tx_mcf_params),
.mcf_id('0),
.mcf_dest('0),
.mcf_user(2'b10),
/*
* Pause interface
*/
.tx_pause_req(tx_pause_req_int),
.tx_pause_ack(tx_pause_ack),
/*
* Status
*/
.stat_tx_mcf(stat_tx_mcf)
);
taxi_mac_ctrl_rx #(
.USER_W(RX_USER_W),
.USE_READY(0),
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE)
)
mac_ctrl_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* AXI stream input
*/
.s_axis(axis_rx_int),
/*
* AXI stream output
*/
.m_axis(m_axis_rx),
/*
* MAC control frame interface
*/
.mcf_valid(rx_mcf_valid),
.mcf_eth_dst(rx_mcf_eth_dst),
.mcf_eth_src(rx_mcf_eth_src),
.mcf_eth_type(rx_mcf_eth_type),
.mcf_opcode(rx_mcf_opcode),
.mcf_params(rx_mcf_params),
.mcf_id(),
.mcf_dest(),
.mcf_user(),
/*
* Configuration
*/
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc && PFC_EN),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable),
/*
* Status
*/
.stat_rx_mcf(stat_rx_mcf)
);
taxi_mac_pause_ctrl_tx #(
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE),
.PFC_EN(PFC_EN)
)
mac_pause_ctrl_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* MAC control frame interface
*/
.mcf_valid(tx_mcf_valid),
.mcf_ready(tx_mcf_ready),
.mcf_eth_dst(tx_mcf_eth_dst),
.mcf_eth_src(tx_mcf_eth_src),
.mcf_eth_type(tx_mcf_eth_type),
.mcf_opcode(tx_mcf_opcode),
.mcf_params(tx_mcf_params),
/*
* Pause (IEEE 802.3 annex 31B)
*/
.tx_lfc_req(tx_lfc_req),
.tx_lfc_resend(tx_lfc_resend),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D)
*/
.tx_pfc_req(tx_pfc_req),
.tx_pfc_resend(tx_pfc_resend),
/*
* Configuration
*/
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode),
.cfg_tx_lfc_en(cfg_tx_lfc_en),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode),
.cfg_tx_pfc_en(cfg_tx_pfc_en),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh),
.cfg_quanta_step(10'((DATA_W*256)/512)),
.cfg_quanta_clk_en(1'b1),
/*
* Status
*/
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused)
);
taxi_mac_pause_ctrl_rx #(
.MCF_PARAMS_SIZE(18),
.PFC_EN(PFC_EN)
)
mac_pause_ctrl_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* MAC control frame interface
*/
.mcf_valid(rx_mcf_valid),
.mcf_eth_dst(rx_mcf_eth_dst),
.mcf_eth_src(rx_mcf_eth_src),
.mcf_eth_type(rx_mcf_eth_type),
.mcf_opcode(rx_mcf_opcode),
.mcf_params(rx_mcf_params),
/*
* Pause (IEEE 802.3 annex 31B)
*/
.rx_lfc_en(rx_lfc_en),
.rx_lfc_req(rx_lfc_req),
.rx_lfc_ack(rx_lfc_ack_int),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D)
*/
.rx_pfc_en(rx_pfc_en),
.rx_pfc_req(rx_pfc_req),
.rx_pfc_ack(rx_pfc_ack),
/*
* Configuration
*/
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode),
.cfg_rx_lfc_en(cfg_rx_lfc_en),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode),
.cfg_rx_pfc_en(cfg_rx_pfc_en),
.cfg_quanta_step(10'((DATA_W*256)/512)),
.cfg_quanta_clk_en(1'b1),
/*
* Status
*/
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused)
);
end else begin
assign axis_tx_int.tdata = s_axis_tx.tdata;
assign axis_tx_int.tkeep = s_axis_tx.tkeep;
assign axis_tx_int.tvalid = s_axis_tx.tvalid;
assign s_axis_tx.tready = axis_tx_int.tready;
assign axis_tx_int.tlast = s_axis_tx.tlast;
assign axis_tx_int.tid = s_axis_tx.tid;
assign axis_tx_int.tdest = s_axis_tx.tdest;
assign axis_tx_int.tuser = s_axis_tx.tuser;
assign m_axis_rx.tdata = axis_rx_int.tdata;
assign m_axis_rx.tkeep = axis_rx_int.tkeep;
assign m_axis_rx.tvalid = axis_rx_int.tvalid;
assign m_axis_rx.tlast = axis_rx_int.tlast;
assign m_axis_rx.tid = axis_rx_int.tid;
assign m_axis_rx.tdest = axis_rx_int.tdest;
assign m_axis_rx.tuser = axis_rx_int.tuser;
assign rx_lfc_req = '0;
assign rx_pfc_req = '0;
assign tx_pause_ack = '0;
assign stat_tx_mcf = '0;
assign stat_rx_mcf = '0;
assign stat_tx_lfc_pkt = '0;
assign stat_tx_lfc_xon = '0;
assign stat_tx_lfc_xoff = '0;
assign stat_tx_lfc_paused = '0;
assign stat_tx_pfc_pkt = '0;
assign stat_tx_pfc_xon = '0;
assign stat_tx_pfc_xoff = '0;
assign stat_tx_pfc_paused = '0;
assign stat_rx_lfc_pkt = '0;
assign stat_rx_lfc_xon = '0;
assign stat_rx_lfc_xoff = '0;
assign stat_rx_lfc_paused = '0;
assign stat_rx_pfc_pkt = '0;
assign stat_rx_pfc_xon = '0;
assign stat_rx_pfc_xoff = '0;
assign stat_rx_pfc_paused = '0;
end
endmodule
`resetall

View File

@@ -0,0 +1,4 @@
taxi_eth_mac_10g_fifo.sv
taxi_eth_mac_10g.f
../lib/taxi/src/ptp/rtl/taxi_ptp_clock_cdc.sv
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo_adapter.f

View File

@@ -0,0 +1,556 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet MAC with TX and RX FIFOs
*/
module taxi_eth_mac_10g_fifo #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = 1,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter TX_FIFO_DEPTH = 4096,
parameter TX_FIFO_RAM_PIPELINE = 1,
parameter logic TX_FRAME_FIFO = 1'b1,
parameter logic TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO,
parameter logic TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME,
parameter logic TX_DROP_WHEN_FULL = 1'b0,
parameter TX_CPL_FIFO_DEPTH = 64,
parameter RX_FIFO_DEPTH = 4096,
parameter RX_FIFO_RAM_PIPELINE = 1,
parameter logic RX_FRAME_FIFO = 1'b1,
parameter logic RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO,
parameter logic RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME,
parameter logic RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
input wire logic logic_clk,
input wire logic logic_rst,
input wire logic ptp_sample_clk,
/*
* AXI4-Stream input (sink)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* AXI4-Stream output (source)
*/
taxi_axis_if.src m_axis_rx,
/*
* XGMII interface
*/
input wire logic [DATA_W-1:0] xgmii_rxd,
input wire logic [CTRL_W-1:0] xgmii_rxc,
output wire logic [DATA_W-1:0] xgmii_txd,
output wire logic [CTRL_W-1:0] xgmii_txc,
/*
* PTP clock
*/
input wire logic [PTP_TS_W-1:0] ptp_ts = '0,
input wire logic ptp_ts_step = 1'b0,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_error_underflow,
output wire logic tx_fifo_overflow,
output wire logic tx_fifo_bad_frame,
output wire logic tx_fifo_good_frame,
output wire logic rx_error_bad_frame,
output wire logic rx_error_bad_fcs,
output wire logic rx_fifo_overflow,
output wire logic rx_fifo_bad_frame,
output wire logic rx_fifo_good_frame,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1
);
localparam KEEP_W = DATA_W/8;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(KEEP_W), .USER_EN(1), .USER_W(TX_USER_W), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_cpl_int();
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(KEEP_W), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
wire [PTP_TS_W-1:0] tx_ptp_ts;
wire [PTP_TS_W-1:0] rx_ptp_ts;
wire tx_ptp_locked;
wire rx_ptp_locked;
// synchronize MAC status signals into logic clock domain
wire tx_error_underflow_int;
logic [0:0] tx_sync_reg_1 = '0;
logic [0:0] tx_sync_reg_2 = '0;
logic [0:0] tx_sync_reg_3 = '0;
logic [0:0] tx_sync_reg_4 = '0;
assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
always_ff @(posedge tx_clk or posedge tx_rst) begin
if (tx_rst) begin
tx_sync_reg_1 <= '0;
end else begin
tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
tx_sync_reg_2 <= '0;
tx_sync_reg_3 <= '0;
tx_sync_reg_4 <= '0;
end else begin
tx_sync_reg_2 <= tx_sync_reg_1;
tx_sync_reg_3 <= tx_sync_reg_2;
tx_sync_reg_4 <= tx_sync_reg_3;
end
end
wire rx_error_bad_frame_int;
wire rx_error_bad_fcs_int;
logic [1:0] rx_sync_reg_1 = '0;
logic [1:0] rx_sync_reg_2 = '0;
logic [1:0] rx_sync_reg_3 = '0;
logic [1:0] rx_sync_reg_4 = '0;
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
always_ff @(posedge rx_clk or posedge rx_rst) begin
if (rx_rst) begin
rx_sync_reg_1 <= '0;
end else begin
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_fcs_int, rx_error_bad_frame_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
rx_sync_reg_2 <= '0;
rx_sync_reg_3 <= '0;
rx_sync_reg_4 <= '0;
end else begin
rx_sync_reg_2 <= rx_sync_reg_1;
rx_sync_reg_3 <= rx_sync_reg_2;
rx_sync_reg_4 <= rx_sync_reg_3;
end
end
// PTP timestamping
if (PTP_TS_EN) begin : ptp
taxi_ptp_clock_cdc #(
.TS_W(PTP_TS_W),
.NS_W(6)
)
tx_ptp_cdc (
.input_clk(logic_clk),
.input_rst(logic_rst),
.output_clk(tx_clk),
.output_rst(tx_rst),
.sample_clk(ptp_sample_clk),
.input_ts(ptp_ts),
.input_ts_step(ptp_ts_step),
.output_ts(tx_ptp_ts),
.output_ts_step(),
.output_pps(),
.output_pps_str(),
.locked(tx_ptp_locked)
);
taxi_ptp_clock_cdc #(
.TS_W(PTP_TS_W),
.NS_W(6)
)
rx_ptp_cdc (
.input_clk(logic_clk),
.input_rst(logic_rst),
.output_clk(rx_clk),
.output_rst(rx_rst),
.sample_clk(ptp_sample_clk),
.input_ts(ptp_ts),
.input_ts_step(ptp_ts_step),
.output_ts(rx_ptp_ts),
.output_ts_step(),
.output_pps(),
.output_pps_str(),
.locked(rx_ptp_locked)
);
end else begin
assign tx_ptp_ts = '0;
assign rx_ptp_ts = '0;
assign tx_ptp_locked = 1'b0;
assign rx_ptp_locked = 1'b0;
end
wire stat_rx_fifo_drop;
taxi_eth_mac_10g #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.PFC_EN(1'b0),
.PAUSE_EN(1'b0),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_10g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(axis_tx_cpl_int),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* XGMII interface
*/
.xgmii_rxd(xgmii_rxd),
.xgmii_rxc(xgmii_rxc),
.xgmii_txd(xgmii_txd),
.xgmii_txc(xgmii_txc),
/*
* PTP
*/
.tx_ptp_ts(tx_ptp_ts),
.rx_ptp_ts(rx_ptp_ts),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(0),
.tx_lfc_resend(0),
.rx_lfc_en(0),
.rx_lfc_req(),
.rx_lfc_ack(0),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(0),
.tx_pfc_resend(0),
.rx_pfc_en(0),
.rx_pfc_req(),
.rx_pfc_ack(0),
/*
* Pause interface
*/
.tx_lfc_pause_en(0),
.tx_pause_req(0),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(),
.stat_tx_byte(),
.stat_tx_pkt_len(),
.stat_tx_pkt_ucast(),
.stat_tx_pkt_mcast(),
.stat_tx_pkt_bcast(),
.stat_tx_pkt_vlan(),
.stat_tx_pkt_good(),
.stat_tx_pkt_bad(),
.stat_tx_err_oversize(),
.stat_tx_err_user(),
.stat_tx_err_underflow(tx_error_underflow_int),
.rx_start_packet(),
.stat_rx_byte(),
.stat_rx_pkt_len(),
.stat_rx_pkt_fragment(),
.stat_rx_pkt_jabber(),
.stat_rx_pkt_ucast(),
.stat_rx_pkt_mcast(),
.stat_rx_pkt_bcast(),
.stat_rx_pkt_vlan(),
.stat_rx_pkt_good(),
.stat_rx_pkt_bad(rx_error_bad_frame_int),
.stat_rx_err_oversize(),
.stat_rx_err_bad_fcs(rx_error_bad_fcs_int),
.stat_rx_err_bad_block(),
.stat_rx_err_framing(),
.stat_rx_err_preamble(),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(),
.stat_rx_mcf(),
.stat_tx_lfc_pkt(),
.stat_tx_lfc_xon(),
.stat_tx_lfc_xoff(),
.stat_tx_lfc_paused(),
.stat_tx_pfc_pkt(),
.stat_tx_pfc_xon(),
.stat_tx_pfc_xoff(),
.stat_tx_pfc_paused(),
.stat_rx_lfc_pkt(),
.stat_rx_lfc_xon(),
.stat_rx_lfc_xoff(),
.stat_rx_lfc_paused(),
.stat_rx_pfc_pkt(),
.stat_rx_pfc_xon(),
.stat_rx_pfc_xoff(),
.stat_rx_pfc_paused(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast('0),
.cfg_mcf_rx_check_eth_dst_mcast('0),
.cfg_mcf_rx_eth_dst_ucast('0),
.cfg_mcf_rx_check_eth_dst_ucast('0),
.cfg_mcf_rx_eth_src('0),
.cfg_mcf_rx_check_eth_src('0),
.cfg_mcf_rx_eth_type('0),
.cfg_mcf_rx_opcode_lfc('0),
.cfg_mcf_rx_check_opcode_lfc('0),
.cfg_mcf_rx_opcode_pfc('0),
.cfg_mcf_rx_check_opcode_pfc('0),
.cfg_mcf_rx_forward('0),
.cfg_mcf_rx_enable('0),
.cfg_tx_lfc_eth_dst('0),
.cfg_tx_lfc_eth_src('0),
.cfg_tx_lfc_eth_type('0),
.cfg_tx_lfc_opcode('0),
.cfg_tx_lfc_en('0),
.cfg_tx_lfc_quanta('0),
.cfg_tx_lfc_refresh('0),
.cfg_tx_pfc_eth_dst('0),
.cfg_tx_pfc_eth_src('0),
.cfg_tx_pfc_eth_type('0),
.cfg_tx_pfc_opcode('0),
.cfg_tx_pfc_en('0),
.cfg_tx_pfc_quanta('{8{'0}}),
.cfg_tx_pfc_refresh('{8{'0}}),
.cfg_rx_lfc_opcode('0),
.cfg_rx_lfc_en('0),
.cfg_rx_pfc_opcode('0),
.cfg_rx_pfc_en('0)
);
taxi_axis_async_fifo_adapter #(
.DEPTH(TX_FIFO_DEPTH),
.RAM_PIPELINE(TX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(TX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
)
tx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(logic_clk),
.s_rst(logic_rst),
.s_axis(s_axis_tx),
/*
* AXI4-Stream output (source)
*/
.m_clk(tx_clk),
.m_rst(tx_rst),
.m_axis(axis_tx_int),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(tx_fifo_overflow),
.s_status_bad_frame(tx_fifo_bad_frame),
.s_status_good_frame(tx_fifo_good_frame),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo #(
.DEPTH(TX_CPL_FIFO_DEPTH),
.FRAME_FIFO(1'b0)
)
tx_cpl_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(tx_clk),
.s_rst(tx_rst),
.s_axis(axis_tx_cpl_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_tx_cpl),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo_adapter #(
.DEPTH(RX_FIFO_DEPTH),
.RAM_PIPELINE(RX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(RX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(RX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(RX_DROP_WHEN_FULL)
)
rx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(rx_clk),
.s_rst(rx_rst),
.s_axis(axis_rx_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_rx),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(stat_rx_fifo_drop),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(rx_fifo_overflow),
.m_status_bad_frame(rx_fifo_bad_frame),
.m_status_good_frame(rx_fifo_good_frame)
);
endmodule
`resetall

View File

@@ -0,0 +1,11 @@
taxi_eth_mac_1g.sv
taxi_axis_gmii_rx.sv
taxi_axis_gmii_tx.sv
taxi_eth_mac_stats.f
taxi_mac_ctrl_tx.sv
taxi_mac_ctrl_rx.sv
taxi_mac_pause_ctrl_tx.sv
taxi_mac_pause_ctrl_rx.sv
../lib/taxi/src/lfsr/rtl/taxi_lfsr.sv
../lib/taxi/src/axis/rtl/taxi_axis_if.sv
../lib/taxi/src/sync/rtl/taxi_sync_signal.sv

View File

@@ -0,0 +1,734 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 1G Ethernet MAC
*/
module taxi_eth_mac_1g #
(
parameter DATA_W = 8,
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96,
parameter logic PFC_EN = 1'b0,
parameter logic PAUSE_EN = PFC_EN,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC"
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* GMII interface
*/
input wire logic [DATA_W-1:0] gmii_rxd,
input wire logic gmii_rx_dv,
input wire logic gmii_rx_er,
output wire logic [DATA_W-1:0] gmii_txd,
output wire logic gmii_tx_en,
output wire logic gmii_tx_er,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts = '0,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts = '0,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req = 1'b0,
input wire logic tx_lfc_resend = 1'b0,
input wire logic rx_lfc_en = 1'b0,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack = 1'b0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req = '0,
input wire logic tx_pfc_resend = 1'b0,
input wire logic [7:0] rx_pfc_en = '0,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack = '0,
/*
* Pause interface
*/
input wire logic tx_lfc_pause_en = 1'b0,
input wire logic tx_pause_req = 1'b0,
output wire logic tx_pause_ack,
/*
* Control
*/
input wire logic rx_clk_enable = 1'b1,
input wire logic tx_clk_enable = 1'b1,
input wire logic rx_mii_select = 1'b0,
input wire logic tx_mii_select = 1'b0,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_start_packet,
output wire logic stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
output wire logic rx_start_packet,
output wire logic stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop = 1'b0,
output wire logic stat_tx_mcf,
output wire logic stat_rx_mcf,
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused,
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast = 48'h01_80_C2_00_00_01,
input wire logic cfg_mcf_rx_check_eth_dst_mcast = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast = 48'd0,
input wire logic cfg_mcf_rx_check_eth_dst_ucast = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_src = 48'd0,
input wire logic cfg_mcf_rx_check_eth_src = 1'b0,
input wire logic [15:0] cfg_mcf_rx_eth_type = 16'h8808,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc = 16'h0001,
input wire logic cfg_mcf_rx_check_opcode_lfc = 1'b1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc = 16'h0101,
input wire logic cfg_mcf_rx_check_opcode_pfc = 1'b1,
input wire logic cfg_mcf_rx_forward = 1'b0,
input wire logic cfg_mcf_rx_enable = 1'b0,
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0
);
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
localparam MAC_CTRL_EN = PAUSE_EN || PFC_EN;
localparam TX_USER_W_INT = (MAC_CTRL_EN ? 1 : 0) + TX_USER_W;
taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(TX_USER_W_INT), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
taxi_axis_gmii_rx #(
.DATA_W(DATA_W),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W)
)
axis_gmii_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* GMII input
*/
.gmii_rxd(gmii_rxd),
.gmii_rx_dv(gmii_rx_dv),
.gmii_rx_er(gmii_rx_er),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* PTP
*/
.ptp_ts(rx_ptp_ts),
/*
* Control
*/
.clk_enable(rx_clk_enable),
.mii_select(rx_mii_select),
/*
* Configuration
*/
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
/*
* Status
*/
.rx_start_packet(rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble)
);
taxi_axis_gmii_tx #(
.DATA_W(DATA_W),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W),
.TX_CPL_CTRL_IN_TUSER(MAC_CTRL_EN)
)
axis_gmii_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* GMII output
*/
.gmii_txd(gmii_txd),
.gmii_tx_en(gmii_tx_en),
.gmii_tx_er(gmii_tx_er),
/*
* PTP
*/
.ptp_ts(tx_ptp_ts),
/*
* Control
*/
.clk_enable(tx_clk_enable),
.mii_select(tx_mii_select),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow)
);
if (STAT_EN) begin : stats
taxi_eth_mac_stats #(
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR),
.INC_W(1)
)
mac_stats_inst (
.rx_clk(rx_clk),
.rx_rst(rx_rst),
.tx_clk(tx_clk),
.tx_rst(tx_rst),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
.rx_start_packet(rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(stat_tx_mcf),
.stat_rx_mcf(stat_rx_mcf),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused)
);
end else begin
assign m_axis_stat.tdata = '0;
assign m_axis_stat.tkeep = '0;
assign m_axis_stat.tlast = '0;
assign m_axis_stat.tvalid = '0;
assign m_axis_stat.tid = '0;
assign m_axis_stat.tdest = '0;
assign m_axis_stat.tuser = '0;
end
if (MAC_CTRL_EN) begin : mac_ctrl
localparam MCF_PARAMS_SIZE = PFC_EN ? 18 : 2;
wire tx_mcf_valid;
wire tx_mcf_ready;
wire [47:0] tx_mcf_eth_dst;
wire [47:0] tx_mcf_eth_src;
wire [15:0] tx_mcf_eth_type;
wire [15:0] tx_mcf_opcode;
wire [MCF_PARAMS_SIZE*8-1:0] tx_mcf_params;
wire rx_mcf_valid;
wire [47:0] rx_mcf_eth_dst;
wire [47:0] rx_mcf_eth_src;
wire [15:0] rx_mcf_eth_type;
wire [15:0] rx_mcf_opcode;
wire [MCF_PARAMS_SIZE*8-1:0] rx_mcf_params;
// terminate LFC pause requests from RX internally on TX side
wire tx_pause_req_int;
wire rx_lfc_ack_int;
wire rx_lfc_req_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
rx_lfc_req_sync_inst (
.clk(tx_clk),
.in(rx_lfc_req),
.out(rx_lfc_req_sync)
);
wire tx_pause_ack_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
tx_pause_ack_sync_inst (
.clk(rx_clk),
.in(tx_lfc_pause_en && tx_pause_ack),
.out(tx_pause_ack_sync)
);
assign tx_pause_req_int = tx_pause_req || (tx_lfc_pause_en && rx_lfc_req_sync);
assign rx_lfc_ack_int = rx_lfc_ack || tx_pause_ack_sync;
taxi_mac_ctrl_tx #(
.ID_W(s_axis_tx.ID_W),
.DEST_W(s_axis_tx.DEST_W),
.USER_W(TX_USER_W_INT),
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE)
)
mac_ctrl_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* AXI stream input
*/
.s_axis(s_axis_tx),
/*
* AXI stream output
*/
.m_axis(axis_tx_int),
/*
* MAC control frame interface
*/
.mcf_valid(tx_mcf_valid),
.mcf_ready(tx_mcf_ready),
.mcf_eth_dst(tx_mcf_eth_dst),
.mcf_eth_src(tx_mcf_eth_src),
.mcf_eth_type(tx_mcf_eth_type),
.mcf_opcode(tx_mcf_opcode),
.mcf_params(tx_mcf_params),
.mcf_id('0),
.mcf_dest('0),
.mcf_user(2'b10),
/*
* Pause interface
*/
.tx_pause_req(tx_pause_req_int),
.tx_pause_ack(tx_pause_ack),
/*
* Status
*/
.stat_tx_mcf(stat_tx_mcf)
);
taxi_mac_ctrl_rx #(
.USER_W(RX_USER_W),
.USE_READY(0),
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE)
)
mac_ctrl_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* AXI stream input
*/
.s_axis(axis_rx_int),
/*
* AXI stream output
*/
.m_axis(m_axis_rx),
/*
* MAC control frame interface
*/
.mcf_valid(rx_mcf_valid),
.mcf_eth_dst(rx_mcf_eth_dst),
.mcf_eth_src(rx_mcf_eth_src),
.mcf_eth_type(rx_mcf_eth_type),
.mcf_opcode(rx_mcf_opcode),
.mcf_params(rx_mcf_params),
.mcf_id(),
.mcf_dest(),
.mcf_user(),
/*
* Configuration
*/
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable),
/*
* Status
*/
.stat_rx_mcf(stat_rx_mcf)
);
taxi_mac_pause_ctrl_tx #(
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE),
.PFC_EN(PFC_EN)
)
mac_pause_ctrl_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* MAC control frame interface
*/
.mcf_valid(tx_mcf_valid),
.mcf_ready(tx_mcf_ready),
.mcf_eth_dst(tx_mcf_eth_dst),
.mcf_eth_src(tx_mcf_eth_src),
.mcf_eth_type(tx_mcf_eth_type),
.mcf_opcode(tx_mcf_opcode),
.mcf_params(tx_mcf_params),
/*
* Pause (IEEE 802.3 annex 31B)
*/
.tx_lfc_req(tx_lfc_req),
.tx_lfc_resend(tx_lfc_resend),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D)
*/
.tx_pfc_req(tx_pfc_req),
.tx_pfc_resend(tx_pfc_resend),
/*
* Configuration
*/
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode),
.cfg_tx_lfc_en(cfg_tx_lfc_en),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode),
.cfg_tx_pfc_en(cfg_tx_pfc_en),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh),
.cfg_quanta_step(tx_mii_select ? 10'((4*256)/512) : 10'((8*256)/512)),
.cfg_quanta_clk_en(tx_clk_enable),
/*
* Status
*/
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused)
);
taxi_mac_pause_ctrl_rx #(
.MCF_PARAMS_SIZE(18),
.PFC_EN(PFC_EN)
)
mac_pause_ctrl_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* MAC control frame interface
*/
.mcf_valid(rx_mcf_valid),
.mcf_eth_dst(rx_mcf_eth_dst),
.mcf_eth_src(rx_mcf_eth_src),
.mcf_eth_type(rx_mcf_eth_type),
.mcf_opcode(rx_mcf_opcode),
.mcf_params(rx_mcf_params),
/*
* Pause (IEEE 802.3 annex 31B)
*/
.rx_lfc_en(rx_lfc_en),
.rx_lfc_req(rx_lfc_req),
.rx_lfc_ack(rx_lfc_ack_int),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D)
*/
.rx_pfc_en(rx_pfc_en),
.rx_pfc_req(rx_pfc_req),
.rx_pfc_ack(rx_pfc_ack),
/*
* Configuration
*/
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode),
.cfg_rx_lfc_en(cfg_rx_lfc_en),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode),
.cfg_rx_pfc_en(cfg_rx_pfc_en),
.cfg_quanta_step(rx_mii_select ? 10'((4*256)/512) : 10'((8*256)/512)),
.cfg_quanta_clk_en(rx_clk_enable),
/*
* Status
*/
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused)
);
end else begin
assign axis_tx_int.tdata = s_axis_tx.tdata;
assign axis_tx_int.tkeep = s_axis_tx.tkeep;
assign axis_tx_int.tvalid = s_axis_tx.tvalid;
assign s_axis_tx.tready = axis_tx_int.tready;
assign axis_tx_int.tlast = s_axis_tx.tlast;
assign axis_tx_int.tid = s_axis_tx.tid;
assign axis_tx_int.tdest = s_axis_tx.tdest;
assign axis_tx_int.tuser = s_axis_tx.tuser;
assign m_axis_rx.tdata = axis_rx_int.tdata;
assign m_axis_rx.tkeep = axis_rx_int.tkeep;
assign m_axis_rx.tvalid = axis_rx_int.tvalid;
assign m_axis_rx.tlast = axis_rx_int.tlast;
assign m_axis_rx.tid = axis_rx_int.tid;
assign m_axis_rx.tdest = axis_rx_int.tdest;
assign m_axis_rx.tuser = axis_rx_int.tuser;
assign rx_lfc_req = '0;
assign rx_pfc_req = '0;
assign tx_pause_ack = '0;
assign stat_tx_mcf = '0;
assign stat_rx_mcf = '0;
assign stat_tx_lfc_pkt = '0;
assign stat_tx_lfc_xon = '0;
assign stat_tx_lfc_xoff = '0;
assign stat_tx_lfc_paused = '0;
assign stat_tx_pfc_pkt = '0;
assign stat_tx_pfc_xon = '0;
assign stat_tx_pfc_xoff = '0;
assign stat_tx_pfc_paused = '0;
assign stat_rx_lfc_pkt = '0;
assign stat_rx_lfc_xon = '0;
assign stat_rx_lfc_xoff = '0;
assign stat_rx_lfc_paused = '0;
assign stat_rx_pfc_pkt = '0;
assign stat_rx_pfc_xon = '0;
assign stat_rx_pfc_xoff = '0;
assign stat_rx_pfc_paused = '0;
end
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_mac_1g_fifo.sv
taxi_eth_mac_1g.f
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo_adapter.f

View File

@@ -0,0 +1,506 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 1G Ethernet MAC with TX and RX FIFOs
*/
module taxi_eth_mac_1g_fifo #
(
parameter DATA_W = 8,
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter TX_FIFO_DEPTH = 4096,
parameter TX_FIFO_RAM_PIPELINE = 1,
parameter logic TX_FRAME_FIFO = 1'b1,
parameter logic TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO,
parameter logic TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME,
parameter logic TX_DROP_WHEN_FULL = 1'b0,
parameter TX_CPL_FIFO_DEPTH = 64,
parameter RX_FIFO_DEPTH = 4096,
parameter RX_FIFO_RAM_PIPELINE = 1,
parameter logic RX_FRAME_FIFO = 1'b1,
parameter logic RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO,
parameter logic RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME,
parameter logic RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
input wire logic logic_clk,
input wire logic logic_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* GMII interface
*/
input wire logic [DATA_W-1:0] gmii_rxd,
input wire logic gmii_rx_dv,
input wire logic gmii_rx_er,
output wire logic [DATA_W-1:0] gmii_txd,
output wire logic gmii_tx_en,
output wire logic gmii_tx_er,
/*
* Control
*/
input wire logic rx_clk_enable = 1'b1,
input wire logic tx_clk_enable = 1'b1,
input wire logic rx_mii_select = 1'b0,
input wire logic tx_mii_select = 1'b0,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_error_underflow,
output wire logic tx_fifo_overflow,
output wire logic tx_fifo_bad_frame,
output wire logic tx_fifo_good_frame,
output wire logic rx_error_bad_frame,
output wire logic rx_error_bad_fcs,
output wire logic rx_fifo_overflow,
output wire logic rx_fifo_bad_frame,
output wire logic rx_fifo_good_frame,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1
);
localparam PTP_TS_EN = 1'b0;
localparam PTP_TS_W = 96;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(1), .USER_EN(1), .USER_W(TX_USER_W), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_cpl_int();
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(1), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
// synchronize MAC status signals into logic clock domain
wire tx_error_underflow_int;
logic [0:0] tx_sync_reg_1 = '0;
logic [0:0] tx_sync_reg_2 = '0;
logic [0:0] tx_sync_reg_3 = '0;
logic [0:0] tx_sync_reg_4 = '0;
assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
always_ff @(posedge tx_clk or posedge tx_rst) begin
if (tx_rst) begin
tx_sync_reg_1 <= '0;
end else begin
tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
tx_sync_reg_2 <= '0;
tx_sync_reg_3 <= '0;
tx_sync_reg_4 <= '0;
end else begin
tx_sync_reg_2 <= tx_sync_reg_1;
tx_sync_reg_3 <= tx_sync_reg_2;
tx_sync_reg_4 <= tx_sync_reg_3;
end
end
wire rx_error_bad_frame_int;
wire rx_error_bad_fcs_int;
logic [1:0] rx_sync_reg_1 = '0;
logic [1:0] rx_sync_reg_2 = '0;
logic [1:0] rx_sync_reg_3 = '0;
logic [1:0] rx_sync_reg_4 = '0;
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
always_ff @(posedge rx_clk or posedge rx_rst) begin
if (rx_rst) begin
rx_sync_reg_1 <= '0;
end else begin
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_fcs_int, rx_error_bad_frame_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
rx_sync_reg_2 <= '0;
rx_sync_reg_3 <= '0;
rx_sync_reg_4 <= '0;
end else begin
rx_sync_reg_2 <= rx_sync_reg_1;
rx_sync_reg_3 <= rx_sync_reg_2;
rx_sync_reg_4 <= rx_sync_reg_3;
end
end
wire stat_rx_fifo_drop;
taxi_eth_mac_1g #(
.DATA_W(DATA_W),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(1'b0),
.PFC_EN(1'b0),
.PAUSE_EN(1'b0),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_1g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(axis_tx_cpl_int),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* GMII interface
*/
.gmii_rxd(gmii_rxd),
.gmii_rx_dv(gmii_rx_dv),
.gmii_rx_er(gmii_rx_er),
.gmii_txd(gmii_txd),
.gmii_tx_en(gmii_tx_en),
.gmii_tx_er(gmii_tx_er),
/*
* PTP
*/
.tx_ptp_ts(0),
.rx_ptp_ts(0),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(0),
.tx_lfc_resend(0),
.rx_lfc_en(0),
.rx_lfc_req(),
.rx_lfc_ack(0),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(0),
.tx_pfc_resend(0),
.rx_pfc_en(0),
.rx_pfc_req(),
.rx_pfc_ack(0),
/*
* Pause interface
*/
.tx_lfc_pause_en(0),
.tx_pause_req(0),
.tx_pause_ack(),
/*
* Control
*/
.rx_clk_enable(rx_clk_enable),
.tx_clk_enable(tx_clk_enable),
.rx_mii_select(rx_mii_select),
.tx_mii_select(tx_mii_select),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
// .rx_error_bad_frame(rx_error_bad_frame_int),
.tx_start_packet(),
.stat_tx_byte(),
.stat_tx_pkt_len(),
.stat_tx_pkt_ucast(),
.stat_tx_pkt_mcast(),
.stat_tx_pkt_bcast(),
.stat_tx_pkt_vlan(),
.stat_tx_pkt_good(),
.stat_tx_pkt_bad(),
.stat_tx_err_oversize(),
.stat_tx_err_user(),
.stat_tx_err_underflow(tx_error_underflow_int),
.rx_start_packet(),
.stat_rx_byte(),
.stat_rx_pkt_len(),
.stat_rx_pkt_fragment(),
.stat_rx_pkt_jabber(),
.stat_rx_pkt_ucast(),
.stat_rx_pkt_mcast(),
.stat_rx_pkt_bcast(),
.stat_rx_pkt_vlan(),
.stat_rx_pkt_good(),
.stat_rx_pkt_bad(),
.stat_rx_err_oversize(),
.stat_rx_err_bad_fcs(rx_error_bad_fcs_int),
.stat_rx_err_bad_block(),
.stat_rx_err_framing(),
.stat_rx_err_preamble(),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(),
.stat_rx_mcf(),
.stat_tx_lfc_pkt(),
.stat_tx_lfc_xon(),
.stat_tx_lfc_xoff(),
.stat_tx_lfc_paused(),
.stat_tx_pfc_pkt(),
.stat_tx_pfc_xon(),
.stat_tx_pfc_xoff(),
.stat_tx_pfc_paused(),
.stat_rx_lfc_pkt(),
.stat_rx_lfc_xon(),
.stat_rx_lfc_xoff(),
.stat_rx_lfc_paused(),
.stat_rx_pfc_pkt(),
.stat_rx_pfc_xon(),
.stat_rx_pfc_xoff(),
.stat_rx_pfc_paused(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast('0),
.cfg_mcf_rx_check_eth_dst_mcast('0),
.cfg_mcf_rx_eth_dst_ucast('0),
.cfg_mcf_rx_check_eth_dst_ucast('0),
.cfg_mcf_rx_eth_src('0),
.cfg_mcf_rx_check_eth_src('0),
.cfg_mcf_rx_eth_type('0),
.cfg_mcf_rx_opcode_lfc('0),
.cfg_mcf_rx_check_opcode_lfc('0),
.cfg_mcf_rx_opcode_pfc('0),
.cfg_mcf_rx_check_opcode_pfc('0),
.cfg_mcf_rx_forward('0),
.cfg_mcf_rx_enable('0),
.cfg_tx_lfc_eth_dst('0),
.cfg_tx_lfc_eth_src('0),
.cfg_tx_lfc_eth_type('0),
.cfg_tx_lfc_opcode('0),
.cfg_tx_lfc_en('0),
.cfg_tx_lfc_quanta('0),
.cfg_tx_lfc_refresh('0),
.cfg_tx_pfc_eth_dst('0),
.cfg_tx_pfc_eth_src('0),
.cfg_tx_pfc_eth_type('0),
.cfg_tx_pfc_opcode('0),
.cfg_tx_pfc_en('0),
.cfg_tx_pfc_quanta('{8{'0}}),
.cfg_tx_pfc_refresh('{8{'0}}),
.cfg_rx_lfc_opcode('0),
.cfg_rx_lfc_en('0),
.cfg_rx_pfc_opcode('0),
.cfg_rx_pfc_en('0)
);
taxi_axis_async_fifo_adapter #(
.DEPTH(TX_FIFO_DEPTH),
.RAM_PIPELINE(TX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(TX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
)
tx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(logic_clk),
.s_rst(logic_rst),
.s_axis(s_axis_tx),
/*
* AXI4-Stream output (source)
*/
.m_clk(tx_clk),
.m_rst(tx_rst),
.m_axis(axis_tx_int),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(tx_fifo_overflow),
.s_status_bad_frame(tx_fifo_bad_frame),
.s_status_good_frame(tx_fifo_good_frame),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo #(
.DEPTH(TX_CPL_FIFO_DEPTH),
.FRAME_FIFO(1'b0)
)
tx_cpl_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(tx_clk),
.s_rst(tx_rst),
.s_axis(axis_tx_cpl_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_tx_cpl),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo_adapter #(
.DEPTH(RX_FIFO_DEPTH),
.RAM_PIPELINE(RX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(RX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(RX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(RX_DROP_WHEN_FULL)
)
rx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(rx_clk),
.s_rst(rx_rst),
.s_axis(axis_rx_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_rx),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(stat_rx_fifo_drop),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(rx_fifo_overflow),
.m_status_bad_frame(rx_fifo_bad_frame),
.m_status_good_frame(rx_fifo_good_frame)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_mac_1g_gmii.sv
taxi_eth_mac_1g.f
taxi_gmii_phy_if.f

View File

@@ -0,0 +1,520 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 1G Ethernet MAC with GMII interface
*/
module taxi_eth_mac_1g_gmii #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtex7",
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96,
parameter logic PFC_EN = 1'b0,
parameter logic PAUSE_EN = PFC_EN,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC"
)
(
input wire logic gtx_clk,
input wire logic gtx_rst,
output wire logic rx_clk,
output wire logic rx_rst,
output wire logic tx_clk,
output wire logic tx_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* GMII interface
*/
input wire logic gmii_rx_clk,
input wire logic [7:0] gmii_rxd,
input wire logic gmii_rx_dv,
input wire logic gmii_rx_er,
input wire logic mii_tx_clk,
output wire logic gmii_tx_clk,
output wire logic [7:0] gmii_txd,
output wire logic gmii_tx_en,
output wire logic gmii_tx_er,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts = '0,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts = '0,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req = 1'b0,
input wire logic tx_lfc_resend = 1'b0,
input wire logic rx_lfc_en = 1'b0,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack = 1'b0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req = '0,
input wire logic tx_pfc_resend = 1'b0,
input wire logic [7:0] rx_pfc_en = '0,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack = '0,
/*
* Pause interface
*/
input wire logic tx_lfc_pause_en = 1'b0,
input wire logic tx_pause_req = 1'b0,
output wire logic tx_pause_ack,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_start_packet,
output wire logic stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
output wire logic rx_start_packet,
output wire logic stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop = 1'b0,
output wire logic [1:0] link_speed,
output wire logic stat_tx_mcf,
output wire logic stat_rx_mcf,
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused,
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast = 48'h01_80_C2_00_00_01,
input wire logic cfg_mcf_rx_check_eth_dst_mcast = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast = 48'd0,
input wire logic cfg_mcf_rx_check_eth_dst_ucast = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_src = 48'd0,
input wire logic cfg_mcf_rx_check_eth_src = 1'b0,
input wire logic [15:0] cfg_mcf_rx_eth_type = 16'h8808,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc = 16'h0001,
input wire logic cfg_mcf_rx_check_opcode_lfc = 1'b1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc = 16'h0101,
input wire logic cfg_mcf_rx_check_opcode_pfc = 1'b1,
input wire logic cfg_mcf_rx_forward = 1'b0,
input wire logic cfg_mcf_rx_enable = 1'b0,
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0
);
reg [1:0] link_speed_reg = 2'b10;
reg mii_select_reg = 1'b0;
wire tx_mii_select_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
tx_mii_select_sync_inst (
.clk(tx_clk),
.in(mii_select_reg),
.out(tx_mii_select_sync)
);
wire rx_mii_select_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
rx_mii_select_sync_inst (
.clk(rx_clk),
.in(mii_select_reg),
.out(rx_mii_select_sync)
);
// PHY speed detection
reg [2:0] rx_prescale = 3'd0;
always_ff @(posedge rx_clk) begin
rx_prescale <= rx_prescale + 3'd1;
end
wire rx_prescale_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
rx_prescale_sync_inst (
.clk(gtx_clk),
.in(rx_prescale[2]),
.out(rx_prescale_sync)
);
reg [6:0] rx_speed_count_1 = 0;
reg [1:0] rx_speed_count_2 = 0;
reg rx_prescale_sync_last_reg = 1'b0;
always_ff @(posedge gtx_clk) begin
rx_prescale_sync_last_reg <= rx_prescale_sync;
rx_speed_count_1 <= rx_speed_count_1 + 1;
if (rx_prescale_sync ^ rx_prescale_sync_last_reg) begin
rx_speed_count_2 <= rx_speed_count_2 + 1;
end
if (&rx_speed_count_1) begin
// reference count overflow - 10M
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
link_speed_reg <= 2'b00;
mii_select_reg <= 1'b1;
end
if (&rx_speed_count_2) begin
// prescaled count overflow - 100M or 1000M
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
if (rx_speed_count_1[6:5] != 0) begin
// large reference count - 100M
link_speed_reg <= 2'b01;
mii_select_reg <= 1'b1;
end else begin
// small reference count - 1000M
link_speed_reg <= 2'b10;
mii_select_reg <= 1'b0;
end
end
if (gtx_rst) begin
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
link_speed_reg <= 2'b10;
mii_select_reg <= 1'b0;
end
end
assign link_speed = link_speed_reg;
wire [7:0] mac_gmii_rxd;
wire mac_gmii_rx_dv;
wire mac_gmii_rx_er;
wire [7:0] mac_gmii_txd;
wire mac_gmii_tx_en;
wire mac_gmii_tx_er;
taxi_gmii_phy_if #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY)
)
gmii_phy_if_inst (
.gtx_clk(gtx_clk),
.gtx_rst(gtx_rst),
/*
* GMII interface to MAC
*/
.mac_gmii_rx_clk(rx_clk),
.mac_gmii_rx_rst(rx_rst),
.mac_gmii_rxd(mac_gmii_rxd),
.mac_gmii_rx_dv(mac_gmii_rx_dv),
.mac_gmii_rx_er(mac_gmii_rx_er),
.mac_gmii_tx_clk(tx_clk),
.mac_gmii_tx_rst(tx_rst),
.mac_gmii_txd(mac_gmii_txd),
.mac_gmii_tx_en(mac_gmii_tx_en),
.mac_gmii_tx_er(mac_gmii_tx_er),
/*
* GMII interface to PHY
*/
.phy_gmii_rx_clk(gmii_rx_clk),
.phy_gmii_rxd(gmii_rxd),
.phy_gmii_rx_dv(gmii_rx_dv),
.phy_gmii_rx_er(gmii_rx_er),
.phy_mii_tx_clk(mii_tx_clk),
.phy_gmii_tx_clk(gmii_tx_clk),
.phy_gmii_txd(gmii_txd),
.phy_gmii_tx_en(gmii_tx_en),
.phy_gmii_tx_er(gmii_tx_er),
.mii_select(mii_select_reg)
);
taxi_eth_mac_1g #(
.DATA_W(8),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W),
.PFC_EN(PFC_EN),
.PAUSE_EN(PAUSE_EN),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_1g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(s_axis_tx),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(m_axis_rx),
/*
* GMII interface
*/
.gmii_rxd(mac_gmii_rxd),
.gmii_rx_dv(mac_gmii_rx_dv),
.gmii_rx_er(mac_gmii_rx_er),
.gmii_txd(mac_gmii_txd),
.gmii_tx_en(mac_gmii_tx_en),
.gmii_tx_er(mac_gmii_tx_er),
/*
* PTP
*/
.tx_ptp_ts(tx_ptp_ts),
.rx_ptp_ts(rx_ptp_ts),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(tx_lfc_req),
.tx_lfc_resend(tx_lfc_resend),
.rx_lfc_en(rx_lfc_en),
.rx_lfc_req(rx_lfc_req),
.rx_lfc_ack(rx_lfc_ack),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(tx_pfc_req),
.tx_pfc_resend(tx_pfc_resend),
.rx_pfc_en(rx_pfc_en),
.rx_pfc_req(rx_pfc_req),
.rx_pfc_ack(rx_pfc_ack),
/*
* Pause interface
*/
.tx_lfc_pause_en(tx_lfc_pause_en),
.tx_pause_req(tx_pause_req),
.tx_pause_ack(tx_pause_ack),
/*
* Control
*/
.rx_clk_enable(1'b1),
.tx_clk_enable(1'b1),
.rx_mii_select(rx_mii_select_sync),
.tx_mii_select(tx_mii_select_sync),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
.rx_start_packet(rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(stat_tx_mcf),
.stat_rx_mcf(stat_rx_mcf),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable),
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode),
.cfg_tx_lfc_en(cfg_tx_lfc_en),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode),
.cfg_tx_pfc_en(cfg_tx_pfc_en),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh),
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode),
.cfg_rx_lfc_en(cfg_rx_lfc_en),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode),
.cfg_rx_pfc_en(cfg_rx_pfc_en)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_mac_1g_gmii_fifo.sv
taxi_eth_mac_1g_gmii.f
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo_adapter.f

View File

@@ -0,0 +1,519 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 1G Ethernet MAC with GMII interface and TX and RX FIFOs
*/
module taxi_eth_mac_1g_gmii_fifo #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtex7",
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter TX_FIFO_DEPTH = 4096,
parameter TX_FIFO_RAM_PIPELINE = 1,
parameter logic TX_FRAME_FIFO = 1'b1,
parameter logic TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO,
parameter logic TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME,
parameter logic TX_DROP_WHEN_FULL = 1'b0,
parameter TX_CPL_FIFO_DEPTH = 64,
parameter RX_FIFO_DEPTH = 4096,
parameter RX_FIFO_RAM_PIPELINE = 1,
parameter logic RX_FRAME_FIFO = 1'b1,
parameter logic RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO,
parameter logic RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME,
parameter logic RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME
)
(
input wire logic gtx_clk,
input wire logic gtx_rst,
input wire logic logic_clk,
input wire logic logic_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* GMII interface
*/
input wire logic gmii_rx_clk,
input wire logic [7:0] gmii_rxd,
input wire logic gmii_rx_dv,
input wire logic gmii_rx_er,
input wire logic mii_tx_clk,
output wire logic gmii_tx_clk,
output wire logic [7:0] gmii_txd,
output wire logic gmii_tx_en,
output wire logic gmii_tx_er,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_error_underflow,
output wire logic tx_fifo_overflow,
output wire logic tx_fifo_bad_frame,
output wire logic tx_fifo_good_frame,
output wire logic rx_error_bad_frame,
output wire logic rx_error_bad_fcs,
output wire logic rx_fifo_overflow,
output wire logic rx_fifo_bad_frame,
output wire logic rx_fifo_good_frame,
output wire logic [1:0] link_speed,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1
);
localparam PTP_TS_EN = 1'b0;
localparam PTP_TS_W = 96;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
wire tx_clk;
wire rx_clk;
wire tx_rst;
wire rx_rst;
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(TX_USER_W), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_cpl_int();
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
// synchronize MAC status signals into logic clock domain
wire tx_error_underflow_int;
logic [0:0] tx_sync_reg_1 = '0;
logic [0:0] tx_sync_reg_2 = '0;
logic [0:0] tx_sync_reg_3 = '0;
logic [0:0] tx_sync_reg_4 = '0;
assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
always_ff @(posedge tx_clk or posedge tx_rst) begin
if (tx_rst) begin
tx_sync_reg_1 <= '0;
end else begin
tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
tx_sync_reg_2 <= '0;
tx_sync_reg_3 <= '0;
tx_sync_reg_4 <= '0;
end else begin
tx_sync_reg_2 <= tx_sync_reg_1;
tx_sync_reg_3 <= tx_sync_reg_2;
tx_sync_reg_4 <= tx_sync_reg_3;
end
end
wire rx_error_bad_frame_int;
wire rx_error_bad_fcs_int;
logic [1:0] rx_sync_reg_1 = '0;
logic [1:0] rx_sync_reg_2 = '0;
logic [1:0] rx_sync_reg_3 = '0;
logic [1:0] rx_sync_reg_4 = '0;
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
always_ff @(posedge rx_clk or posedge rx_rst) begin
if (rx_rst) begin
rx_sync_reg_1 <= '0;
end else begin
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_fcs_int, rx_error_bad_frame_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
rx_sync_reg_2 <= '0;
rx_sync_reg_3 <= '0;
rx_sync_reg_4 <= '0;
end else begin
rx_sync_reg_2 <= rx_sync_reg_1;
rx_sync_reg_3 <= rx_sync_reg_2;
rx_sync_reg_4 <= rx_sync_reg_3;
end
end
wire [1:0] link_speed_int;
reg [1:0] link_speed_sync_reg_1 = 2'b10;
reg [1:0] link_speed_sync_reg_2 = 2'b10;
assign link_speed = link_speed_sync_reg_2;
always @(posedge logic_clk) begin
link_speed_sync_reg_1 <= link_speed_int;
link_speed_sync_reg_2 <= link_speed_sync_reg_1;
end
wire stat_rx_fifo_drop;
taxi_eth_mac_1g_gmii #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(1'b0),
.PFC_EN(1'b0),
.PAUSE_EN(1'b0),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_1g_gmii_inst (
.gtx_clk(gtx_clk),
.gtx_rst(gtx_rst),
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(axis_tx_cpl_int),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* GMII interface
*/
.gmii_rx_clk(gmii_rx_clk),
.gmii_rxd(gmii_rxd),
.gmii_rx_dv(gmii_rx_dv),
.gmii_rx_er(gmii_rx_er),
.gmii_tx_clk(gmii_tx_clk),
.mii_tx_clk(mii_tx_clk),
.gmii_txd(gmii_txd),
.gmii_tx_en(gmii_tx_en),
.gmii_tx_er(gmii_tx_er),
/*
* PTP
*/
.tx_ptp_ts(0),
.rx_ptp_ts(0),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(0),
.tx_lfc_resend(0),
.rx_lfc_en(0),
.rx_lfc_req(),
.rx_lfc_ack(0),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(0),
.tx_pfc_resend(0),
.rx_pfc_en(0),
.rx_pfc_req(),
.rx_pfc_ack(0),
/*
* Pause interface
*/
.tx_lfc_pause_en(0),
.tx_pause_req(0),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
// .rx_error_bad_frame(rx_error_bad_frame_int),
.tx_start_packet(),
.stat_tx_byte(),
.stat_tx_pkt_len(),
.stat_tx_pkt_ucast(),
.stat_tx_pkt_mcast(),
.stat_tx_pkt_bcast(),
.stat_tx_pkt_vlan(),
.stat_tx_pkt_good(),
.stat_tx_pkt_bad(),
.stat_tx_err_oversize(),
.stat_tx_err_user(),
.stat_tx_err_underflow(tx_error_underflow_int),
.rx_start_packet(),
.stat_rx_byte(),
.stat_rx_pkt_len(),
.stat_rx_pkt_fragment(),
.stat_rx_pkt_jabber(),
.stat_rx_pkt_ucast(),
.stat_rx_pkt_mcast(),
.stat_rx_pkt_bcast(),
.stat_rx_pkt_vlan(),
.stat_rx_pkt_good(),
.stat_rx_pkt_bad(),
.stat_rx_err_oversize(),
.stat_rx_err_bad_fcs(rx_error_bad_fcs_int),
.stat_rx_err_bad_block(),
.stat_rx_err_framing(),
.stat_rx_err_preamble(),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.link_speed(link_speed_int),
.stat_tx_mcf(),
.stat_rx_mcf(),
.stat_tx_lfc_pkt(),
.stat_tx_lfc_xon(),
.stat_tx_lfc_xoff(),
.stat_tx_lfc_paused(),
.stat_tx_pfc_pkt(),
.stat_tx_pfc_xon(),
.stat_tx_pfc_xoff(),
.stat_tx_pfc_paused(),
.stat_rx_lfc_pkt(),
.stat_rx_lfc_xon(),
.stat_rx_lfc_xoff(),
.stat_rx_lfc_paused(),
.stat_rx_pfc_pkt(),
.stat_rx_pfc_xon(),
.stat_rx_pfc_xoff(),
.stat_rx_pfc_paused(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast('0),
.cfg_mcf_rx_check_eth_dst_mcast('0),
.cfg_mcf_rx_eth_dst_ucast('0),
.cfg_mcf_rx_check_eth_dst_ucast('0),
.cfg_mcf_rx_eth_src('0),
.cfg_mcf_rx_check_eth_src('0),
.cfg_mcf_rx_eth_type('0),
.cfg_mcf_rx_opcode_lfc('0),
.cfg_mcf_rx_check_opcode_lfc('0),
.cfg_mcf_rx_opcode_pfc('0),
.cfg_mcf_rx_check_opcode_pfc('0),
.cfg_mcf_rx_forward('0),
.cfg_mcf_rx_enable('0),
.cfg_tx_lfc_eth_dst('0),
.cfg_tx_lfc_eth_src('0),
.cfg_tx_lfc_eth_type('0),
.cfg_tx_lfc_opcode('0),
.cfg_tx_lfc_en('0),
.cfg_tx_lfc_quanta('0),
.cfg_tx_lfc_refresh('0),
.cfg_tx_pfc_eth_dst('0),
.cfg_tx_pfc_eth_src('0),
.cfg_tx_pfc_eth_type('0),
.cfg_tx_pfc_opcode('0),
.cfg_tx_pfc_en('0),
.cfg_tx_pfc_quanta('{8{'0}}),
.cfg_tx_pfc_refresh('{8{'0}}),
.cfg_rx_lfc_opcode('0),
.cfg_rx_lfc_en('0),
.cfg_rx_pfc_opcode('0),
.cfg_rx_pfc_en('0)
);
taxi_axis_async_fifo_adapter #(
.DEPTH(TX_FIFO_DEPTH),
.RAM_PIPELINE(TX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(TX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
)
tx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(logic_clk),
.s_rst(logic_rst),
.s_axis(s_axis_tx),
/*
* AXI4-Stream output (source)
*/
.m_clk(tx_clk),
.m_rst(tx_rst),
.m_axis(axis_tx_int),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(tx_fifo_overflow),
.s_status_bad_frame(tx_fifo_bad_frame),
.s_status_good_frame(tx_fifo_good_frame),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo #(
.DEPTH(TX_CPL_FIFO_DEPTH),
.FRAME_FIFO(1'b0)
)
tx_cpl_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(tx_clk),
.s_rst(tx_rst),
.s_axis(axis_tx_cpl_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_tx_cpl),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo_adapter #(
.DEPTH(RX_FIFO_DEPTH),
.RAM_PIPELINE(RX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(RX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(RX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(RX_DROP_WHEN_FULL)
)
rx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(rx_clk),
.s_rst(rx_rst),
.s_axis(axis_rx_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_rx),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(stat_rx_fifo_drop),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(rx_fifo_overflow),
.m_status_bad_frame(rx_fifo_bad_frame),
.m_status_good_frame(rx_fifo_good_frame)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_mac_1g_rgmii.sv
taxi_eth_mac_1g.f
taxi_rgmii_phy_if.f

View File

@@ -0,0 +1,520 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 1G Ethernet MAC with RGMII interface
*/
module taxi_eth_mac_1g_rgmii #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtex7",
parameter logic USE_CLK90 = 1'b1,
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96,
parameter logic PFC_EN = 1'b0,
parameter logic PAUSE_EN = PFC_EN,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC"
)
(
input wire logic gtx_clk,
input wire logic gtx_clk90,
input wire logic gtx_rst,
output wire logic rx_clk,
output wire logic rx_rst,
output wire logic tx_clk,
output wire logic tx_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* RGMII interface
*/
input wire logic rgmii_rx_clk,
input wire logic [3:0] rgmii_rxd,
input wire logic rgmii_rx_ctl,
output wire logic rgmii_tx_clk,
output wire logic [3:0] rgmii_txd,
output wire logic rgmii_tx_ctl,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts = '0,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts = '0,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req = 1'b0,
input wire logic tx_lfc_resend = 1'b0,
input wire logic rx_lfc_en = 1'b0,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack = 1'b0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req = '0,
input wire logic tx_pfc_resend = 1'b0,
input wire logic [7:0] rx_pfc_en = '0,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack = '0,
/*
* Pause interface
*/
input wire logic tx_lfc_pause_en = 1'b0,
input wire logic tx_pause_req = 1'b0,
output wire logic tx_pause_ack,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_start_packet,
output wire logic stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
output wire logic rx_start_packet,
output wire logic stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop = 1'b0,
output wire logic [1:0] link_speed,
output wire logic stat_tx_mcf,
output wire logic stat_rx_mcf,
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused,
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast = 48'h01_80_C2_00_00_01,
input wire logic cfg_mcf_rx_check_eth_dst_mcast = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast = 48'd0,
input wire logic cfg_mcf_rx_check_eth_dst_ucast = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_src = 48'd0,
input wire logic cfg_mcf_rx_check_eth_src = 1'b0,
input wire logic [15:0] cfg_mcf_rx_eth_type = 16'h8808,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc = 16'h0001,
input wire logic cfg_mcf_rx_check_opcode_lfc = 1'b1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc = 16'h0101,
input wire logic cfg_mcf_rx_check_opcode_pfc = 1'b1,
input wire logic cfg_mcf_rx_forward = 1'b0,
input wire logic cfg_mcf_rx_enable = 1'b0,
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0
);
reg [1:0] link_speed_reg = 2'b10;
reg mii_select_reg = 1'b0;
wire tx_mii_select_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
tx_mii_select_sync_inst (
.clk(tx_clk),
.in(mii_select_reg),
.out(tx_mii_select_sync)
);
wire rx_mii_select_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
rx_mii_select_sync_inst (
.clk(rx_clk),
.in(mii_select_reg),
.out(rx_mii_select_sync)
);
// PHY speed detection
reg [2:0] rx_prescale = 3'd0;
always_ff @(posedge rx_clk) begin
rx_prescale <= rx_prescale + 3'd1;
end
wire rx_prescale_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
rx_prescale_sync_inst (
.clk(gtx_clk),
.in(rx_prescale[2]),
.out(rx_prescale_sync)
);
reg [6:0] rx_speed_count_1 = 0;
reg [1:0] rx_speed_count_2 = 0;
reg rx_prescale_sync_last_reg = 1'b0;
always_ff @(posedge gtx_clk) begin
rx_prescale_sync_last_reg <= rx_prescale_sync;
rx_speed_count_1 <= rx_speed_count_1 + 1;
if (rx_prescale_sync ^ rx_prescale_sync_last_reg) begin
rx_speed_count_2 <= rx_speed_count_2 + 1;
end
if (&rx_speed_count_1) begin
// reference count overflow - 10M
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
link_speed_reg <= 2'b00;
mii_select_reg <= 1'b1;
end
if (&rx_speed_count_2) begin
// prescaled count overflow - 100M or 1000M
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
if (rx_speed_count_1[6:5] != 0) begin
// large reference count - 100M
link_speed_reg <= 2'b01;
mii_select_reg <= 1'b1;
end else begin
// small reference count - 1000M
link_speed_reg <= 2'b10;
mii_select_reg <= 1'b0;
end
end
if (gtx_rst) begin
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
link_speed_reg <= 2'b10;
mii_select_reg <= 1'b0;
end
end
assign link_speed = link_speed_reg;
wire [7:0] mac_gmii_rxd;
wire mac_gmii_rx_dv;
wire mac_gmii_rx_er;
wire mac_gmii_tx_clk_en;
wire [7:0] mac_gmii_txd;
wire mac_gmii_tx_en;
wire mac_gmii_tx_er;
taxi_rgmii_phy_if #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.USE_CLK90(USE_CLK90)
)
rgmii_phy_if_inst (
.gtx_clk(gtx_clk),
.gtx_clk90(gtx_clk90),
.gtx_rst(gtx_rst),
/*
* GMII interface to MAC
*/
.mac_gmii_rx_clk(rx_clk),
.mac_gmii_rx_rst(rx_rst),
.mac_gmii_rxd(mac_gmii_rxd),
.mac_gmii_rx_dv(mac_gmii_rx_dv),
.mac_gmii_rx_er(mac_gmii_rx_er),
.mac_gmii_tx_clk(tx_clk),
.mac_gmii_tx_rst(tx_rst),
.mac_gmii_tx_clk_en(mac_gmii_tx_clk_en),
.mac_gmii_txd(mac_gmii_txd),
.mac_gmii_tx_en(mac_gmii_tx_en),
.mac_gmii_tx_er(mac_gmii_tx_er),
/*
* RGMII interface to PHY
*/
.phy_rgmii_rx_clk(rgmii_rx_clk),
.phy_rgmii_rxd(rgmii_rxd),
.phy_rgmii_rx_ctl(rgmii_rx_ctl),
.phy_rgmii_tx_clk(rgmii_tx_clk),
.phy_rgmii_txd(rgmii_txd),
.phy_rgmii_tx_ctl(rgmii_tx_ctl),
.speed(link_speed)
);
taxi_eth_mac_1g #(
.DATA_W(8),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W),
.PFC_EN(PFC_EN),
.PAUSE_EN(PAUSE_EN),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_1g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(s_axis_tx),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(m_axis_rx),
/*
* GMII interface
*/
.gmii_rxd(mac_gmii_rxd),
.gmii_rx_dv(mac_gmii_rx_dv),
.gmii_rx_er(mac_gmii_rx_er),
.gmii_txd(mac_gmii_txd),
.gmii_tx_en(mac_gmii_tx_en),
.gmii_tx_er(mac_gmii_tx_er),
/*
* PTP
*/
.tx_ptp_ts(tx_ptp_ts),
.rx_ptp_ts(rx_ptp_ts),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(tx_lfc_req),
.tx_lfc_resend(tx_lfc_resend),
.rx_lfc_en(rx_lfc_en),
.rx_lfc_req(rx_lfc_req),
.rx_lfc_ack(rx_lfc_ack),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(tx_pfc_req),
.tx_pfc_resend(tx_pfc_resend),
.rx_pfc_en(rx_pfc_en),
.rx_pfc_req(rx_pfc_req),
.rx_pfc_ack(rx_pfc_ack),
/*
* Pause interface
*/
.tx_lfc_pause_en(tx_lfc_pause_en),
.tx_pause_req(tx_pause_req),
.tx_pause_ack(tx_pause_ack),
/*
* Control
*/
.rx_clk_enable(1'b1),
.tx_clk_enable(mac_gmii_tx_clk_en),
.rx_mii_select(rx_mii_select_sync),
.tx_mii_select(tx_mii_select_sync),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
.rx_start_packet(rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(stat_tx_mcf),
.stat_rx_mcf(stat_rx_mcf),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable),
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode),
.cfg_tx_lfc_en(cfg_tx_lfc_en),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode),
.cfg_tx_pfc_en(cfg_tx_pfc_en),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh),
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode),
.cfg_rx_lfc_en(cfg_rx_lfc_en),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode),
.cfg_rx_pfc_en(cfg_rx_pfc_en)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_mac_1g_rgmii_fifo.sv
taxi_eth_mac_1g_rgmii.f
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo_adapter.f

View File

@@ -0,0 +1,517 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 1G Ethernet MAC with RGMII interface and TX and RX FIFOs
*/
module taxi_eth_mac_1g_rgmii_fifo #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtex7",
parameter logic USE_CLK90 = 1'b1,
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter TX_FIFO_DEPTH = 4096,
parameter TX_FIFO_RAM_PIPELINE = 1,
parameter logic TX_FRAME_FIFO = 1'b1,
parameter logic TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO,
parameter logic TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME,
parameter logic TX_DROP_WHEN_FULL = 1'b0,
parameter TX_CPL_FIFO_DEPTH = 64,
parameter RX_FIFO_DEPTH = 4096,
parameter RX_FIFO_RAM_PIPELINE = 1,
parameter logic RX_FRAME_FIFO = 1'b1,
parameter logic RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO,
parameter logic RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME,
parameter logic RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME
)
(
input wire logic gtx_clk,
input wire logic gtx_clk90,
input wire logic gtx_rst,
input wire logic logic_clk,
input wire logic logic_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* RGMII interface
*/
input wire logic rgmii_rx_clk,
input wire logic [3:0] rgmii_rxd,
input wire logic rgmii_rx_ctl,
output wire logic rgmii_tx_clk,
output wire logic [3:0] rgmii_txd,
output wire logic rgmii_tx_ctl,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_error_underflow,
output wire logic tx_fifo_overflow,
output wire logic tx_fifo_bad_frame,
output wire logic tx_fifo_good_frame,
output wire logic rx_error_bad_frame,
output wire logic rx_error_bad_fcs,
output wire logic rx_fifo_overflow,
output wire logic rx_fifo_bad_frame,
output wire logic rx_fifo_good_frame,
output wire logic [1:0] link_speed,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1
);
localparam PTP_TS_EN = 1'b0;
localparam PTP_TS_W = 96;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
wire tx_clk;
wire rx_clk;
wire tx_rst;
wire rx_rst;
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(TX_USER_W), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_cpl_int();
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
// synchronize MAC status signals into logic clock domain
wire tx_error_underflow_int;
logic [0:0] tx_sync_reg_1 = '0;
logic [0:0] tx_sync_reg_2 = '0;
logic [0:0] tx_sync_reg_3 = '0;
logic [0:0] tx_sync_reg_4 = '0;
assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
always_ff @(posedge tx_clk or posedge tx_rst) begin
if (tx_rst) begin
tx_sync_reg_1 <= '0;
end else begin
tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
tx_sync_reg_2 <= '0;
tx_sync_reg_3 <= '0;
tx_sync_reg_4 <= '0;
end else begin
tx_sync_reg_2 <= tx_sync_reg_1;
tx_sync_reg_3 <= tx_sync_reg_2;
tx_sync_reg_4 <= tx_sync_reg_3;
end
end
wire rx_error_bad_frame_int;
wire rx_error_bad_fcs_int;
logic [1:0] rx_sync_reg_1 = '0;
logic [1:0] rx_sync_reg_2 = '0;
logic [1:0] rx_sync_reg_3 = '0;
logic [1:0] rx_sync_reg_4 = '0;
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
always_ff @(posedge rx_clk or posedge rx_rst) begin
if (rx_rst) begin
rx_sync_reg_1 <= '0;
end else begin
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_fcs_int, rx_error_bad_frame_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
rx_sync_reg_2 <= '0;
rx_sync_reg_3 <= '0;
rx_sync_reg_4 <= '0;
end else begin
rx_sync_reg_2 <= rx_sync_reg_1;
rx_sync_reg_3 <= rx_sync_reg_2;
rx_sync_reg_4 <= rx_sync_reg_3;
end
end
wire [1:0] link_speed_int;
reg [1:0] link_speed_sync_reg_1 = 2'b10;
reg [1:0] link_speed_sync_reg_2 = 2'b10;
assign link_speed = link_speed_sync_reg_2;
always @(posedge logic_clk) begin
link_speed_sync_reg_1 <= link_speed_int;
link_speed_sync_reg_2 <= link_speed_sync_reg_1;
end
wire stat_rx_fifo_drop;
taxi_eth_mac_1g_rgmii #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.USE_CLK90(USE_CLK90),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(1'b0),
.PFC_EN(1'b0),
.PAUSE_EN(1'b0),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_1g_rgmii_inst (
.gtx_clk(gtx_clk),
.gtx_clk90(gtx_clk90),
.gtx_rst(gtx_rst),
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(axis_tx_cpl_int),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* RGMII interface
*/
.rgmii_rx_clk(rgmii_rx_clk),
.rgmii_rxd(rgmii_rxd),
.rgmii_rx_ctl(rgmii_rx_ctl),
.rgmii_tx_clk(rgmii_tx_clk),
.rgmii_txd(rgmii_txd),
.rgmii_tx_ctl(rgmii_tx_ctl),
/*
* PTP
*/
.tx_ptp_ts(0),
.rx_ptp_ts(0),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(0),
.tx_lfc_resend(0),
.rx_lfc_en(0),
.rx_lfc_req(),
.rx_lfc_ack(0),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(0),
.tx_pfc_resend(0),
.rx_pfc_en(0),
.rx_pfc_req(),
.rx_pfc_ack(0),
/*
* Pause interface
*/
.tx_lfc_pause_en(0),
.tx_pause_req(0),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
// .rx_error_bad_frame(rx_error_bad_frame_int),
.tx_start_packet(),
.stat_tx_byte(),
.stat_tx_pkt_len(),
.stat_tx_pkt_ucast(),
.stat_tx_pkt_mcast(),
.stat_tx_pkt_bcast(),
.stat_tx_pkt_vlan(),
.stat_tx_pkt_good(),
.stat_tx_pkt_bad(),
.stat_tx_err_oversize(),
.stat_tx_err_user(),
.stat_tx_err_underflow(tx_error_underflow_int),
.rx_start_packet(),
.stat_rx_byte(),
.stat_rx_pkt_len(),
.stat_rx_pkt_fragment(),
.stat_rx_pkt_jabber(),
.stat_rx_pkt_ucast(),
.stat_rx_pkt_mcast(),
.stat_rx_pkt_bcast(),
.stat_rx_pkt_vlan(),
.stat_rx_pkt_good(),
.stat_rx_pkt_bad(),
.stat_rx_err_oversize(),
.stat_rx_err_bad_fcs(rx_error_bad_fcs_int),
.stat_rx_err_bad_block(),
.stat_rx_err_framing(),
.stat_rx_err_preamble(),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.link_speed(link_speed_int),
.stat_tx_mcf(),
.stat_rx_mcf(),
.stat_tx_lfc_pkt(),
.stat_tx_lfc_xon(),
.stat_tx_lfc_xoff(),
.stat_tx_lfc_paused(),
.stat_tx_pfc_pkt(),
.stat_tx_pfc_xon(),
.stat_tx_pfc_xoff(),
.stat_tx_pfc_paused(),
.stat_rx_lfc_pkt(),
.stat_rx_lfc_xon(),
.stat_rx_lfc_xoff(),
.stat_rx_lfc_paused(),
.stat_rx_pfc_pkt(),
.stat_rx_pfc_xon(),
.stat_rx_pfc_xoff(),
.stat_rx_pfc_paused(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast('0),
.cfg_mcf_rx_check_eth_dst_mcast('0),
.cfg_mcf_rx_eth_dst_ucast('0),
.cfg_mcf_rx_check_eth_dst_ucast('0),
.cfg_mcf_rx_eth_src('0),
.cfg_mcf_rx_check_eth_src('0),
.cfg_mcf_rx_eth_type('0),
.cfg_mcf_rx_opcode_lfc('0),
.cfg_mcf_rx_check_opcode_lfc('0),
.cfg_mcf_rx_opcode_pfc('0),
.cfg_mcf_rx_check_opcode_pfc('0),
.cfg_mcf_rx_forward('0),
.cfg_mcf_rx_enable('0),
.cfg_tx_lfc_eth_dst('0),
.cfg_tx_lfc_eth_src('0),
.cfg_tx_lfc_eth_type('0),
.cfg_tx_lfc_opcode('0),
.cfg_tx_lfc_en('0),
.cfg_tx_lfc_quanta('0),
.cfg_tx_lfc_refresh('0),
.cfg_tx_pfc_eth_dst('0),
.cfg_tx_pfc_eth_src('0),
.cfg_tx_pfc_eth_type('0),
.cfg_tx_pfc_opcode('0),
.cfg_tx_pfc_en('0),
.cfg_tx_pfc_quanta('{8{'0}}),
.cfg_tx_pfc_refresh('{8{'0}}),
.cfg_rx_lfc_opcode('0),
.cfg_rx_lfc_en('0),
.cfg_rx_pfc_opcode('0),
.cfg_rx_pfc_en('0)
);
taxi_axis_async_fifo_adapter #(
.DEPTH(TX_FIFO_DEPTH),
.RAM_PIPELINE(TX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(TX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
)
tx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(logic_clk),
.s_rst(logic_rst),
.s_axis(s_axis_tx),
/*
* AXI4-Stream output (source)
*/
.m_clk(tx_clk),
.m_rst(tx_rst),
.m_axis(axis_tx_int),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(tx_fifo_overflow),
.s_status_bad_frame(tx_fifo_bad_frame),
.s_status_good_frame(tx_fifo_good_frame),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo #(
.DEPTH(TX_CPL_FIFO_DEPTH),
.FRAME_FIFO(1'b0)
)
tx_cpl_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(tx_clk),
.s_rst(tx_rst),
.s_axis(axis_tx_cpl_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_tx_cpl),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo_adapter #(
.DEPTH(RX_FIFO_DEPTH),
.RAM_PIPELINE(RX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(RX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(RX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(RX_DROP_WHEN_FULL)
)
rx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(rx_clk),
.s_rst(rx_rst),
.s_axis(axis_rx_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_rx),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(stat_rx_fifo_drop),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(rx_fifo_overflow),
.m_status_bad_frame(rx_fifo_bad_frame),
.m_status_good_frame(rx_fifo_good_frame)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_mac_mii.sv
taxi_eth_mac_1g.f
taxi_mii_phy_if.f

View File

@@ -0,0 +1,424 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10M/100M Ethernet MAC with MII interface
*/
module taxi_eth_mac_mii #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtex7",
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic PTP_TS_EN = 1'b0,
parameter PTP_TS_W = 96,
parameter logic PFC_EN = 1'b0,
parameter logic PAUSE_EN = PFC_EN,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC"
)
(
input wire logic rst,
output wire logic rx_clk,
output wire logic rx_rst,
output wire logic tx_clk,
output wire logic tx_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* MII interface
*/
input wire logic mii_rx_clk,
input wire logic [3:0] mii_rxd,
input wire logic mii_rx_dv,
input wire logic mii_rx_er,
input wire logic mii_tx_clk,
output wire logic [3:0] mii_txd,
output wire logic mii_tx_en,
output wire logic mii_tx_er,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts = '0,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts = '0,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req = 1'b0,
input wire logic tx_lfc_resend = 1'b0,
input wire logic rx_lfc_en = 1'b0,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack = 1'b0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req = '0,
input wire logic tx_pfc_resend = 1'b0,
input wire logic [7:0] rx_pfc_en = '0,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack = '0,
/*
* Pause interface
*/
input wire logic tx_lfc_pause_en = 1'b0,
input wire logic tx_pause_req = 1'b0,
output wire logic tx_pause_ack,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_start_packet,
output wire logic stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
output wire logic rx_start_packet,
output wire logic stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop = 1'b0,
output wire logic stat_tx_mcf,
output wire logic stat_rx_mcf,
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused,
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast = 48'h01_80_C2_00_00_01,
input wire logic cfg_mcf_rx_check_eth_dst_mcast = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast = 48'd0,
input wire logic cfg_mcf_rx_check_eth_dst_ucast = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_src = 48'd0,
input wire logic cfg_mcf_rx_check_eth_src = 1'b0,
input wire logic [15:0] cfg_mcf_rx_eth_type = 16'h8808,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc = 16'h0001,
input wire logic cfg_mcf_rx_check_opcode_lfc = 1'b1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc = 16'h0101,
input wire logic cfg_mcf_rx_check_opcode_pfc = 1'b1,
input wire logic cfg_mcf_rx_forward = 1'b0,
input wire logic cfg_mcf_rx_enable = 1'b0,
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0
);
wire [7:0] mac_gmii_rxd;
wire mac_gmii_rx_dv;
wire mac_gmii_rx_er;
wire [7:0] mac_gmii_txd;
wire mac_gmii_tx_en;
wire mac_gmii_tx_er;
taxi_mii_phy_if #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY)
)
mii_phy_if_inst (
.rst(rst),
/*
* MII interface to MAC
*/
.mac_mii_rx_clk(rx_clk),
.mac_mii_rx_rst(rx_rst),
.mac_mii_rxd(mac_gmii_rxd[3:0]),
.mac_mii_rx_dv(mac_gmii_rx_dv),
.mac_mii_rx_er(mac_gmii_rx_er),
.mac_mii_tx_clk(tx_clk),
.mac_mii_tx_rst(tx_rst),
.mac_mii_txd(mac_gmii_txd[3:0]),
.mac_mii_tx_en(mac_gmii_tx_en),
.mac_mii_tx_er(mac_gmii_tx_er),
/*
* MII interface to PHY
*/
.phy_mii_rx_clk(mii_rx_clk),
.phy_mii_rxd(mii_rxd),
.phy_mii_rx_dv(mii_rx_dv),
.phy_mii_rx_er(mii_rx_er),
.phy_mii_tx_clk(mii_tx_clk),
.phy_mii_txd(mii_txd),
.phy_mii_tx_en(mii_tx_en),
.phy_mii_tx_er(mii_tx_er)
);
assign mac_gmii_rxd[7:4] = '0;
taxi_eth_mac_1g #(
.DATA_W(8),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_W(PTP_TS_W),
.PFC_EN(PFC_EN),
.PAUSE_EN(PAUSE_EN),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_1g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(s_axis_tx),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(m_axis_rx),
/*
* GMII interface
*/
.gmii_rxd(mac_gmii_rxd),
.gmii_rx_dv(mac_gmii_rx_dv),
.gmii_rx_er(mac_gmii_rx_er),
.gmii_txd(mac_gmii_txd),
.gmii_tx_en(mac_gmii_tx_en),
.gmii_tx_er(mac_gmii_tx_er),
/*
* PTP
*/
.tx_ptp_ts(tx_ptp_ts),
.rx_ptp_ts(rx_ptp_ts),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(tx_lfc_req),
.tx_lfc_resend(tx_lfc_resend),
.rx_lfc_en(rx_lfc_en),
.rx_lfc_req(rx_lfc_req),
.rx_lfc_ack(rx_lfc_ack),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(tx_pfc_req),
.tx_pfc_resend(tx_pfc_resend),
.rx_pfc_en(rx_pfc_en),
.rx_pfc_req(rx_pfc_req),
.rx_pfc_ack(rx_pfc_ack),
/*
* Pause interface
*/
.tx_lfc_pause_en(tx_lfc_pause_en),
.tx_pause_req(tx_pause_req),
.tx_pause_ack(tx_pause_ack),
/*
* Control
*/
.rx_clk_enable(1'b1),
.tx_clk_enable(1'b1),
.rx_mii_select(1'b1),
.tx_mii_select(1'b1),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
.rx_start_packet(rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(stat_tx_mcf),
.stat_rx_mcf(stat_rx_mcf),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable),
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode),
.cfg_tx_lfc_en(cfg_tx_lfc_en),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode),
.cfg_tx_pfc_en(cfg_tx_pfc_en),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh),
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode),
.cfg_rx_lfc_en(cfg_rx_lfc_en),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode),
.cfg_rx_pfc_en(cfg_rx_pfc_en)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_mac_mii_fifo.sv
taxi_eth_mac_mii.f
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo_adapter.f

View File

@@ -0,0 +1,501 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10M/100M Ethernet MAC with MII interface and TX and RX FIFOs
*/
module taxi_eth_mac_mii_fifo #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtex7",
parameter logic PADDING_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = STAT_TX_LEVEL,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter TX_FIFO_DEPTH = 4096,
parameter TX_FIFO_RAM_PIPELINE = 1,
parameter logic TX_FRAME_FIFO = 1'b1,
parameter logic TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO,
parameter logic TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME,
parameter logic TX_DROP_WHEN_FULL = 1'b0,
parameter TX_CPL_FIFO_DEPTH = 64,
parameter RX_FIFO_DEPTH = 4096,
parameter RX_FIFO_RAM_PIPELINE = 1,
parameter logic RX_FRAME_FIFO = 1'b1,
parameter logic RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO,
parameter logic RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME,
parameter logic RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME
)
(
input wire rst,
input wire logic_clk,
input wire logic_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* MII interface
*/
input wire mii_rx_clk,
input wire [3:0] mii_rxd,
input wire mii_rx_dv,
input wire mii_rx_er,
input wire mii_tx_clk,
output wire [3:0] mii_txd,
output wire mii_tx_en,
output wire mii_tx_er,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_error_underflow,
output wire logic tx_fifo_overflow,
output wire logic tx_fifo_bad_frame,
output wire logic tx_fifo_good_frame,
output wire logic rx_error_bad_frame,
output wire logic rx_error_bad_fcs,
output wire logic rx_fifo_overflow,
output wire logic rx_fifo_bad_frame,
output wire logic rx_fifo_good_frame,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1
);
localparam PTP_TS_EN = 1'b0;
localparam PTP_TS_W = 96;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
wire tx_clk;
wire rx_clk;
wire tx_rst;
wire rx_rst;
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(TX_USER_W), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_cpl_int();
taxi_axis_if #(.DATA_W(8), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
// synchronize MAC status signals into logic clock domain
wire tx_error_underflow_int;
logic [0:0] tx_sync_reg_1 = '0;
logic [0:0] tx_sync_reg_2 = '0;
logic [0:0] tx_sync_reg_3 = '0;
logic [0:0] tx_sync_reg_4 = '0;
assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
always_ff @(posedge tx_clk or posedge tx_rst) begin
if (tx_rst) begin
tx_sync_reg_1 <= '0;
end else begin
tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
tx_sync_reg_2 <= '0;
tx_sync_reg_3 <= '0;
tx_sync_reg_4 <= '0;
end else begin
tx_sync_reg_2 <= tx_sync_reg_1;
tx_sync_reg_3 <= tx_sync_reg_2;
tx_sync_reg_4 <= tx_sync_reg_3;
end
end
wire rx_error_bad_frame_int;
wire rx_error_bad_fcs_int;
logic [1:0] rx_sync_reg_1 = '0;
logic [1:0] rx_sync_reg_2 = '0;
logic [1:0] rx_sync_reg_3 = '0;
logic [1:0] rx_sync_reg_4 = '0;
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
always_ff @(posedge rx_clk or posedge rx_rst) begin
if (rx_rst) begin
rx_sync_reg_1 <= '0;
end else begin
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_fcs_int, rx_error_bad_frame_int};
end
end
always_ff @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
rx_sync_reg_2 <= '0;
rx_sync_reg_3 <= '0;
rx_sync_reg_4 <= '0;
end else begin
rx_sync_reg_2 <= rx_sync_reg_1;
rx_sync_reg_3 <= rx_sync_reg_2;
rx_sync_reg_4 <= rx_sync_reg_3;
end
end
wire stat_rx_fifo_drop;
taxi_eth_mac_mii #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.PADDING_EN(PADDING_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(1'b0),
.PFC_EN(1'b0),
.PAUSE_EN(1'b0),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_mii_inst (
.rst(rst),
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(axis_tx_cpl_int),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* MII interface
*/
.mii_rx_clk(mii_rx_clk),
.mii_rxd(mii_rxd),
.mii_rx_dv(mii_rx_dv),
.mii_rx_er(mii_rx_er),
.mii_tx_clk(mii_tx_clk),
.mii_txd(mii_txd),
.mii_tx_en(mii_tx_en),
.mii_tx_er(mii_tx_er),
/*
* PTP
*/
.tx_ptp_ts(0),
.rx_ptp_ts(0),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(0),
.tx_lfc_resend(0),
.rx_lfc_en(0),
.rx_lfc_req(),
.rx_lfc_ack(0),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(0),
.tx_pfc_resend(0),
.rx_pfc_en(0),
.rx_pfc_req(),
.rx_pfc_ack(0),
/*
* Pause interface
*/
.tx_lfc_pause_en(0),
.tx_pause_req(0),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
// .rx_error_bad_frame(rx_error_bad_frame_int),
.tx_start_packet(),
.stat_tx_byte(),
.stat_tx_pkt_len(),
.stat_tx_pkt_ucast(),
.stat_tx_pkt_mcast(),
.stat_tx_pkt_bcast(),
.stat_tx_pkt_vlan(),
.stat_tx_pkt_good(),
.stat_tx_pkt_bad(),
.stat_tx_err_oversize(),
.stat_tx_err_user(),
.stat_tx_err_underflow(tx_error_underflow_int),
.rx_start_packet(),
.stat_rx_byte(),
.stat_rx_pkt_len(),
.stat_rx_pkt_fragment(),
.stat_rx_pkt_jabber(),
.stat_rx_pkt_ucast(),
.stat_rx_pkt_mcast(),
.stat_rx_pkt_bcast(),
.stat_rx_pkt_vlan(),
.stat_rx_pkt_good(),
.stat_rx_pkt_bad(),
.stat_rx_err_oversize(),
.stat_rx_err_bad_fcs(rx_error_bad_fcs_int),
.stat_rx_err_bad_block(),
.stat_rx_err_framing(),
.stat_rx_err_preamble(),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(),
.stat_rx_mcf(),
.stat_tx_lfc_pkt(),
.stat_tx_lfc_xon(),
.stat_tx_lfc_xoff(),
.stat_tx_lfc_paused(),
.stat_tx_pfc_pkt(),
.stat_tx_pfc_xon(),
.stat_tx_pfc_xoff(),
.stat_tx_pfc_paused(),
.stat_rx_lfc_pkt(),
.stat_rx_lfc_xon(),
.stat_rx_lfc_xoff(),
.stat_rx_lfc_paused(),
.stat_rx_pfc_pkt(),
.stat_rx_pfc_xon(),
.stat_rx_pfc_xoff(),
.stat_rx_pfc_paused(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_mcf_rx_eth_dst_mcast('0),
.cfg_mcf_rx_check_eth_dst_mcast('0),
.cfg_mcf_rx_eth_dst_ucast('0),
.cfg_mcf_rx_check_eth_dst_ucast('0),
.cfg_mcf_rx_eth_src('0),
.cfg_mcf_rx_check_eth_src('0),
.cfg_mcf_rx_eth_type('0),
.cfg_mcf_rx_opcode_lfc('0),
.cfg_mcf_rx_check_opcode_lfc('0),
.cfg_mcf_rx_opcode_pfc('0),
.cfg_mcf_rx_check_opcode_pfc('0),
.cfg_mcf_rx_forward('0),
.cfg_mcf_rx_enable('0),
.cfg_tx_lfc_eth_dst('0),
.cfg_tx_lfc_eth_src('0),
.cfg_tx_lfc_eth_type('0),
.cfg_tx_lfc_opcode('0),
.cfg_tx_lfc_en('0),
.cfg_tx_lfc_quanta('0),
.cfg_tx_lfc_refresh('0),
.cfg_tx_pfc_eth_dst('0),
.cfg_tx_pfc_eth_src('0),
.cfg_tx_pfc_eth_type('0),
.cfg_tx_pfc_opcode('0),
.cfg_tx_pfc_en('0),
.cfg_tx_pfc_quanta('{8{'0}}),
.cfg_tx_pfc_refresh('{8{'0}}),
.cfg_rx_lfc_opcode('0),
.cfg_rx_lfc_en('0),
.cfg_rx_pfc_opcode('0),
.cfg_rx_pfc_en('0)
);
taxi_axis_async_fifo_adapter #(
.DEPTH(TX_FIFO_DEPTH),
.RAM_PIPELINE(TX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(TX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
)
tx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(logic_clk),
.s_rst(logic_rst),
.s_axis(s_axis_tx),
/*
* AXI4-Stream output (source)
*/
.m_clk(tx_clk),
.m_rst(tx_rst),
.m_axis(axis_tx_int),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(tx_fifo_overflow),
.s_status_bad_frame(tx_fifo_bad_frame),
.s_status_good_frame(tx_fifo_good_frame),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo #(
.DEPTH(TX_CPL_FIFO_DEPTH),
.FRAME_FIFO(1'b0)
)
tx_cpl_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(tx_clk),
.s_rst(tx_rst),
.s_axis(axis_tx_cpl_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_tx_cpl),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo_adapter #(
.DEPTH(RX_FIFO_DEPTH),
.RAM_PIPELINE(RX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(RX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(RX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(RX_DROP_WHEN_FULL)
)
rx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(rx_clk),
.s_rst(rx_rst),
.s_axis(axis_rx_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_rx),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(stat_rx_fifo_drop),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(rx_fifo_overflow),
.m_status_bad_frame(rx_fifo_bad_frame),
.m_status_good_frame(rx_fifo_good_frame)
);
endmodule
`resetall

View File

@@ -0,0 +1,9 @@
taxi_eth_mac_phy_10g.sv
taxi_eth_mac_phy_10g_rx.f
taxi_eth_mac_phy_10g_tx.f
taxi_eth_mac_stats.f
taxi_mac_ctrl_tx.sv
taxi_mac_ctrl_rx.sv
taxi_mac_pause_ctrl_tx.sv
taxi_mac_pause_ctrl_rx.sv
../lib/taxi/src/sync/rtl/taxi_sync_signal.sv

View File

@@ -0,0 +1,754 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet MAC/PHY combination
*/
module taxi_eth_mac_phy_10g #
(
parameter DATA_W = 64,
parameter HDR_W = (DATA_W/32),
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter TX_SERDES_PIPELINE = 0,
parameter RX_SERDES_PIPELINE = 0,
parameter BITSLIP_HIGH_CYCLES = 0,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = 1,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter logic PFC_EN = 1'b0,
parameter logic PAUSE_EN = PFC_EN
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* SERDES interface
*/
output wire logic [DATA_W-1:0] serdes_tx_data,
output wire logic [HDR_W-1:0] serdes_tx_hdr,
input wire logic [DATA_W-1:0] serdes_rx_data,
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req = 1'b0,
input wire logic tx_lfc_resend = 1'b0,
input wire logic rx_lfc_en = 1'b0,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack = 1'b0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req = '0,
input wire logic tx_pfc_resend = 1'b0,
input wire logic [7:0] rx_pfc_en = '0,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack = '0,
/*
* Pause interface
*/
input wire logic tx_lfc_pause_en = 1'b0,
input wire logic tx_pause_req = 1'b0,
output wire logic tx_pause_ack,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic [1:0] tx_start_packet,
output wire logic [3:0] stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
output wire logic [1:0] rx_start_packet,
output wire logic [6:0] rx_error_count,
output wire logic rx_block_lock,
output wire logic rx_high_ber,
output wire logic rx_status,
output wire logic [3:0] stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop = 1'b0,
output wire logic stat_tx_mcf,
output wire logic stat_rx_mcf,
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused,
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic cfg_tx_prbs31_enable = 1'b0,
input wire logic cfg_rx_prbs31_enable = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast = 48'h01_80_C2_00_00_01,
input wire logic cfg_mcf_rx_check_eth_dst_mcast = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast = 48'd0,
input wire logic cfg_mcf_rx_check_eth_dst_ucast = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_src = 48'd0,
input wire logic cfg_mcf_rx_check_eth_src = 1'b0,
input wire logic [15:0] cfg_mcf_rx_eth_type = 16'h8808,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc = 16'h0001,
input wire logic cfg_mcf_rx_check_opcode_lfc = 1'b1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc = 16'h0101,
input wire logic cfg_mcf_rx_check_opcode_pfc = 1'b1,
input wire logic cfg_mcf_rx_forward = 1'b0,
input wire logic cfg_mcf_rx_enable = 1'b0,
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0
);
localparam KEEP_W = s_axis_tx.KEEP_W;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
localparam MAC_CTRL_EN = PAUSE_EN || PFC_EN;
localparam TX_USER_W_INT = (MAC_CTRL_EN ? 1 : 0) + TX_USER_W;
taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(TX_USER_W_INT), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(DATA_W), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
taxi_eth_mac_phy_10g_rx #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(RX_SERDES_PIPELINE),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES),
.COUNT_125US(COUNT_125US)
)
eth_mac_phy_10g_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* SERDES interface
*/
.serdes_rx_data(serdes_rx_data),
.serdes_rx_hdr(serdes_rx_hdr),
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* PTP
*/
.ptp_ts(rx_ptp_ts),
/*
* Status
*/
.rx_start_packet(rx_start_packet),
.rx_error_count(rx_error_count),
.rx_block_lock(rx_block_lock),
.rx_high_ber(rx_high_ber),
.rx_status(rx_status),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
/*
* Configuration
*/
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable)
);
taxi_eth_mac_phy_10g_tx #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.TX_CPL_CTRL_IN_TUSER(MAC_CTRL_EN),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(TX_SERDES_PIPELINE)
)
eth_mac_phy_10g_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* SERDES interface
*/
.serdes_tx_data(serdes_tx_data),
.serdes_tx_hdr(serdes_tx_hdr),
/*
* PTP
*/
.ptp_ts(tx_ptp_ts),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable)
);
if (STAT_EN) begin : stats
taxi_eth_mac_stats #(
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR),
.INC_W(4)
)
mac_stats_inst (
.rx_clk(rx_clk),
.rx_rst(rx_rst),
.tx_clk(tx_clk),
.tx_rst(tx_rst),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(|tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
.rx_start_packet(|rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(stat_tx_mcf),
.stat_rx_mcf(stat_rx_mcf),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused)
);
end else begin
assign m_axis_stat.tdata = '0;
assign m_axis_stat.tkeep = '0;
assign m_axis_stat.tlast = '0;
assign m_axis_stat.tvalid = '0;
assign m_axis_stat.tid = '0;
assign m_axis_stat.tdest = '0;
assign m_axis_stat.tuser = '0;
end
if (MAC_CTRL_EN) begin : mac_ctrl
localparam MCF_PARAMS_SIZE = PFC_EN ? 18 : 2;
wire tx_mcf_valid;
wire tx_mcf_ready;
wire [47:0] tx_mcf_eth_dst;
wire [47:0] tx_mcf_eth_src;
wire [15:0] tx_mcf_eth_type;
wire [15:0] tx_mcf_opcode;
wire [MCF_PARAMS_SIZE*8-1:0] tx_mcf_params;
wire rx_mcf_valid;
wire [47:0] rx_mcf_eth_dst;
wire [47:0] rx_mcf_eth_src;
wire [15:0] rx_mcf_eth_type;
wire [15:0] rx_mcf_opcode;
wire [MCF_PARAMS_SIZE*8-1:0] rx_mcf_params;
// terminate LFC pause requests from RX internally on TX side
wire tx_pause_req_int;
wire rx_lfc_ack_int;
wire rx_lfc_req_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
rx_lfc_req_sync_inst (
.clk(tx_clk),
.in(rx_lfc_req),
.out(rx_lfc_req_sync)
);
wire tx_pause_ack_sync;
taxi_sync_signal #(
.WIDTH(1),
.N(2)
)
tx_pause_ack_sync_inst (
.clk(rx_clk),
.in(tx_lfc_pause_en && tx_pause_ack),
.out(tx_pause_ack_sync)
);
assign tx_pause_req_int = tx_pause_req || (tx_lfc_pause_en && rx_lfc_req_sync);
assign rx_lfc_ack_int = rx_lfc_ack || tx_pause_ack_sync;
taxi_mac_ctrl_tx #(
.ID_W(s_axis_tx.ID_W),
.DEST_W(s_axis_tx.DEST_W),
.USER_W(TX_USER_W_INT),
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE)
)
mac_ctrl_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* AXI stream input
*/
.s_axis(s_axis_tx),
/*
* AXI stream output
*/
.m_axis(axis_tx_int),
/*
* MAC control frame interface
*/
.mcf_valid(tx_mcf_valid),
.mcf_ready(tx_mcf_ready),
.mcf_eth_dst(tx_mcf_eth_dst),
.mcf_eth_src(tx_mcf_eth_src),
.mcf_eth_type(tx_mcf_eth_type),
.mcf_opcode(tx_mcf_opcode),
.mcf_params(tx_mcf_params),
.mcf_id('0),
.mcf_dest('0),
.mcf_user(2'b10),
/*
* Pause interface
*/
.tx_pause_req(tx_pause_req_int),
.tx_pause_ack(tx_pause_ack),
/*
* Status
*/
.stat_tx_mcf(stat_tx_mcf)
);
taxi_mac_ctrl_rx #(
.USER_W(RX_USER_W),
.USE_READY(0),
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE)
)
mac_ctrl_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* AXI stream input
*/
.s_axis(axis_rx_int),
/*
* AXI stream output
*/
.m_axis(m_axis_rx),
/*
* MAC control frame interface
*/
.mcf_valid(rx_mcf_valid),
.mcf_eth_dst(rx_mcf_eth_dst),
.mcf_eth_src(rx_mcf_eth_src),
.mcf_eth_type(rx_mcf_eth_type),
.mcf_opcode(rx_mcf_opcode),
.mcf_params(rx_mcf_params),
.mcf_id(),
.mcf_dest(),
.mcf_user(),
/*
* Configuration
*/
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc && PFC_EN),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable),
/*
* Status
*/
.stat_rx_mcf(stat_rx_mcf)
);
taxi_mac_pause_ctrl_tx #(
.MCF_PARAMS_SIZE(MCF_PARAMS_SIZE),
.PFC_EN(PFC_EN)
)
mac_pause_ctrl_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* MAC control frame interface
*/
.mcf_valid(tx_mcf_valid),
.mcf_ready(tx_mcf_ready),
.mcf_eth_dst(tx_mcf_eth_dst),
.mcf_eth_src(tx_mcf_eth_src),
.mcf_eth_type(tx_mcf_eth_type),
.mcf_opcode(tx_mcf_opcode),
.mcf_params(tx_mcf_params),
/*
* Pause (IEEE 802.3 annex 31B)
*/
.tx_lfc_req(tx_lfc_req),
.tx_lfc_resend(tx_lfc_resend),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D)
*/
.tx_pfc_req(tx_pfc_req),
.tx_pfc_resend(tx_pfc_resend),
/*
* Configuration
*/
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode),
.cfg_tx_lfc_en(cfg_tx_lfc_en),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode),
.cfg_tx_pfc_en(cfg_tx_pfc_en),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh),
.cfg_quanta_step(10'((DATA_W*256)/512)),
.cfg_quanta_clk_en(1'b1),
/*
* Status
*/
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused)
);
taxi_mac_pause_ctrl_rx #(
.MCF_PARAMS_SIZE(18),
.PFC_EN(PFC_EN)
)
mac_pause_ctrl_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* MAC control frame interface
*/
.mcf_valid(rx_mcf_valid),
.mcf_eth_dst(rx_mcf_eth_dst),
.mcf_eth_src(rx_mcf_eth_src),
.mcf_eth_type(rx_mcf_eth_type),
.mcf_opcode(rx_mcf_opcode),
.mcf_params(rx_mcf_params),
/*
* Pause (IEEE 802.3 annex 31B)
*/
.rx_lfc_en(rx_lfc_en),
.rx_lfc_req(rx_lfc_req),
.rx_lfc_ack(rx_lfc_ack_int),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D)
*/
.rx_pfc_en(rx_pfc_en),
.rx_pfc_req(rx_pfc_req),
.rx_pfc_ack(rx_pfc_ack),
/*
* Configuration
*/
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode),
.cfg_rx_lfc_en(cfg_rx_lfc_en),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode),
.cfg_rx_pfc_en(cfg_rx_pfc_en),
.cfg_quanta_step(10'((DATA_W*256)/512)),
.cfg_quanta_clk_en(1'b1),
/*
* Status
*/
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused)
);
end else begin
assign axis_tx_int.tdata = s_axis_tx.tdata;
assign axis_tx_int.tkeep = s_axis_tx.tkeep;
assign axis_tx_int.tvalid = s_axis_tx.tvalid;
assign s_axis_tx.tready = axis_tx_int.tready;
assign axis_tx_int.tlast = s_axis_tx.tlast;
assign axis_tx_int.tid = s_axis_tx.tid;
assign axis_tx_int.tdest = s_axis_tx.tdest;
assign axis_tx_int.tuser = s_axis_tx.tuser;
assign m_axis_rx.tdata = axis_rx_int.tdata;
assign m_axis_rx.tkeep = axis_rx_int.tkeep;
assign m_axis_rx.tvalid = axis_rx_int.tvalid;
assign m_axis_rx.tlast = axis_rx_int.tlast;
assign m_axis_rx.tid = axis_rx_int.tid;
assign m_axis_rx.tdest = axis_rx_int.tdest;
assign m_axis_rx.tuser = axis_rx_int.tuser;
assign rx_lfc_req = 0;
assign rx_pfc_req = 0;
assign tx_pause_ack = 0;
assign stat_tx_mcf = 0;
assign stat_rx_mcf = 0;
assign stat_tx_lfc_pkt = 0;
assign stat_tx_lfc_xon = 0;
assign stat_tx_lfc_xoff = 0;
assign stat_tx_lfc_paused = 0;
assign stat_tx_pfc_pkt = 0;
assign stat_tx_pfc_xon = 0;
assign stat_tx_pfc_xoff = 0;
assign stat_tx_pfc_paused = 0;
assign stat_rx_lfc_pkt = 0;
assign stat_rx_lfc_xon = 0;
assign stat_rx_lfc_xoff = 0;
assign stat_rx_lfc_paused = 0;
assign stat_rx_pfc_pkt = 0;
assign stat_rx_pfc_xon = 0;
assign stat_rx_pfc_xoff = 0;
assign stat_rx_pfc_paused = 0;
end
endmodule
`resetall

View File

@@ -0,0 +1,4 @@
taxi_eth_mac_phy_10g_fifo.sv
taxi_eth_mac_phy_10g.f
../lib/taxi/src/ptp/rtl/taxi_ptp_clock_cdc.sv
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo_adapter.f

View File

@@ -0,0 +1,603 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet MAC/PHY combination with TX and RX FIFOs
*/
module taxi_eth_mac_phy_10g_fifo #
(
parameter DATA_W = 64,
parameter HDR_W = (DATA_W/32),
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter TX_SERDES_PIPELINE = 0,
parameter RX_SERDES_PIPELINE = 0,
parameter BITSLIP_HIGH_CYCLES = 0,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = 1,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter TX_FIFO_DEPTH = 4096,
parameter TX_FIFO_RAM_PIPELINE = 1,
parameter logic TX_FRAME_FIFO = 1'b1,
parameter logic TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO,
parameter logic TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME,
parameter logic TX_DROP_WHEN_FULL = 1'b0,
parameter TX_CPL_FIFO_DEPTH = 64,
parameter RX_FIFO_DEPTH = 4096,
parameter RX_FIFO_RAM_PIPELINE = 1,
parameter logic RX_FRAME_FIFO = 1'b1,
parameter logic RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO,
parameter logic RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME,
parameter logic RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
input wire logic logic_clk,
input wire logic logic_rst,
input wire logic ptp_sample_clk,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* SERDES interface
*/
output wire logic [DATA_W-1:0] serdes_tx_data,
output wire logic [HDR_W-1:0] serdes_tx_hdr,
input wire logic [DATA_W-1:0] serdes_rx_data,
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* PTP clock
*/
input wire logic [PTP_TS_W-1:0] ptp_ts = '0,
input wire logic ptp_ts_step = 1'b0,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic tx_error_underflow,
output wire logic tx_fifo_overflow,
output wire logic tx_fifo_bad_frame,
output wire logic tx_fifo_good_frame,
output wire logic rx_error_bad_frame,
output wire logic rx_error_bad_fcs,
output wire logic rx_bad_block,
output wire logic rx_sequence_error,
output wire logic rx_block_lock,
output wire logic rx_high_ber,
output wire logic rx_status,
output wire logic rx_fifo_overflow,
output wire logic rx_fifo_bad_frame,
output wire logic rx_fifo_good_frame,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic cfg_tx_prbs31_enable = 1'b0,
input wire logic cfg_rx_prbs31_enable = 1'b0
);
localparam KEEP_W = DATA_W/8;
localparam TX_USER_W = 1;
localparam RX_USER_W = (PTP_TS_EN ? PTP_TS_W : 0) + 1;
localparam TX_TAG_W = s_axis_tx.ID_W;
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(KEEP_W), .USER_EN(1), .USER_W(TX_USER_W), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_int();
taxi_axis_if #(.DATA_W(PTP_TS_W), .KEEP_W(1), .ID_EN(1), .ID_W(TX_TAG_W)) axis_tx_cpl_int();
taxi_axis_if #(.DATA_W(DATA_W), .KEEP_W(KEEP_W), .USER_EN(1), .USER_W(RX_USER_W)) axis_rx_int();
wire [PTP_TS_W-1:0] tx_ptp_ts;
wire [PTP_TS_W-1:0] rx_ptp_ts;
wire tx_ptp_locked;
wire rx_ptp_locked;
// synchronize MAC status signals into logic clock domain
wire tx_error_underflow_int;
reg [0:0] tx_sync_reg_1 = '0;
reg [0:0] tx_sync_reg_2 = '0;
reg [0:0] tx_sync_reg_3 = '0;
reg [0:0] tx_sync_reg_4 = '0;
assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
always @(posedge tx_clk or posedge tx_rst) begin
if (tx_rst) begin
tx_sync_reg_1 <= '0;
end else begin
tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
end
end
always @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
tx_sync_reg_2 <= '0;
tx_sync_reg_3 <= '0;
tx_sync_reg_4 <= '0;
end else begin
tx_sync_reg_2 <= tx_sync_reg_1;
tx_sync_reg_3 <= tx_sync_reg_2;
tx_sync_reg_4 <= tx_sync_reg_3;
end
end
wire rx_error_bad_frame_int;
wire rx_error_bad_fcs_int;
wire rx_bad_block_int;
wire rx_sequence_error_int;
wire rx_block_lock_int;
wire rx_high_ber_int;
wire rx_status_int;
reg [6:0] rx_sync_reg_1 = '0;
reg [6:0] rx_sync_reg_2 = '0;
reg [6:0] rx_sync_reg_3 = '0;
reg [6:0] rx_sync_reg_4 = '0;
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
assign rx_bad_block = rx_sync_reg_3[2] ^ rx_sync_reg_4[2];
assign rx_sequence_error = rx_sync_reg_3[3] ^ rx_sync_reg_4[3];
assign rx_block_lock = rx_sync_reg_4[4];
assign rx_high_ber = rx_sync_reg_4[5];
assign rx_status = rx_sync_reg_4[6];
always @(posedge rx_clk or posedge rx_rst) begin
if (rx_rst) begin
rx_sync_reg_1 <= '0;
end else begin
rx_sync_reg_1[0] <= rx_sync_reg_1[0] ^ rx_error_bad_frame_int;
rx_sync_reg_1[1] <= rx_sync_reg_1[1] ^ rx_error_bad_fcs_int;
rx_sync_reg_1[2] <= rx_sync_reg_1[2] ^ rx_bad_block_int;
rx_sync_reg_1[3] <= rx_sync_reg_1[3] ^ rx_sequence_error_int;
rx_sync_reg_1[4] <= rx_block_lock_int;
rx_sync_reg_1[5] <= rx_high_ber_int;
rx_sync_reg_1[6] <= rx_status_int;
end
end
always @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
rx_sync_reg_2 <= '0;
rx_sync_reg_3 <= '0;
rx_sync_reg_4 <= '0;
end else begin
rx_sync_reg_2 <= rx_sync_reg_1;
rx_sync_reg_3 <= rx_sync_reg_2;
rx_sync_reg_4 <= rx_sync_reg_3;
end
end
// PTP timestamping
if (PTP_TS_EN) begin : ptp
taxi_ptp_clock_cdc #(
.TS_W(PTP_TS_W),
.NS_W(6)
)
tx_ptp_cdc (
.input_clk(logic_clk),
.input_rst(logic_rst),
.output_clk(tx_clk),
.output_rst(tx_rst),
.sample_clk(ptp_sample_clk),
.input_ts(ptp_ts),
.input_ts_step(ptp_ts_step),
.output_ts(tx_ptp_ts),
.output_ts_step(),
.output_pps(),
.output_pps_str(),
.locked(tx_ptp_locked)
);
taxi_ptp_clock_cdc #(
.TS_W(PTP_TS_W),
.NS_W(6)
)
rx_ptp_cdc (
.input_clk(logic_clk),
.input_rst(logic_rst),
.output_clk(rx_clk),
.output_rst(rx_rst),
.sample_clk(ptp_sample_clk),
.input_ts(ptp_ts),
.input_ts_step(ptp_ts_step),
.output_ts(rx_ptp_ts),
.output_ts_step(),
.output_pps(),
.output_pps_str(),
.locked(rx_ptp_locked)
);
end else begin
assign tx_ptp_ts = '0;
assign rx_ptp_ts = '0;
assign tx_ptp_locked = 1'b0;
assign rx_ptp_locked = 1'b0;
end
wire stat_rx_fifo_drop;
taxi_eth_mac_phy_10g #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.TX_SERDES_PIPELINE(TX_SERDES_PIPELINE),
.RX_SERDES_PIPELINE(RX_SERDES_PIPELINE),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES),
.COUNT_125US(COUNT_125US),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_phy_10g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(axis_tx_int),
.m_axis_tx_cpl(axis_tx_cpl_int),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(axis_rx_int),
/*
* Serdes interface
*/
.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),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* PTP
*/
.tx_ptp_ts(tx_ptp_ts),
.rx_ptp_ts(rx_ptp_ts),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(0),
.tx_lfc_resend(0),
.rx_lfc_en(0),
.rx_lfc_req(),
.rx_lfc_ack(0),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(0),
.tx_pfc_resend(0),
.rx_pfc_en(0),
.rx_pfc_req(),
.rx_pfc_ack(0),
/*
* Pause interface
*/
.tx_lfc_pause_en(0),
.tx_pause_req(0),
.tx_pause_ack(),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(),
.stat_tx_byte(),
.stat_tx_pkt_len(),
.stat_tx_pkt_ucast(),
.stat_tx_pkt_mcast(),
.stat_tx_pkt_bcast(),
.stat_tx_pkt_vlan(),
.stat_tx_pkt_good(),
.stat_tx_pkt_bad(),
.stat_tx_err_oversize(),
.stat_tx_err_user(),
.stat_tx_err_underflow(tx_error_underflow_int),
.rx_start_packet(),
.rx_error_count(),
.rx_block_lock(rx_block_lock_int),
.rx_high_ber(rx_high_ber_int),
.rx_status(rx_status_int),
.stat_rx_byte(),
.stat_rx_pkt_len(),
.stat_rx_pkt_fragment(),
.stat_rx_pkt_jabber(),
.stat_rx_pkt_ucast(),
.stat_rx_pkt_mcast(),
.stat_rx_pkt_bcast(),
.stat_rx_pkt_vlan(),
.stat_rx_pkt_good(),
.stat_rx_pkt_bad(rx_error_bad_frame_int),
.stat_rx_err_oversize(),
.stat_rx_err_bad_fcs(rx_error_bad_fcs_int),
.stat_rx_err_bad_block(rx_bad_block_int),
.stat_rx_err_framing(rx_sequence_error_int),
.stat_rx_err_preamble(),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(),
.stat_rx_mcf(),
.stat_tx_lfc_pkt(),
.stat_tx_lfc_xon(),
.stat_tx_lfc_xoff(),
.stat_tx_lfc_paused(),
.stat_tx_pfc_pkt(),
.stat_tx_pfc_xon(),
.stat_tx_pfc_xoff(),
.stat_tx_pfc_paused(),
.stat_rx_lfc_pkt(),
.stat_rx_lfc_xon(),
.stat_rx_lfc_xoff(),
.stat_rx_lfc_paused(),
.stat_rx_pfc_pkt(),
.stat_rx_pfc_xon(),
.stat_rx_pfc_xoff(),
.stat_rx_pfc_paused(),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable),
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable),
.cfg_mcf_rx_eth_dst_mcast('0),
.cfg_mcf_rx_check_eth_dst_mcast('0),
.cfg_mcf_rx_eth_dst_ucast('0),
.cfg_mcf_rx_check_eth_dst_ucast('0),
.cfg_mcf_rx_eth_src('0),
.cfg_mcf_rx_check_eth_src('0),
.cfg_mcf_rx_eth_type('0),
.cfg_mcf_rx_opcode_lfc('0),
.cfg_mcf_rx_check_opcode_lfc('0),
.cfg_mcf_rx_opcode_pfc('0),
.cfg_mcf_rx_check_opcode_pfc('0),
.cfg_mcf_rx_forward('0),
.cfg_mcf_rx_enable('0),
.cfg_tx_lfc_eth_dst('0),
.cfg_tx_lfc_eth_src('0),
.cfg_tx_lfc_eth_type('0),
.cfg_tx_lfc_opcode('0),
.cfg_tx_lfc_en('0),
.cfg_tx_lfc_quanta('0),
.cfg_tx_lfc_refresh('0),
.cfg_tx_pfc_eth_dst('0),
.cfg_tx_pfc_eth_src('0),
.cfg_tx_pfc_eth_type('0),
.cfg_tx_pfc_opcode('0),
.cfg_tx_pfc_en('0),
.cfg_tx_pfc_quanta('{8{'0}}),
.cfg_tx_pfc_refresh('{8{'0}}),
.cfg_rx_lfc_opcode('0),
.cfg_rx_lfc_en('0),
.cfg_rx_pfc_opcode('0),
.cfg_rx_pfc_en('0)
);
taxi_axis_async_fifo_adapter #(
.DEPTH(TX_FIFO_DEPTH),
.RAM_PIPELINE(TX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(TX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
)
tx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(logic_clk),
.s_rst(logic_rst),
.s_axis(s_axis_tx),
/*
* AXI4-Stream output (source)
*/
.m_clk(tx_clk),
.m_rst(tx_rst),
.m_axis(axis_tx_int),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(tx_fifo_overflow),
.s_status_bad_frame(tx_fifo_bad_frame),
.s_status_good_frame(tx_fifo_good_frame),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo #(
.DEPTH(TX_CPL_FIFO_DEPTH),
.FRAME_FIFO(1'b0)
)
tx_cpl_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(tx_clk),
.s_rst(tx_rst),
.s_axis(axis_tx_cpl_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_tx_cpl),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_async_fifo_adapter #(
.DEPTH(RX_FIFO_DEPTH),
.RAM_PIPELINE(RX_FIFO_RAM_PIPELINE),
.FRAME_FIFO(RX_FRAME_FIFO),
.USER_BAD_FRAME_VALUE(1'b1),
.USER_BAD_FRAME_MASK(1'b1),
.DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME),
.DROP_BAD_FRAME(RX_DROP_BAD_FRAME),
.DROP_WHEN_FULL(RX_DROP_WHEN_FULL)
)
rx_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(rx_clk),
.s_rst(rx_rst),
.s_axis(axis_rx_int),
/*
* AXI4-Stream output (source)
*/
.m_clk(logic_clk),
.m_rst(logic_rst),
.m_axis(m_axis_rx),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(stat_rx_fifo_drop),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(rx_fifo_overflow),
.m_status_bad_frame(rx_fifo_bad_frame),
.m_status_good_frame(rx_fifo_good_frame)
);
endmodule
`resetall

View File

@@ -0,0 +1,5 @@
taxi_eth_mac_phy_10g_rx.sv
taxi_eth_phy_10g_rx_if.f
taxi_axis_baser_rx_64.sv
../lib/taxi/src/lfsr/rtl/taxi_lfsr.sv
../lib/taxi/src/axis/rtl/taxi_axis_if.sv

View File

@@ -0,0 +1,191 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet MAC/PHY combination
*/
module taxi_eth_mac_phy_10g_rx #
(
parameter DATA_W = 64,
parameter HDR_W = (DATA_W/32),
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,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter SERDES_PIPELINE = 0,
parameter BITSLIP_HIGH_CYCLES = 0,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4
)
(
input wire logic clk,
input wire logic rst,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* SERDES interface
*/
input wire logic [DATA_W-1:0] serdes_rx_data,
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Status
*/
output wire logic [1:0] rx_start_packet,
output wire logic [6:0] rx_error_count,
output wire logic rx_block_lock,
output wire logic rx_high_ber,
output wire logic rx_status,
output wire logic [3:0] stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
/*
* Configuration
*/
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable,
input wire logic cfg_rx_prbs31_enable
);
wire [DATA_W-1:0] encoded_rx_data;
wire [HDR_W-1:0] encoded_rx_hdr;
taxi_eth_phy_10g_rx_if #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(SERDES_PIPELINE),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES),
.COUNT_125US(COUNT_125US)
)
eth_phy_10g_rx_if_inst (
.clk(clk),
.rst(rst),
/*
* 10GBASE-R encoded interface
*/
.encoded_rx_data(encoded_rx_data),
.encoded_rx_hdr(encoded_rx_hdr),
/*
* SERDES interface
*/
.serdes_rx_data(serdes_rx_data),
.serdes_rx_hdr(serdes_rx_hdr),
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* Status
*/
.rx_bad_block(stat_rx_err_bad_block),
.rx_sequence_error(stat_rx_err_framing),
.rx_error_count(rx_error_count),
.rx_block_lock(rx_block_lock),
.rx_high_ber(rx_high_ber),
.rx_status(rx_status),
/*
* Configuration
*/
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable)
);
taxi_axis_baser_rx_64 #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W)
)
axis_baser_rx_inst (
.clk(clk),
.rst(rst),
/*
* 10GBASE-R encoded input
*/
.encoded_rx_data(encoded_rx_data),
.encoded_rx_hdr(encoded_rx_hdr),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(m_axis_rx),
/*
* PTP
*/
.ptp_ts(ptp_ts),
/*
* Configuration
*/
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
/*
* Status
*/
.rx_start_packet(rx_start_packet),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble)
);
endmodule
`resetall

View File

@@ -0,0 +1,5 @@
taxi_eth_mac_phy_10g_tx.sv
taxi_eth_phy_10g_tx_if.f
taxi_axis_baser_tx_64.sv
../lib/taxi/src/lfsr/rtl/taxi_lfsr.sv
../lib/taxi/src/axis/rtl/taxi_axis_if.sv

View File

@@ -0,0 +1,171 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet MAC/PHY combination
*/
module taxi_eth_mac_phy_10g_tx #
(
parameter DATA_W = 64,
parameter HDR_W = (DATA_W/32),
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic TX_CPL_CTRL_IN_TUSER = 1'b0,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter SERDES_PIPELINE = 0
)
(
input wire logic clk,
input wire logic rst,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* SERDES interface
*/
output wire logic [DATA_W-1:0] serdes_tx_data,
output wire logic [HDR_W-1:0] serdes_tx_hdr,
/*
* PTP
*/
input wire logic [PTP_TS_W-1:0] ptp_ts,
/*
* Status
*/
output wire logic [1:0] tx_start_packet,
output wire logic [3:0] stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable,
input wire logic cfg_tx_prbs31_enable
);
wire [DATA_W-1:0] encoded_tx_data;
wire [HDR_W-1:0] encoded_tx_hdr;
taxi_axis_baser_tx_64 #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.TX_CPL_CTRL_IN_TUSER(TX_CPL_CTRL_IN_TUSER)
)
axis_baser_tx_inst (
.clk(clk),
.rst(rst),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(s_axis_tx),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* 10GBASE-R encoded interface
*/
.encoded_tx_data(encoded_tx_data),
.encoded_tx_hdr(encoded_tx_hdr),
/*
* PTP
*/
.ptp_ts(ptp_ts),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow)
);
taxi_eth_phy_10g_tx_if #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(SERDES_PIPELINE)
)
eth_phy_10g_tx_if_inst (
.clk(clk),
.rst(rst),
/*
* 10GBASE-R encoded interface
*/
.encoded_tx_data(encoded_tx_data),
.encoded_tx_hdr(encoded_tx_hdr),
/*
* SERDES interface
*/
.serdes_tx_data(serdes_tx_data),
.serdes_tx_hdr(serdes_tx_hdr),
/*
* Configuration
*/
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable)
);
endmodule
`resetall

View File

@@ -0,0 +1,4 @@
taxi_eth_mac_stats.sv
../lib/taxi/src/axis/rtl/taxi_axis_async_fifo.f
../lib/taxi/src/axis/rtl/taxi_axis_arb_mux.f
../lib/taxi/src/stats/rtl/taxi_stats_collect.sv

View File

@@ -0,0 +1,459 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* MAC statistics
*/
module taxi_eth_mac_stats #
(
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = 1,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC",
parameter INC_W = 1
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
input wire logic tx_start_packet,
input wire logic [INC_W-1:0] stat_tx_byte,
input wire logic [15:0] stat_tx_pkt_len,
input wire logic stat_tx_pkt_ucast,
input wire logic stat_tx_pkt_mcast,
input wire logic stat_tx_pkt_bcast,
input wire logic stat_tx_pkt_vlan,
input wire logic stat_tx_pkt_good,
input wire logic stat_tx_pkt_bad,
input wire logic stat_tx_err_oversize,
input wire logic stat_tx_err_user,
input wire logic stat_tx_err_underflow,
input wire logic rx_start_packet,
input wire logic [INC_W-1:0] stat_rx_byte,
input wire logic [15:0] stat_rx_pkt_len,
input wire logic stat_rx_pkt_fragment,
input wire logic stat_rx_pkt_jabber,
input wire logic stat_rx_pkt_ucast,
input wire logic stat_rx_pkt_mcast,
input wire logic stat_rx_pkt_bcast,
input wire logic stat_rx_pkt_vlan,
input wire logic stat_rx_pkt_good,
input wire logic stat_rx_pkt_bad,
input wire logic stat_rx_err_oversize,
input wire logic stat_rx_err_bad_fcs,
input wire logic stat_rx_err_bad_block,
input wire logic stat_rx_err_framing,
input wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop,
input wire logic stat_tx_mcf,
input wire logic stat_rx_mcf,
input wire logic stat_tx_lfc_pkt,
input wire logic stat_tx_lfc_xon,
input wire logic stat_tx_lfc_xoff,
input wire logic stat_tx_lfc_paused,
input wire logic stat_tx_pfc_pkt,
input wire logic [7:0] stat_tx_pfc_xon,
input wire logic [7:0] stat_tx_pfc_xoff,
input wire logic [7:0] stat_tx_pfc_paused,
input wire logic stat_rx_lfc_pkt,
input wire logic stat_rx_lfc_xon,
input wire logic stat_rx_lfc_xoff,
input wire logic stat_rx_lfc_paused,
input wire logic stat_rx_pfc_pkt,
input wire logic [7:0] stat_rx_pfc_xon,
input wire logic [7:0] stat_rx_pfc_xoff,
input wire logic [7:0] stat_rx_pfc_paused
);
localparam TX_CNT = STAT_TX_LEVEL == 0 ? 8 : (STAT_TX_LEVEL == 1 ? 16: 32);
localparam RX_CNT = STAT_RX_LEVEL == 0 ? 8 : (STAT_RX_LEVEL == 1 ? 16: 32);
taxi_axis_if #(
.DATA_W(m_axis_stat.DATA_W),
.KEEP_EN(m_axis_stat.KEEP_EN),
.KEEP_W(m_axis_stat.KEEP_W),
.LAST_EN(m_axis_stat.LAST_EN),
.ID_EN(m_axis_stat.ID_EN),
.ID_W(m_axis_stat.ID_W),
.USER_EN(m_axis_stat.USER_EN),
.USER_W(m_axis_stat.USER_W)
)
axis_stat_tx(), axis_stat_rx(), axis_stat_int[2]();
wire [8*8-1:0] tx_str[TX_CNT];
wire [INC_W-1:0] tx_inc[TX_CNT];
wire hist_tx_pkt_small = (stat_tx_pkt_len != 0) && stat_tx_pkt_len[15:6] == 0;
wire hist_tx_pkt_64 = stat_tx_pkt_len == 64;
wire hist_tx_pkt_65_127 = stat_tx_pkt_len[15:6] == 1 && stat_tx_pkt_len != 64;
wire hist_tx_pkt_128_255 = stat_tx_pkt_len[15:7] == 1;
wire hist_tx_pkt_256_511 = stat_tx_pkt_len[15:8] == 1;
wire hist_tx_pkt_512_1023 = stat_tx_pkt_len[15:9] == 1;
wire hist_tx_pkt_1024_1518 = stat_tx_pkt_len[15:10] == 1 && stat_tx_pkt_len <= 1518;
wire hist_tx_pkt_large_1 = stat_tx_pkt_len > 1518;
wire hist_tx_pkt_1519_2047 = stat_tx_pkt_len[15:11] == 0 && stat_tx_pkt_len > 1518;
wire hist_tx_pkt_2048_4095 = stat_tx_pkt_len[15:11] == 1;
wire hist_tx_pkt_4096_8192 = stat_tx_pkt_len[15:12] == 1;
wire hist_tx_pkt_8192_9215 = stat_tx_pkt_len[15:13] == 1 && stat_tx_pkt_len <= 9215;
wire hist_tx_pkt_large_2 = stat_tx_pkt_len > 9215;
assign tx_str[0] = "TX_BYTES";
assign tx_inc[0] = stat_tx_byte;
assign tx_str[1] = "TX_PKTS";
assign tx_inc[1] = INC_W'(tx_start_packet);
assign tx_str[2] = "TX_ERR";
assign tx_inc[2] = INC_W'(stat_tx_err_user);
assign tx_str[3] = "TX_UNDR";
assign tx_inc[3] = INC_W'(stat_tx_err_underflow);
assign tx_str[4] = "TX_OVRSZ";
assign tx_inc[4] = INC_W'(stat_tx_err_oversize);
assign tx_str[5] = "TX_CTRL";
assign tx_inc[5] = INC_W'(stat_tx_mcf);
assign tx_str[6] = "TX_COL";
assign tx_inc[6] = INC_W'(0);
assign tx_str[7] = "";
assign tx_inc[7] = INC_W'(0);
if (STAT_TX_LEVEL > 0) begin
assign tx_str[8] = "TX_PSM";
assign tx_inc[8] = INC_W'(hist_tx_pkt_small);
assign tx_str[9] = "TX_P64";
assign tx_inc[9] = INC_W'(hist_tx_pkt_64);
assign tx_str[10] = "TX_P65";
assign tx_inc[10] = INC_W'(hist_tx_pkt_65_127);
assign tx_str[11] = "TX_P128";
assign tx_inc[11] = INC_W'(hist_tx_pkt_128_255);
assign tx_str[12] = "TX_P256";
assign tx_inc[12] = INC_W'(hist_tx_pkt_256_511);
assign tx_str[13] = "TX_P512";
assign tx_inc[13] = INC_W'(hist_tx_pkt_512_1023);
assign tx_str[14] = "TX_P1024";
assign tx_inc[14] = INC_W'(hist_tx_pkt_1024_1518);
assign tx_str[15] = "TX_PLG";
assign tx_inc[15] = INC_W'(STAT_TX_LEVEL > 1 ? hist_tx_pkt_large_2 : hist_tx_pkt_large_1);
end
if (STAT_TX_LEVEL > 1) begin
assign tx_str[16] = "TX_P1519";
assign tx_inc[16] = INC_W'(hist_tx_pkt_1519_2047);
assign tx_str[17] = "TX_P2048";
assign tx_inc[17] = INC_W'(hist_tx_pkt_2048_4095);
assign tx_str[18] = "TX_P4096";
assign tx_inc[18] = INC_W'(hist_tx_pkt_4096_8192);
assign tx_str[19] = "TX_P8192";
assign tx_inc[19] = INC_W'(hist_tx_pkt_8192_9215);
assign tx_str[20] = "TX_UCAST";
assign tx_inc[20] = INC_W'(stat_tx_pkt_ucast);
assign tx_str[21] = "TX_MCAST";
assign tx_inc[21] = INC_W'(stat_tx_pkt_mcast);
assign tx_str[22] = "TX_BCAST";
assign tx_inc[22] = INC_W'(stat_tx_pkt_bcast);
assign tx_str[23] = "TX_VLAN";
assign tx_inc[23] = INC_W'(stat_tx_pkt_vlan);
assign tx_str[24] = "TX_LFC";
assign tx_inc[24] = INC_W'(stat_tx_lfc_pkt);
assign tx_str[25] = "TX_PFC";
assign tx_inc[25] = INC_W'(stat_tx_pfc_pkt);
assign tx_str[26] = "TX_MCOL";
assign tx_inc[26] = INC_W'(0);
assign tx_str[27] = "TX_DEFER";
assign tx_inc[27] = INC_W'(0);
assign tx_str[28] = "TX_LCOL";
assign tx_inc[28] = INC_W'(0);
assign tx_str[29] = "TX_ECOL";
assign tx_inc[29] = INC_W'(0);
assign tx_str[30] = "TX_EDEF";
assign tx_inc[30] = INC_W'(0);
assign tx_str[31] = "";
assign tx_inc[31] = INC_W'(0);
end
taxi_stats_collect #(
.CNT(TX_CNT),
.INC_W(INC_W),
.ID_BASE(STAT_ID_BASE),
.UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STR_EN(STAT_STR_EN),
.PREFIX_STR(STAT_PREFIX_STR)
)
tx_stats_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* Increment inputs
*/
.stat_inc(tx_inc),
.stat_valid('{TX_CNT{1'b1}}),
.stat_str(tx_str),
/*
* Statistics increment output
*/
.m_axis_stat(axis_stat_tx),
/*
* Control inputs
*/
.gate(1'b1),
.update(1'b0)
);
taxi_axis_async_fifo #(
.DEPTH(32),
.RAM_PIPELINE(0),
.FRAME_FIFO(1'b0),
.DROP_BAD_FRAME(1'b0),
.DROP_WHEN_FULL(1'b0)
)
tx_stat_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(tx_clk),
.s_rst(tx_rst),
.s_axis(axis_stat_tx),
/*
* AXI4-Stream output (source)
*/
.m_clk(stat_clk),
.m_rst(stat_rst),
.m_axis(axis_stat_int[0]),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
wire [8*8-1:0] rx_str[TX_CNT];
wire [INC_W-1:0] rx_inc[RX_CNT];
wire hist_rx_pkt_small = (stat_rx_pkt_len != 0) && stat_rx_pkt_len[15:6] == 0;
wire hist_rx_pkt_64 = stat_rx_pkt_len == 64;
wire hist_rx_pkt_65_127 = stat_rx_pkt_len[15:6] == 1 && stat_rx_pkt_len != 64;
wire hist_rx_pkt_128_255 = stat_rx_pkt_len[15:7] == 1;
wire hist_rx_pkt_256_511 = stat_rx_pkt_len[15:8] == 1;
wire hist_rx_pkt_512_1023 = stat_rx_pkt_len[15:9] == 1;
wire hist_rx_pkt_1024_1518 = stat_rx_pkt_len[15:10] == 1 && stat_rx_pkt_len <= 1518;
wire hist_rx_pkt_large_1 = stat_rx_pkt_len > 1518;
wire hist_rx_pkt_1519_2047 = stat_rx_pkt_len[15:11] == 0 && stat_rx_pkt_len > 1518;
wire hist_rx_pkt_2048_4095 = stat_rx_pkt_len[15:11] == 1;
wire hist_rx_pkt_4096_8192 = stat_rx_pkt_len[15:12] == 1;
wire hist_rx_pkt_8192_9215 = stat_rx_pkt_len[15:13] == 1 && stat_rx_pkt_len <= 9215;
wire hist_rx_pkt_large_2 = stat_rx_pkt_len > 9215;
assign rx_str[0] = "RX_BYTES";
assign rx_inc[0] = stat_rx_byte;
assign rx_str[1] = "RX_PKTS";
assign rx_inc[1] = INC_W'(rx_start_packet);
assign rx_str[2] = "RX_FCSER";
assign rx_inc[2] = INC_W'(stat_rx_err_bad_fcs);
assign rx_str[3] = "RX_FDRP";
assign rx_inc[3] = INC_W'(stat_rx_fifo_drop);
assign rx_str[4] = "RX_OVRSZ";
assign rx_inc[4] = INC_W'(stat_rx_err_oversize);
assign rx_str[5] = "RX_CTRL";
assign rx_inc[5] = INC_W'(stat_rx_mcf);
assign rx_str[6] = "RX_ERBLK";
assign rx_inc[6] = INC_W'(stat_rx_err_bad_block);
assign rx_str[7] = "RX_ERFRM";
assign rx_inc[7] = INC_W'(stat_rx_err_framing);
if (STAT_RX_LEVEL > 0) begin
assign rx_str[8] = "RX_PSM";
assign rx_inc[8] = INC_W'(hist_rx_pkt_small);
assign rx_str[9] = "RX_P64";
assign rx_inc[9] = INC_W'(hist_rx_pkt_64);
assign rx_str[10] = "RX_P65";
assign rx_inc[10] = INC_W'(hist_rx_pkt_65_127);
assign rx_str[11] = "RX_P128";
assign rx_inc[11] = INC_W'(hist_rx_pkt_128_255);
assign rx_str[12] = "RX_P256";
assign rx_inc[12] = INC_W'(hist_rx_pkt_256_511);
assign rx_str[13] = "RX_P512";
assign rx_inc[13] = INC_W'(hist_rx_pkt_512_1023);
assign rx_str[14] = "RX_P1024";
assign rx_inc[14] = INC_W'(hist_rx_pkt_1024_1518);
assign rx_str[15] = "RX_PLG";
assign rx_inc[15] = INC_W'(STAT_RX_LEVEL > 1 ? hist_rx_pkt_large_2 : hist_rx_pkt_large_1);
end
if (STAT_RX_LEVEL > 1) begin
assign rx_str[16] = "RX_P1519";
assign rx_inc[16] = INC_W'(hist_rx_pkt_1519_2047);
assign rx_str[17] = "RX_P2048";
assign rx_inc[17] = INC_W'(hist_rx_pkt_2048_4095);
assign rx_str[18] = "RX_P4096";
assign rx_inc[18] = INC_W'(hist_rx_pkt_4096_8192);
assign rx_str[19] = "RX_P8192";
assign rx_inc[19] = INC_W'(hist_rx_pkt_8192_9215);
assign rx_str[20] = "RX_UCAST";
assign rx_inc[20] = INC_W'(stat_rx_pkt_ucast);
assign rx_str[21] = "RX_MCAST";
assign rx_inc[21] = INC_W'(stat_rx_pkt_mcast);
assign rx_str[22] = "RX_BCAST";
assign rx_inc[22] = INC_W'(stat_rx_pkt_bcast);
assign rx_str[23] = "RX_VLAN";
assign rx_inc[23] = INC_W'(stat_rx_pkt_vlan);
assign rx_str[24] = "RX_LFC";
assign rx_inc[24] = INC_W'(stat_rx_lfc_pkt);
assign rx_str[25] = "RX_PFC";
assign rx_inc[25] = INC_W'(stat_rx_pfc_pkt);
assign rx_str[26] = "RX_ERPRE";
assign rx_inc[26] = INC_W'(stat_rx_err_preamble);
assign rx_str[27] = "RX_FRG";
assign rx_inc[27] = INC_W'(stat_rx_pkt_fragment);
assign rx_str[28] = "RX_JBR";
assign rx_inc[28] = INC_W'(stat_rx_pkt_jabber);
assign rx_str[29] = "";
assign rx_inc[29] = INC_W'(0);
assign rx_str[30] = "";
assign rx_inc[30] = INC_W'(0);
assign rx_str[31] = "";
assign rx_inc[31] = INC_W'(0);
end
taxi_stats_collect #(
.CNT(RX_CNT),
.INC_W(INC_W),
.ID_BASE(STAT_ID_BASE+TX_CNT),
.UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STR_EN(STAT_STR_EN),
.PREFIX_STR(STAT_PREFIX_STR)
)
rx_stats_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* Increment inputs
*/
.stat_inc(rx_inc),
.stat_valid('{RX_CNT{1'b1}}),
.stat_str(rx_str),
/*
* Statistics increment output
*/
.m_axis_stat(axis_stat_rx),
/*
* Control inputs
*/
.gate(1'b1),
.update(1'b0)
);
taxi_axis_async_fifo #(
.DEPTH(32),
.RAM_PIPELINE(0),
.FRAME_FIFO(1'b0),
.DROP_BAD_FRAME(1'b0),
.DROP_WHEN_FULL(1'b0)
)
rx_stat_fifo (
/*
* AXI4-Stream input (sink)
*/
.s_clk(rx_clk),
.s_rst(rx_rst),
.s_axis(axis_stat_rx),
/*
* AXI4-Stream output (source)
*/
.m_clk(stat_clk),
.m_rst(stat_rst),
.m_axis(axis_stat_int[1]),
/*
* Pause
*/
.s_pause_req(1'b0),
.s_pause_ack(),
.m_pause_req(1'b0),
.m_pause_ack(),
/*
* Status
*/
.s_status_depth(),
.s_status_depth_commit(),
.s_status_overflow(),
.s_status_bad_frame(),
.s_status_good_frame(),
.m_status_depth(),
.m_status_depth_commit(),
.m_status_overflow(),
.m_status_bad_frame(),
.m_status_good_frame()
);
taxi_axis_arb_mux #(
.S_COUNT($size(axis_stat_int)),
.UPDATE_TID(1'b0),
.ARB_ROUND_ROBIN(1'b1),
.ARB_LSB_HIGH_PRIO(1'b0)
)
stat_mux_inst (
.clk(stat_clk),
.rst(stat_rst),
/*
* AXI4-Stream inputs (sink)
*/
.s_axis(axis_stat_int),
/*
* AXI4-Stream output (source)
*/
.m_axis(m_axis_stat)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_phy_10g.sv
taxi_eth_phy_10g_rx.f
taxi_eth_phy_10g_tx.f

View File

@@ -0,0 +1,158 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY
*/
module taxi_eth_phy_10g #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter HDR_W = 2,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter TX_SERDES_PIPELINE = 0,
parameter RX_SERDES_PIPELINE = 0,
parameter BITSLIP_HIGH_CYCLES = 1,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4
)
(
input wire logic rx_clk,
input wire logic rx_rst,
input wire logic tx_clk,
input wire logic tx_rst,
/*
* XGMII interface
*/
input wire logic [DATA_W-1:0] xgmii_txd,
input wire logic [CTRL_W-1:0] xgmii_txc,
output wire logic [DATA_W-1:0] xgmii_rxd,
output wire logic [CTRL_W-1:0] xgmii_rxc,
/*
* SERDES interface
*/
output wire logic [DATA_W-1:0] serdes_tx_data,
output wire logic [HDR_W-1:0] serdes_tx_hdr,
input wire logic [DATA_W-1:0] serdes_rx_data,
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* Status
*/
output wire logic tx_bad_block,
output wire logic [6:0] rx_error_count,
output wire logic rx_bad_block,
output wire logic rx_sequence_error,
output wire logic rx_block_lock,
output wire logic rx_high_ber,
output wire logic rx_status,
/*
* Configuration
*/
input wire logic cfg_tx_prbs31_enable = 1'b0,
input wire logic cfg_rx_prbs31_enable = 1'b0
);
taxi_eth_phy_10g_rx #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.HDR_W(HDR_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(RX_SERDES_PIPELINE),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES),
.COUNT_125US(COUNT_125US)
)
eth_phy_10g_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
/*
* XGMII interface
*/
.xgmii_rxd(xgmii_rxd),
.xgmii_rxc(xgmii_rxc),
/*
* SERDES interface
*/
.serdes_rx_data(serdes_rx_data),
.serdes_rx_hdr(serdes_rx_hdr),
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* Status
*/
.rx_error_count(rx_error_count),
.rx_bad_block(rx_bad_block),
.rx_sequence_error(rx_sequence_error),
.rx_block_lock(rx_block_lock),
.rx_high_ber(rx_high_ber),
.rx_status(rx_status),
/*
* Configuration
*/
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable)
);
taxi_eth_phy_10g_tx #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.HDR_W(HDR_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(TX_SERDES_PIPELINE)
)
eth_phy_10g_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
/*
* XGMII interface
*/
.xgmii_txd(xgmii_txd),
.xgmii_txc(xgmii_txc),
/*
* SERDES interface
*/
.serdes_tx_data(serdes_tx_data),
.serdes_tx_hdr(serdes_tx_hdr),
/*
* Status
*/
.tx_bad_block(tx_bad_block),
/*
* Configuration
*/
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable)
);
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_phy_10g_rx.sv
taxi_eth_phy_10g_rx_if.f
taxi_xgmii_baser_dec_64.sv

View File

@@ -0,0 +1,153 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY RX
*/
module taxi_eth_phy_10g_rx #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter HDR_W = 2,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter SERDES_PIPELINE = 0,
parameter BITSLIP_HIGH_CYCLES = 1,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4
)
(
input wire logic clk,
input wire logic rst,
/*
* XGMII interface
*/
output wire logic [DATA_W-1:0] xgmii_rxd,
output wire logic [CTRL_W-1:0] xgmii_rxc,
/*
* SERDES interface
*/
input wire logic [DATA_W-1:0] serdes_rx_data,
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* Status
*/
output wire logic [6:0] rx_error_count,
output wire logic rx_bad_block,
output wire logic rx_sequence_error,
output wire logic rx_block_lock,
output wire logic rx_high_ber,
output wire logic rx_status,
/*
* Configuration
*/
input wire logic cfg_rx_prbs31_enable
);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64");
if (CTRL_W * 8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
wire [DATA_W-1:0] encoded_rx_data;
wire [HDR_W-1:0] encoded_rx_hdr;
taxi_eth_phy_10g_rx_if #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(SERDES_PIPELINE),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES),
.COUNT_125US(COUNT_125US)
)
eth_phy_10g_rx_if_inst (
.clk(clk),
.rst(rst),
/*
* 10GBASE-R encoded interface
*/
.encoded_rx_data(encoded_rx_data),
.encoded_rx_hdr(encoded_rx_hdr),
/*
* SERDES interface
*/
.serdes_rx_data(serdes_rx_data),
.serdes_rx_hdr(serdes_rx_hdr),
.serdes_rx_bitslip(serdes_rx_bitslip),
.serdes_rx_reset_req(serdes_rx_reset_req),
/*
* Status
*/
.rx_bad_block(rx_bad_block),
.rx_sequence_error(rx_sequence_error),
.rx_error_count(rx_error_count),
.rx_block_lock(rx_block_lock),
.rx_high_ber(rx_high_ber),
.rx_status(rx_status),
/*
* Configuration
*/
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable)
);
taxi_xgmii_baser_dec_64 #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.HDR_W(HDR_W)
)
xgmii_baser_dec_inst (
.clk(clk),
.rst(rst),
/*
* 10GBASE-R encoded input
*/
.encoded_rx_data(encoded_rx_data),
.encoded_rx_hdr(encoded_rx_hdr),
/*
* XGMII interface
*/
.xgmii_rxd(xgmii_rxd),
.xgmii_rxc(xgmii_rxc),
/*
* Status
*/
.rx_bad_block(rx_bad_block),
.rx_sequence_error(rx_sequence_error)
);
endmodule
`resetall

View File

@@ -0,0 +1,105 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY BER monitor
*/
module taxi_eth_phy_10g_rx_ber_mon #
(
parameter HDR_W = 2,
parameter COUNT_125US = 125000/6.4
)
(
input wire logic clk,
input wire logic rst,
/*
* SERDES interface
*/
input wire logic [HDR_W-1:0] serdes_rx_hdr,
/*
* Status
*/
output wire logic rx_high_ber
);
// check configuration
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
localparam COUNT_W = $clog2($rtoi(COUNT_125US)+1);
localparam logic [COUNT_W-1:0] COUNT_125US_INT = COUNT_W'($rtoi(COUNT_125US));
localparam [1:0]
SYNC_DATA = 2'b10,
SYNC_CTRL = 2'b01;
logic [COUNT_W-1:0] time_count_reg = COUNT_125US_INT, time_count_next;
logic [3:0] ber_count_reg = 4'd0, ber_count_next;
logic rx_high_ber_reg = 1'b0, rx_high_ber_next;
assign rx_high_ber = rx_high_ber_reg;
always_comb 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_INT;
end
end
always_ff @(posedge clk) begin
time_count_reg <= time_count_next;
ber_count_reg <= ber_count_next;
rx_high_ber_reg <= rx_high_ber_next;
if (rst) begin
time_count_reg <= COUNT_125US_INT;
ber_count_reg <= 4'd0;
rx_high_ber_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,126 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY frame sync
*/
module taxi_eth_phy_10g_rx_frame_sync #
(
parameter HDR_W = 2,
parameter BITSLIP_HIGH_CYCLES = 1,
parameter BITSLIP_LOW_CYCLES = 7
)
(
input wire logic clk,
input wire logic rst,
/*
* SERDES interface
*/
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_bitslip,
/*
* Status
*/
output wire logic rx_block_lock
);
localparam BITSLIP_MAX_CYCLES = BITSLIP_HIGH_CYCLES > BITSLIP_LOW_CYCLES ? BITSLIP_HIGH_CYCLES : BITSLIP_LOW_CYCLES;
localparam BITSLIP_COUNT_W = $clog2(BITSLIP_MAX_CYCLES);
// check configuration
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
localparam [1:0]
SYNC_DATA = 2'b10,
SYNC_CTRL = 2'b01;
logic [5:0] sh_count_reg = 6'd0, sh_count_next;
logic [3:0] sh_invalid_count_reg = 4'd0, sh_invalid_count_next;
logic [BITSLIP_COUNT_W-1:0] bitslip_count_reg = '0, bitslip_count_next;
logic serdes_rx_bitslip_reg = 1'b0, serdes_rx_bitslip_next;
logic 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_comb begin
sh_count_next = sh_count_reg;
sh_invalid_count_next = sh_invalid_count_reg;
bitslip_count_next = bitslip_count_reg;
serdes_rx_bitslip_next = serdes_rx_bitslip_reg;
rx_block_lock_next = rx_block_lock_reg;
if (bitslip_count_reg != 0) begin
bitslip_count_next = bitslip_count_reg-1;
end else if (serdes_rx_bitslip_reg) begin
serdes_rx_bitslip_next = 1'b0;
bitslip_count_next = BITSLIP_COUNT_W'(BITSLIP_LOW_CYCLES);
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 == 0) 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;
// slip one bit
serdes_rx_bitslip_next = 1'b1;
bitslip_count_next = BITSLIP_COUNT_W'(BITSLIP_HIGH_CYCLES);
end else if (&sh_count_reg) begin
// valid count overflow, reset
sh_count_next = '0;
sh_invalid_count_next = '0;
end
end
end
always_ff @(posedge clk) begin
sh_count_reg <= sh_count_next;
sh_invalid_count_reg <= sh_invalid_count_next;
bitslip_count_reg <= bitslip_count_next;
serdes_rx_bitslip_reg <= serdes_rx_bitslip_next;
rx_block_lock_reg <= rx_block_lock_next;
if (rst) begin
sh_count_reg <= '0;
sh_invalid_count_reg <= '0;
bitslip_count_reg <= '0;
serdes_rx_bitslip_reg <= 1'b0;
rx_block_lock_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,5 @@
taxi_eth_phy_10g_rx_if.sv
taxi_eth_phy_10g_rx_ber_mon.sv
taxi_eth_phy_10g_rx_frame_sync.sv
taxi_eth_phy_10g_rx_watchdog.sv
../lib/taxi/src/lfsr/rtl/taxi_lfsr.sv

View File

@@ -0,0 +1,247 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY RX IF
*/
module taxi_eth_phy_10g_rx_if #
(
parameter DATA_W = 64,
parameter HDR_W = 2,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter SERDES_PIPELINE = 0,
parameter BITSLIP_HIGH_CYCLES = 1,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4
)
(
input wire logic clk,
input wire logic rst,
/*
* 10GBASE-R encoded interface
*/
output wire logic [DATA_W-1:0] encoded_rx_data,
output wire logic [HDR_W-1:0] encoded_rx_hdr,
/*
* SERDES interface
*/
input wire logic [DATA_W-1:0] serdes_rx_data,
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_bitslip,
output wire logic serdes_rx_reset_req,
/*
* Status
*/
input wire logic rx_bad_block,
input wire logic rx_sequence_error,
output wire logic [6:0] rx_error_count,
output wire logic rx_block_lock,
output wire logic rx_high_ber,
output wire logic rx_status,
/*
* Configuration
*/
input wire logic cfg_rx_prbs31_enable
);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
wire [DATA_W-1:0] serdes_rx_data_rev, serdes_rx_data_int;
wire [HDR_W-1:0] serdes_rx_hdr_rev, serdes_rx_hdr_int;
if (BIT_REVERSE) begin
for (genvar n = 0; n < DATA_W; n = n + 1) begin
assign serdes_rx_data_rev[n] = serdes_rx_data[DATA_W-n-1];
end
for (genvar n = 0; n < HDR_W; n = n + 1) begin
assign serdes_rx_hdr_rev[n] = serdes_rx_hdr[HDR_W-n-1];
end
end else begin
assign serdes_rx_data_rev = serdes_rx_data;
assign serdes_rx_hdr_rev = serdes_rx_hdr;
end
if (SERDES_PIPELINE > 0) begin
(* srl_style = "register" *)
logic [DATA_W-1:0] serdes_rx_data_pipe_reg[SERDES_PIPELINE-1:0];
(* srl_style = "register" *)
logic [HDR_W-1:0] serdes_rx_hdr_pipe_reg[SERDES_PIPELINE-1:0];
for (genvar n = 0; n < SERDES_PIPELINE; n = n + 1) begin
initial begin
serdes_rx_data_pipe_reg[n] = '0;
serdes_rx_hdr_pipe_reg[n] = '0;
end
always @(posedge clk) begin
serdes_rx_data_pipe_reg[n] <= n == 0 ? serdes_rx_data_rev : serdes_rx_data_pipe_reg[n-1];
serdes_rx_hdr_pipe_reg[n] <= n == 0 ? serdes_rx_hdr_rev : serdes_rx_hdr_pipe_reg[n-1];
end
end
assign serdes_rx_data_int = serdes_rx_data_pipe_reg[SERDES_PIPELINE-1];
assign serdes_rx_hdr_int = serdes_rx_hdr_pipe_reg[SERDES_PIPELINE-1];
end else begin
assign serdes_rx_data_int = serdes_rx_data_rev;
assign serdes_rx_hdr_int = serdes_rx_hdr_rev;
end
wire [DATA_W-1:0] descrambled_rx_data;
logic [DATA_W-1:0] encoded_rx_data_reg = '0;
logic [HDR_W-1:0] encoded_rx_hdr_reg = '0;
logic [57:0] scrambler_state_reg = {58{1'b1}};
wire [57:0] scrambler_state;
logic [30:0] prbs31_state_reg = 31'h7fffffff;
wire [30:0] prbs31_state;
wire [DATA_W+HDR_W-1:0] prbs31_data;
logic [DATA_W+HDR_W-1:0] prbs31_data_reg = '0;
logic [6:0] rx_error_count_reg = '0;
logic [5:0] rx_error_count_1_reg = '0;
logic [5:0] rx_error_count_2_reg = '0;
logic [5:0] rx_error_count_1_temp;
logic [5:0] rx_error_count_2_temp;
taxi_lfsr #(
.LFSR_W(58),
.LFSR_POLY(58'h8000000001),
.LFSR_GALOIS(0),
.LFSR_FEED_FORWARD(1),
.REVERSE(1),
.DATA_W(DATA_W)
)
descrambler_inst (
.data_in(serdes_rx_data_int),
.state_in(scrambler_state_reg),
.data_out(descrambled_rx_data),
.state_out(scrambler_state)
);
taxi_lfsr #(
.LFSR_W(31),
.LFSR_POLY(31'h10000001),
.LFSR_GALOIS(0),
.LFSR_FEED_FORWARD(1),
.REVERSE(1),
.DATA_W(DATA_W+HDR_W)
)
prbs31_check_inst (
.data_in(~{serdes_rx_data_int, serdes_rx_hdr_int}),
.state_in(prbs31_state_reg),
.data_out(prbs31_data),
.state_out(prbs31_state)
);
always_comb begin
rx_error_count_1_temp = '0;
rx_error_count_2_temp = '0;
for (integer i = 0; i < DATA_W+HDR_W; i = i + 1) begin
if (i[0]) begin
rx_error_count_1_temp = rx_error_count_1_temp + 6'(prbs31_data_reg[i]);
end else begin
rx_error_count_2_temp = rx_error_count_2_temp + 6'(prbs31_data_reg[i]);
end
end
end
always_ff @(posedge clk) begin
scrambler_state_reg <= scrambler_state;
encoded_rx_data_reg <= SCRAMBLER_DISABLE ? serdes_rx_data_int : descrambled_rx_data;
encoded_rx_hdr_reg <= serdes_rx_hdr_int;
if (PRBS31_EN) begin
if (cfg_rx_prbs31_enable) begin
prbs31_state_reg <= prbs31_state;
prbs31_data_reg <= prbs31_data;
end else begin
prbs31_data_reg <= '0;
end
rx_error_count_1_reg <= rx_error_count_1_temp;
rx_error_count_2_reg <= rx_error_count_2_temp;
rx_error_count_reg <= rx_error_count_1_reg + rx_error_count_2_reg;
end else begin
rx_error_count_reg <= '0;
end
end
assign encoded_rx_data = encoded_rx_data_reg;
assign encoded_rx_hdr = encoded_rx_hdr_reg;
assign rx_error_count = rx_error_count_reg;
wire serdes_rx_bitslip_int;
wire serdes_rx_reset_req_int;
assign serdes_rx_bitslip = serdes_rx_bitslip_int && !(PRBS31_EN && cfg_rx_prbs31_enable);
assign serdes_rx_reset_req = serdes_rx_reset_req_int && !(PRBS31_EN && cfg_rx_prbs31_enable);
taxi_eth_phy_10g_rx_frame_sync #(
.HDR_W(HDR_W),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES)
)
eth_phy_10g_rx_frame_sync_inst (
.clk(clk),
.rst(rst),
.serdes_rx_hdr(serdes_rx_hdr_int),
.serdes_rx_bitslip(serdes_rx_bitslip_int),
.rx_block_lock(rx_block_lock)
);
taxi_eth_phy_10g_rx_ber_mon #(
.HDR_W(HDR_W),
.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)
);
taxi_eth_phy_10g_rx_watchdog #(
.HDR_W(HDR_W),
.COUNT_125US(COUNT_125US)
)
eth_phy_10g_rx_watchdog_inst (
.clk(clk),
.rst(rst),
.serdes_rx_hdr(serdes_rx_hdr_int),
.serdes_rx_reset_req(serdes_rx_reset_req_int),
.rx_bad_block(rx_bad_block),
.rx_sequence_error(rx_sequence_error),
.rx_block_lock(rx_block_lock),
.rx_high_ber(rx_high_ber),
.rx_status(rx_status)
);
endmodule
`resetall

View File

@@ -0,0 +1,153 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2021-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY serdes watchdog
*/
module taxi_eth_phy_10g_rx_watchdog #
(
parameter HDR_W = 2,
parameter COUNT_125US = 125000/6.4
)
(
input wire logic clk,
input wire logic rst,
/*
* SERDES interface
*/
input wire logic [HDR_W-1:0] serdes_rx_hdr,
output wire logic serdes_rx_reset_req,
/*
* Monitor inputs
*/
input wire logic rx_bad_block,
input wire logic rx_sequence_error,
input wire logic rx_block_lock,
input wire logic rx_high_ber,
/*
* Status
*/
output wire logic rx_status
);
// check configuration
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
localparam COUNT_W = $clog2($rtoi(COUNT_125US)+1);
localparam logic [COUNT_W-1:0] COUNT_125US_INT = COUNT_W'($rtoi(COUNT_125US));
localparam [1:0]
SYNC_DATA = 2'b10,
SYNC_CTRL = 2'b01;
logic [COUNT_W-1:0] time_count_reg = '0, time_count_next;
logic [3:0] error_count_reg = '0, error_count_next;
logic [3:0] status_count_reg = '0, status_count_next;
logic saw_ctrl_sh_reg = 1'b0, saw_ctrl_sh_next;
logic [9:0] block_error_count_reg = '0, block_error_count_next;
logic serdes_rx_reset_req_reg = 1'b0, serdes_rx_reset_req_next;
logic rx_status_reg = 1'b0, rx_status_next;
assign serdes_rx_reset_req = serdes_rx_reset_req_reg;
assign rx_status = rx_status_reg;
always_comb begin
error_count_next = error_count_reg;
status_count_next = status_count_reg;
saw_ctrl_sh_next = saw_ctrl_sh_reg;
block_error_count_next = block_error_count_reg;
serdes_rx_reset_req_next = 1'b0;
rx_status_next = rx_status_reg;
if (rx_block_lock) begin
if (serdes_rx_hdr == SYNC_CTRL) begin
saw_ctrl_sh_next = 1'b1;
end
if ((rx_bad_block || rx_sequence_error) && !(&block_error_count_reg)) begin
block_error_count_next = block_error_count_reg + 1;
end
end else begin
rx_status_next = 1'b0;
status_count_next = '0;
end
if (time_count_reg != 0) begin
time_count_next = time_count_reg-1;
end else begin
time_count_next = COUNT_125US_INT;
if (!saw_ctrl_sh_reg || &block_error_count_reg) begin
error_count_next = error_count_reg + 1;
status_count_next = '0;
end else begin
error_count_next = '0;
if (!(&status_count_reg)) begin
status_count_next = status_count_reg + 1;
end
end
if (&error_count_reg) begin
error_count_next = '0;
serdes_rx_reset_req_next = 1'b1;
end
if (&status_count_reg) begin
rx_status_next = 1'b1;
end
saw_ctrl_sh_next = 1'b0;
block_error_count_next = '0;
end
end
always_ff @(posedge clk) begin
time_count_reg <= time_count_next;
error_count_reg <= error_count_next;
status_count_reg <= status_count_next;
saw_ctrl_sh_reg <= saw_ctrl_sh_next;
block_error_count_reg <= block_error_count_next;
rx_status_reg <= rx_status_next;
if (rst) begin
time_count_reg <= COUNT_125US_INT;
error_count_reg <= '0;
status_count_reg <= '0;
saw_ctrl_sh_reg <= 1'b0;
block_error_count_reg <= '0;
rx_status_reg <= 1'b0;
end
end
always_ff @(posedge clk or posedge rst) begin
if (rst) begin
serdes_rx_reset_req_reg <= 1'b0;
end else begin
serdes_rx_reset_req_reg <= serdes_rx_reset_req_next;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_eth_phy_10g_tx.sv
taxi_eth_phy_10g_tx_if.f
taxi_xgmii_baser_enc_64.sv

View File

@@ -0,0 +1,127 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY TX
*/
module taxi_eth_phy_10g_tx #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter HDR_W = 2,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter SERDES_PIPELINE = 0
)
(
input wire logic clk,
input wire logic rst,
/*
* XGMII interface
*/
input wire logic [DATA_W-1:0] xgmii_txd,
input wire logic [CTRL_W-1:0] xgmii_txc,
/*
* SERDES interface
*/
output wire logic [DATA_W-1:0] serdes_tx_data,
output wire logic [HDR_W-1:0] serdes_tx_hdr,
/*
* Status
*/
output wire logic tx_bad_block,
/*
* Configuration
*/
input wire logic cfg_tx_prbs31_enable
);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64");
if (CTRL_W * 8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
wire [DATA_W-1:0] encoded_tx_data;
wire [HDR_W-1:0] encoded_tx_hdr;
taxi_xgmii_baser_enc_64 #(
.DATA_W(DATA_W),
.CTRL_W(CTRL_W),
.HDR_W(HDR_W)
)
xgmii_baser_enc_inst (
.clk(clk),
.rst(rst),
/*
* XGMII interface
*/
.xgmii_txd(xgmii_txd),
.xgmii_txc(xgmii_txc),
/*
* 10GBASE-R encoded interface
*/
.encoded_tx_data(encoded_tx_data),
.encoded_tx_hdr(encoded_tx_hdr),
/*
* Status
*/
.tx_bad_block(tx_bad_block)
);
taxi_eth_phy_10g_tx_if #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.BIT_REVERSE(BIT_REVERSE),
.SCRAMBLER_DISABLE(SCRAMBLER_DISABLE),
.PRBS31_EN(PRBS31_EN),
.SERDES_PIPELINE(SERDES_PIPELINE)
)
eth_phy_10g_tx_if_inst (
.clk(clk),
.rst(rst),
/*
* 10GBASE-R encoded interface
*/
.encoded_tx_data(encoded_tx_data),
.encoded_tx_hdr(encoded_tx_hdr),
/*
* SERDES interface
*/
.serdes_tx_data(serdes_tx_data),
.serdes_tx_hdr(serdes_tx_hdr),
/*
* Configuration
*/
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable)
);
endmodule
`resetall

View File

@@ -0,0 +1,2 @@
taxi_eth_phy_10g_tx_if.sv
../lib/taxi/src/lfsr/rtl/taxi_lfsr.sv

View File

@@ -0,0 +1,154 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* 10G Ethernet PHY TX IF
*/
module taxi_eth_phy_10g_tx_if #
(
parameter DATA_W = 64,
parameter HDR_W = 2,
parameter logic BIT_REVERSE = 1'b0,
parameter logic SCRAMBLER_DISABLE = 1'b0,
parameter logic PRBS31_EN = 1'b0,
parameter SERDES_PIPELINE = 0
)
(
input wire logic clk,
input wire logic rst,
/*
* 10GBASE-R encoded interface
*/
input wire logic [DATA_W-1:0] encoded_tx_data,
input wire logic [HDR_W-1:0] encoded_tx_hdr,
/*
* SERDES interface
*/
output wire logic [DATA_W-1:0] serdes_tx_data,
output wire logic [HDR_W-1:0] serdes_tx_hdr,
/*
* Configuration
*/
input wire logic cfg_tx_prbs31_enable
);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
logic [57:0] scrambler_state_reg = '1;
wire [57:0] scrambler_state;
wire [DATA_W-1:0] scrambled_data;
logic [30:0] prbs31_state_reg = 31'h7fffffff;
wire [30:0] prbs31_state;
wire [DATA_W+HDR_W-1:0] prbs31_data;
logic [DATA_W-1:0] serdes_tx_data_reg = '0;
logic [HDR_W-1:0] serdes_tx_hdr_reg = '0;
wire [DATA_W-1:0] serdes_tx_data_int;
wire [HDR_W-1:0] serdes_tx_hdr_int;
if (BIT_REVERSE) begin
for (genvar n = 0; n < DATA_W; n = n + 1) begin
assign serdes_tx_data_int[n] = serdes_tx_data_reg[DATA_W-n-1];
end
for (genvar n = 0; n < HDR_W; n = n + 1) begin
assign serdes_tx_hdr_int[n] = serdes_tx_hdr_reg[HDR_W-n-1];
end
end else begin
assign serdes_tx_data_int = serdes_tx_data_reg;
assign serdes_tx_hdr_int = serdes_tx_hdr_reg;
end
if (SERDES_PIPELINE > 0) begin
(* srl_style = "register" *)
reg [DATA_W-1:0] serdes_tx_data_pipe_reg[SERDES_PIPELINE-1:0];
(* srl_style = "register" *)
reg [HDR_W-1:0] serdes_tx_hdr_pipe_reg[SERDES_PIPELINE-1:0];
for (genvar n = 0; n < SERDES_PIPELINE; n = n + 1) begin
initial begin
serdes_tx_data_pipe_reg[n] = '0;
serdes_tx_hdr_pipe_reg[n] = '0;
end
always @(posedge clk) begin
serdes_tx_data_pipe_reg[n] <= n == 0 ? serdes_tx_data_int : serdes_tx_data_pipe_reg[n-1];
serdes_tx_hdr_pipe_reg[n] <= n == 0 ? serdes_tx_hdr_int : serdes_tx_hdr_pipe_reg[n-1];
end
end
assign serdes_tx_data = serdes_tx_data_pipe_reg[SERDES_PIPELINE-1];
assign serdes_tx_hdr = serdes_tx_hdr_pipe_reg[SERDES_PIPELINE-1];
end else begin
assign serdes_tx_data = serdes_tx_data_int;
assign serdes_tx_hdr = serdes_tx_hdr_int;
end
taxi_lfsr #(
.LFSR_W(58),
.LFSR_POLY(58'h8000000001),
.LFSR_GALOIS(0),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(DATA_W)
)
scrambler_inst (
.data_in(encoded_tx_data),
.state_in(scrambler_state_reg),
.data_out(scrambled_data),
.state_out(scrambler_state)
);
taxi_lfsr #(
.LFSR_W(31),
.LFSR_POLY(31'h10000001),
.LFSR_GALOIS(0),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_W(DATA_W+HDR_W)
)
prbs31_gen_inst (
.data_in('0),
.state_in(prbs31_state_reg),
.data_out(prbs31_data),
.state_out(prbs31_state)
);
always_ff @(posedge clk) begin
scrambler_state_reg <= scrambler_state;
if (PRBS31_EN && cfg_tx_prbs31_enable) begin
prbs31_state_reg <= prbs31_state;
serdes_tx_data_reg <= ~prbs31_data[DATA_W+HDR_W-1:HDR_W];
serdes_tx_hdr_reg <= ~prbs31_data[HDR_W-1:0];
end else begin
serdes_tx_data_reg <= SCRAMBLER_DISABLE ? encoded_tx_data : scrambled_data;
serdes_tx_hdr_reg <= encoded_tx_hdr;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,5 @@
taxi_gmii_phy_if.sv
../lib/taxi/src/io/rtl/taxi_ssio_sdr_in.sv
../lib/taxi/src/io/rtl/taxi_ssio_sdr_out.sv
../lib/taxi/src/io/rtl/taxi_oddr.sv
../lib/taxi/src/sync/rtl/taxi_sync_reset.sv

View File

@@ -0,0 +1,129 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* GMII PHY interface
*/
module taxi_gmii_phy_if #
(
// simulation (set to avoid vendor primitives)
parameter logic SIM = 1'b0,
// vendor ("GENERIC", "XILINX", "ALTERA")
parameter VENDOR = "XILINX",
// device family
parameter FAMILY = "virtex7"
)
(
input wire logic gtx_clk,
input wire logic gtx_rst,
/*
* GMII interface to MAC
*/
output wire logic mac_gmii_rx_clk,
output wire logic mac_gmii_rx_rst,
output wire logic [7:0] mac_gmii_rxd,
output wire logic mac_gmii_rx_dv,
output wire logic mac_gmii_rx_er,
output wire logic mac_gmii_tx_clk,
output wire logic mac_gmii_tx_rst,
input wire logic [7:0] mac_gmii_txd,
input wire logic mac_gmii_tx_en,
input wire logic mac_gmii_tx_er,
/*
* GMII interface to PHY
*/
input wire logic phy_gmii_rx_clk,
input wire logic [7:0] phy_gmii_rxd,
input wire logic phy_gmii_rx_dv,
input wire logic phy_gmii_rx_er,
input wire logic phy_mii_tx_clk,
output wire logic phy_gmii_tx_clk,
output wire logic [7:0] phy_gmii_txd,
output wire logic phy_gmii_tx_en,
output wire logic phy_gmii_tx_er,
/*
* Control
*/
input wire logic mii_select
);
taxi_ssio_sdr_in #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.WIDTH(10)
)
rx_ssio_sdr_inst (
.input_clk(phy_gmii_rx_clk),
.input_d({phy_gmii_rxd, phy_gmii_rx_dv, phy_gmii_rx_er}),
.output_clk(mac_gmii_rx_clk),
.output_q({mac_gmii_rxd, mac_gmii_rx_dv, mac_gmii_rx_er})
);
taxi_ssio_sdr_out #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.WIDTH(10)
)
tx_ssio_sdr_inst (
.clk(mac_gmii_tx_clk),
.input_d({mac_gmii_txd, mac_gmii_tx_en, mac_gmii_tx_er}),
.output_clk(phy_gmii_tx_clk),
.output_q({phy_gmii_txd, phy_gmii_tx_en, phy_gmii_tx_er})
);
if (!SIM && VENDOR == "XILINX") begin
// Xilinx/AMD device support
BUFGMUX
gmii_bufgmux_inst (
.I0(gtx_clk),
.I1(phy_mii_tx_clk),
.S(mii_select),
.O(mac_gmii_tx_clk)
);
end else begin
// generic/simulation implementation (no vendor primitives)
assign mac_gmii_tx_clk = mii_select ? phy_mii_tx_clk : gtx_clk;
end
// reset sync
taxi_sync_reset #(
.N(4)
)
tx_reset_sync_inst (
.clk(mac_gmii_tx_clk),
.rst(gtx_rst),
.out(mac_gmii_tx_rst)
);
taxi_sync_reset #(
.N(4)
)
rx_reset_sync_inst (
.clk(mac_gmii_rx_clk),
.rst(gtx_rst),
.out(mac_gmii_rx_rst)
);
endmodule
`resetall

View File

@@ -0,0 +1,421 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2023-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* MAC control receiver
*/
module taxi_mac_ctrl_rx #
(
parameter ID_W = 8,
parameter DEST_W = 8,
parameter USER_W = 1,
parameter logic USE_READY = 1'b0,
parameter MCF_PARAMS_SIZE = 18
)
(
input wire logic clk,
input wire logic rst,
/*
* AXI4-Stream input (sink)
*/
taxi_axis_if.snk s_axis,
/*
* AXI4-Stream output (source)
*/
taxi_axis_if.src m_axis,
/*
* MAC control frame interface
*/
output wire logic mcf_valid,
output wire logic [47:0] mcf_eth_dst,
output wire logic [47:0] mcf_eth_src,
output wire logic [15:0] mcf_eth_type,
output wire logic [15:0] mcf_opcode,
output wire logic [MCF_PARAMS_SIZE*8-1:0] mcf_params,
output wire logic [ID_W-1:0] mcf_id,
output wire logic [DEST_W-1:0] mcf_dest,
output wire logic [USER_W-1:0] mcf_user,
/*
* Configuration
*/
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast,
input wire logic cfg_mcf_rx_check_eth_dst_mcast,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast,
input wire logic cfg_mcf_rx_check_eth_dst_ucast,
input wire logic [47:0] cfg_mcf_rx_eth_src,
input wire logic cfg_mcf_rx_check_eth_src,
input wire logic [15:0] cfg_mcf_rx_eth_type,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc,
input wire logic cfg_mcf_rx_check_opcode_lfc,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc,
input wire logic cfg_mcf_rx_check_opcode_pfc,
input wire logic cfg_mcf_rx_forward,
input wire logic cfg_mcf_rx_enable,
/*
* Status
*/
output wire logic stat_rx_mcf
);
// extract parameters
localparam DATA_W = s_axis.DATA_W;
localparam logic KEEP_EN = s_axis.KEEP_EN && m_axis.KEEP_EN;
localparam KEEP_W = s_axis.KEEP_W;
localparam logic STRB_EN = s_axis.STRB_EN && m_axis.STRB_EN;
localparam logic LAST_EN = s_axis.LAST_EN && m_axis.LAST_EN;
localparam logic ID_EN = s_axis.ID_EN && m_axis.ID_EN;
localparam logic DEST_EN = s_axis.DEST_EN && m_axis.DEST_EN;
localparam logic USER_EN = s_axis.USER_EN && m_axis.USER_EN;
localparam BYTE_LANES = KEEP_EN ? KEEP_W : 1;
localparam HDR_SIZE = 60;
localparam CYCLE_CNT = (HDR_SIZE+BYTE_LANES-1)/BYTE_LANES;
localparam PTR_W = $clog2(CYCLE_CNT+1);
localparam OFFSET = HDR_SIZE % BYTE_LANES;
// check configuration
if (BYTE_LANES * 8 != DATA_W)
$fatal(0, "Error: AXI stream interface requires byte (8-bit) granularity (instance %m)");
if (MCF_PARAMS_SIZE > 44)
$fatal(0, "Error: Maximum MCF_PARAMS_SIZE is 44 bytes (instance %m)");
if (m_axis.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (KEEP_EN && m_axis.KEEP_W != KEEP_W)
$fatal(0, "Error: Interface KEEP_W parameter mismatch (instance %m)");
/*
MAC control frame
Field Length
Destination MAC address 6 octets [01:80:C2:00:00:01]
Source MAC address 6 octets
Ethertype 2 octets [0x8808]
Opcode 2 octets
Parameters 0-44 octets
This module manages the reception of MAC control frames. Incoming frames are
checked based on the ethertype and (optionally) MAC addresses. Matching control
frames are marked by setting tuser[0] on the data output and forwarded through
a separate interface for processing.
*/
logic read_mcf_reg = 1'b1, read_mcf_next;
logic mcf_frame_reg = 1'b0, mcf_frame_next;
logic [PTR_W-1:0] ptr_reg = 0, ptr_next;
logic s_axis_tready_reg = 1'b0, s_axis_tready_next;
// internal datapath
logic [DATA_W-1:0] m_axis_tdata_int;
logic [KEEP_W-1:0] m_axis_tkeep_int;
logic m_axis_tvalid_int;
logic m_axis_tready_int_reg = 1'b0;
logic m_axis_tlast_int;
logic [ID_W-1:0] m_axis_tid_int;
logic [DEST_W-1:0] m_axis_tdest_int;
logic [USER_W-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
logic mcf_valid_reg = 0, mcf_valid_next;
logic [47:0] mcf_eth_dst_reg = 0, mcf_eth_dst_next;
logic [47:0] mcf_eth_src_reg = 0, mcf_eth_src_next;
logic [15:0] mcf_eth_type_reg = 0, mcf_eth_type_next;
logic [15:0] mcf_opcode_reg = 0, mcf_opcode_next;
logic [MCF_PARAMS_SIZE*8-1:0] mcf_params_reg = 0, mcf_params_next;
logic [ID_W-1:0] mcf_id_reg = 0, mcf_id_next;
logic [DEST_W-1:0] mcf_dest_reg = 0, mcf_dest_next;
logic [USER_W-1:0] mcf_user_reg = 0, mcf_user_next;
logic stat_rx_mcf_reg = 1'b0, stat_rx_mcf_next;
assign s_axis.tready = s_axis_tready_reg;
assign mcf_valid = mcf_valid_reg;
assign mcf_eth_dst = mcf_eth_dst_reg;
assign mcf_eth_src = mcf_eth_src_reg;
assign mcf_eth_type = mcf_eth_type_reg;
assign mcf_opcode = mcf_opcode_reg;
assign mcf_params = mcf_params_reg;
assign mcf_id = mcf_id_reg;
assign mcf_dest = mcf_dest_reg;
assign mcf_user = mcf_user_reg;
assign stat_rx_mcf = stat_rx_mcf_reg;
wire mcf_eth_dst_mcast_match = mcf_eth_dst_next == cfg_mcf_rx_eth_dst_mcast;
wire mcf_eth_dst_ucast_match = mcf_eth_dst_next == cfg_mcf_rx_eth_dst_ucast;
wire mcf_eth_src_match = mcf_eth_src_next == cfg_mcf_rx_eth_src;
wire mcf_eth_type_match = mcf_eth_type_next == cfg_mcf_rx_eth_type;
wire mcf_opcode_lfc_match = mcf_opcode_next == cfg_mcf_rx_opcode_lfc;
wire mcf_opcode_pfc_match = mcf_opcode_next == cfg_mcf_rx_opcode_pfc;
wire mcf_eth_dst_match = ((mcf_eth_dst_mcast_match && cfg_mcf_rx_check_eth_dst_mcast) ||
(mcf_eth_dst_ucast_match && cfg_mcf_rx_check_eth_dst_ucast) ||
(!cfg_mcf_rx_check_eth_dst_mcast && !cfg_mcf_rx_check_eth_dst_ucast));
wire mcf_opcode_match = ((mcf_opcode_lfc_match && cfg_mcf_rx_check_opcode_lfc) ||
(mcf_opcode_pfc_match && cfg_mcf_rx_check_opcode_pfc) ||
(!cfg_mcf_rx_check_opcode_lfc && !cfg_mcf_rx_check_opcode_pfc));
wire mcf_match = (mcf_eth_dst_match &&
(mcf_eth_src_match || !cfg_mcf_rx_check_eth_src) &&
mcf_eth_type_match && mcf_opcode_match);
always_comb begin
read_mcf_next = read_mcf_reg;
mcf_frame_next = mcf_frame_reg;
ptr_next = ptr_reg;
// pass through data
m_axis_tdata_int = s_axis.tdata;
m_axis_tkeep_int = s_axis.tkeep;
m_axis_tvalid_int = s_axis.tvalid;
m_axis_tlast_int = s_axis.tlast;
m_axis_tid_int = s_axis.tid;
m_axis_tdest_int = s_axis.tdest;
m_axis_tuser_int = USER_W'(s_axis.tuser);
s_axis_tready_next = m_axis_tready_int_early || !USE_READY;
mcf_valid_next = 1'b0;
mcf_eth_dst_next = mcf_eth_dst_reg;
mcf_eth_src_next = mcf_eth_src_reg;
mcf_eth_type_next = mcf_eth_type_reg;
mcf_opcode_next = mcf_opcode_reg;
mcf_params_next = mcf_params_reg;
mcf_id_next = mcf_id_reg;
mcf_dest_next = mcf_dest_reg;
mcf_user_next = mcf_user_reg;
stat_rx_mcf_next = 1'b0;
if ((s_axis.tready || !USE_READY) && s_axis.tvalid) begin
if (read_mcf_reg) begin
ptr_next = ptr_reg + 1;
mcf_id_next = s_axis.tid;
mcf_dest_next = s_axis.tdest;
mcf_user_next = s_axis.tuser;
`define _HEADER_FIELD_(offset, field) \
if (ptr_reg == PTR_W'(offset/BYTE_LANES)) begin \
field = s_axis.tdata[(offset%BYTE_LANES)*8 +: 8]; \
end
`_HEADER_FIELD_(0, mcf_eth_dst_next[5*8 +: 8])
`_HEADER_FIELD_(1, mcf_eth_dst_next[4*8 +: 8])
`_HEADER_FIELD_(2, mcf_eth_dst_next[3*8 +: 8])
`_HEADER_FIELD_(3, mcf_eth_dst_next[2*8 +: 8])
`_HEADER_FIELD_(4, mcf_eth_dst_next[1*8 +: 8])
`_HEADER_FIELD_(5, mcf_eth_dst_next[0*8 +: 8])
`_HEADER_FIELD_(6, mcf_eth_src_next[5*8 +: 8])
`_HEADER_FIELD_(7, mcf_eth_src_next[4*8 +: 8])
`_HEADER_FIELD_(8, mcf_eth_src_next[3*8 +: 8])
`_HEADER_FIELD_(9, mcf_eth_src_next[2*8 +: 8])
`_HEADER_FIELD_(10, mcf_eth_src_next[1*8 +: 8])
`_HEADER_FIELD_(11, mcf_eth_src_next[0*8 +: 8])
`_HEADER_FIELD_(12, mcf_eth_type_next[1*8 +: 8])
`_HEADER_FIELD_(13, mcf_eth_type_next[0*8 +: 8])
`_HEADER_FIELD_(14, mcf_opcode_next[1*8 +: 8])
`_HEADER_FIELD_(15, mcf_opcode_next[0*8 +: 8])
if (ptr_reg == PTR_W'(0/BYTE_LANES)) begin
// ensure params field gets cleared
mcf_params_next = 0;
end
for (integer k = 0; k < MCF_PARAMS_SIZE; k = k + 1) begin
if (ptr_reg == PTR_W'((16+k)/BYTE_LANES)) begin
mcf_params_next[k*8 +: 8] = s_axis.tdata[((16+k)%BYTE_LANES)*8 +: 8];
end
end
if (ptr_reg == PTR_W'(15/BYTE_LANES) && (!KEEP_EN || s_axis.tkeep[13%BYTE_LANES])) begin
// record match at end of opcode field
mcf_frame_next = mcf_match && cfg_mcf_rx_enable;
end
if (ptr_reg == PTR_W'((HDR_SIZE-1)/BYTE_LANES)) begin
read_mcf_next = 1'b0;
end
`undef _HEADER_FIELD_
end
if (s_axis.tlast) begin
if (s_axis.tuser[0]) begin
// frame marked invalid
end else if (mcf_frame_next) begin
if (!cfg_mcf_rx_forward) begin
// mark frame invalid
m_axis_tuser_int[0] = 1'b1;
end
// transfer out MAC control frame
mcf_valid_next = 1'b1;
stat_rx_mcf_next = 1'b1;
end
read_mcf_next = 1'b1;
mcf_frame_next = 1'b0;
ptr_next = '0;
end
end
end
always_ff @(posedge clk) begin
read_mcf_reg <= read_mcf_next;
mcf_frame_reg <= mcf_frame_next;
ptr_reg <= ptr_next;
s_axis_tready_reg <= s_axis_tready_next;
mcf_valid_reg <= mcf_valid_next;
mcf_eth_dst_reg <= mcf_eth_dst_next;
mcf_eth_src_reg <= mcf_eth_src_next;
mcf_eth_type_reg <= mcf_eth_type_next;
mcf_opcode_reg <= mcf_opcode_next;
mcf_params_reg <= mcf_params_next;
mcf_id_reg <= mcf_id_next;
mcf_dest_reg <= mcf_dest_next;
mcf_user_reg <= mcf_user_next;
stat_rx_mcf_reg <= stat_rx_mcf_next;
if (rst) begin
read_mcf_reg <= 1'b1;
mcf_frame_reg <= 1'b0;
ptr_reg <= '0;
s_axis_tready_reg <= 1'b0;
mcf_valid_reg <= 1'b0;
stat_rx_mcf_reg <= 1'b0;
end
end
// output datapath logic
reg [DATA_W-1:0] m_axis_tdata_reg = '0;
reg [KEEP_W-1:0] m_axis_tkeep_reg = '0;
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_W-1:0] m_axis_tid_reg = '0;
reg [DEST_W-1:0] m_axis_tdest_reg = '0;
reg [USER_W-1:0] m_axis_tuser_reg = '0;
reg [DATA_W-1:0] temp_m_axis_tdata_reg = '0;
reg [KEEP_W-1:0] temp_m_axis_tkeep_reg = '0;
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_W-1:0] temp_m_axis_tid_reg = '0;
reg [DEST_W-1:0] temp_m_axis_tdest_reg = '0;
reg [USER_W-1:0] temp_m_axis_tuser_reg = '0;
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis.tdata = m_axis_tdata_reg;
assign m_axis.tkeep = KEEP_EN ? m_axis_tkeep_reg : '1;
assign m_axis.tstrb = m_axis.tkeep;
assign m_axis.tvalid = m_axis_tvalid_reg;
assign m_axis.tlast = m_axis_tlast_reg;
assign m_axis.tid = ID_EN ? m_axis_tid_reg : '0;
assign m_axis.tdest = DEST_EN ? m_axis_tdest_reg : '0;
assign m_axis.tuser = USER_EN ? m_axis_tuser_reg : '0;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis.tready || !USE_READY || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always_comb begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis.tready || !USE_READY || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis.tready || !USE_READY) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always_ff @(posedge clk) begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tkeep_reg <= m_axis_tkeep_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tid_reg <= m_axis_tid_int;
m_axis_tdest_reg <= m_axis_tdest_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tkeep_reg <= m_axis_tkeep_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tid_reg <= m_axis_tid_int;
temp_m_axis_tdest_reg <= m_axis_tdest_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,394 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2023-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* MAC control transmitter
*/
module taxi_mac_ctrl_tx #
(
parameter ID_W = 8,
parameter DEST_W = 8,
parameter USER_W = 1,
parameter MCF_PARAMS_SIZE = 18
)
(
input wire logic clk,
input wire logic rst,
/*
* AXI4-Stream input (sink)
*/
taxi_axis_if.snk s_axis,
/*
* AXI4-Stream output (source)
*/
taxi_axis_if.src m_axis,
/*
* MAC control frame interface
*/
input wire logic mcf_valid,
output wire logic mcf_ready,
input wire logic [47:0] mcf_eth_dst,
input wire logic [47:0] mcf_eth_src,
input wire logic [15:0] mcf_eth_type,
input wire logic [15:0] mcf_opcode,
input wire logic [MCF_PARAMS_SIZE*8-1:0] mcf_params,
input wire logic [ID_W-1:0] mcf_id,
input wire logic [DEST_W-1:0] mcf_dest,
input wire logic [USER_W-1:0] mcf_user,
/*
* Pause interface
*/
input wire logic tx_pause_req,
output wire logic tx_pause_ack,
/*
* Status
*/
output wire logic stat_tx_mcf
);
// extract parameters
localparam DATA_W = s_axis.DATA_W;
localparam logic KEEP_EN = s_axis.KEEP_EN && m_axis.KEEP_EN;
localparam KEEP_W = s_axis.KEEP_W;
localparam logic STRB_EN = s_axis.STRB_EN && m_axis.STRB_EN;
localparam logic LAST_EN = s_axis.LAST_EN && m_axis.LAST_EN;
localparam logic ID_EN = s_axis.ID_EN && m_axis.ID_EN;
localparam logic DEST_EN = s_axis.DEST_EN && m_axis.DEST_EN;
localparam logic USER_EN = s_axis.USER_EN && m_axis.USER_EN;
localparam BYTE_LANES = KEEP_EN ? KEEP_W : 1;
localparam HDR_SIZE = 60;
localparam CYCLE_CNT = (HDR_SIZE+BYTE_LANES-1)/BYTE_LANES;
localparam PTR_W = $clog2(CYCLE_CNT+1);
localparam OFFSET = HDR_SIZE % BYTE_LANES;
// check configuration
if (BYTE_LANES * 8 != DATA_W)
$fatal(0, "Error: AXI stream interface requires byte (8-bit) granularity (instance %m)");
if (MCF_PARAMS_SIZE > 44)
$fatal(0, "Error: Maximum MCF_PARAMS_SIZE is 44 bytes (instance %m)");
if (m_axis.DATA_W != DATA_W)
$fatal(0, "Error: Interface DATA_W parameter mismatch (instance %m)");
if (KEEP_EN && m_axis.KEEP_W != KEEP_W)
$fatal(0, "Error: Interface KEEP_W parameter mismatch (instance %m)");
/*
MAC control frame
Field Length
Destination MAC address 6 octets [01:80:C2:00:00:01]
Source MAC address 6 octets
Ethertype 2 octets [0x8808]
Opcode 2 octets
Parameters 0-44 octets
This module manages the transmission of MAC control frames. Control frames
are accepted in parallel, serialized, and merged at a higher priority with
data traffic.
*/
logic send_data_reg = 1'b0, send_data_next;
logic send_mcf_reg = 1'b0, send_mcf_next;
logic [PTR_W-1:0] ptr_reg = 0, ptr_next;
logic s_axis_tready_reg = 1'b0, s_axis_tready_next;
logic mcf_ready_reg = 1'b0, mcf_ready_next;
logic tx_pause_ack_reg = 1'b0, tx_pause_ack_next;
logic stat_tx_mcf_reg = 1'b0, stat_tx_mcf_next;
// internal datapath
logic [DATA_W-1:0] m_axis_tdata_int;
logic [KEEP_W-1:0] m_axis_tkeep_int;
logic m_axis_tvalid_int;
logic m_axis_tready_int_reg = 1'b0;
logic m_axis_tlast_int;
logic [ID_W-1:0] m_axis_tid_int;
logic [DEST_W-1:0] m_axis_tdest_int;
logic [USER_W-1:0] m_axis_tuser_int;
wire m_axis_tready_int_early;
assign s_axis.tready = s_axis_tready_reg;
assign mcf_ready = mcf_ready_reg;
assign tx_pause_ack = tx_pause_ack_reg;
assign stat_tx_mcf = stat_tx_mcf_reg;
always_comb begin
send_data_next = send_data_reg;
send_mcf_next = send_mcf_reg;
ptr_next = ptr_reg;
s_axis_tready_next = 1'b0;
mcf_ready_next = 1'b0;
tx_pause_ack_next = tx_pause_ack_reg;
stat_tx_mcf_next = 1'b0;
m_axis_tdata_int = 0;
m_axis_tkeep_int = 0;
m_axis_tvalid_int = 1'b0;
m_axis_tlast_int = 1'b0;
m_axis_tid_int = 0;
m_axis_tdest_int = 0;
m_axis_tuser_int = 0;
if (!send_data_reg && !send_mcf_reg) begin
m_axis_tdata_int = s_axis.tdata;
m_axis_tkeep_int = s_axis.tkeep;
m_axis_tvalid_int = 1'b0;
m_axis_tlast_int = s_axis.tlast;
m_axis_tid_int = s_axis.tid;
m_axis_tdest_int = s_axis.tdest;
m_axis_tuser_int = USER_W'(s_axis.tuser);
s_axis_tready_next = m_axis_tready_int_early && !tx_pause_req;
tx_pause_ack_next = tx_pause_req;
if (s_axis.tvalid && s_axis.tready) begin
s_axis_tready_next = m_axis_tready_int_early;
tx_pause_ack_next = 1'b0;
m_axis_tvalid_int = 1'b1;
if (s_axis.tlast) begin
s_axis_tready_next = m_axis_tready_int_early && !mcf_valid && !mcf_ready;
send_data_next = 1'b0;
end else begin
send_data_next = 1'b1;
end
end else if (mcf_valid) begin
s_axis_tready_next = 1'b0;
ptr_next = 0;
send_mcf_next = 1'b1;
mcf_ready_next = (CYCLE_CNT == 1) && m_axis_tready_int_early;
end
end
if (send_data_reg) begin
m_axis_tdata_int = s_axis.tdata;
m_axis_tkeep_int = s_axis.tkeep;
m_axis_tvalid_int = 1'b0;
m_axis_tlast_int = s_axis.tlast;
m_axis_tid_int = s_axis.tid;
m_axis_tdest_int = s_axis.tdest;
m_axis_tuser_int = USER_W'(s_axis.tuser);
s_axis_tready_next = m_axis_tready_int_early;
if (s_axis.tvalid && s_axis.tready) begin
m_axis_tvalid_int = 1'b1;
if (s_axis.tlast) begin
s_axis_tready_next = m_axis_tready_int_early && !tx_pause_req;
send_data_next = 1'b0;
if (mcf_valid) begin
s_axis_tready_next = 1'b0;
ptr_next = 0;
send_mcf_next = 1'b1;
mcf_ready_next = (CYCLE_CNT == 1) && m_axis_tready_int_early;
end
end else begin
send_data_next = 1'b1;
end
end
end
if (send_mcf_reg) begin
mcf_ready_next = (CYCLE_CNT == 1 || ptr_reg == PTR_W'(CYCLE_CNT-1)) && m_axis_tready_int_early;
if (m_axis_tready_int_reg) begin
ptr_next = ptr_reg + 1;
m_axis_tvalid_int = 1'b1;
m_axis_tid_int = mcf_id;
m_axis_tdest_int = mcf_dest;
m_axis_tuser_int = mcf_user;
`define _HEADER_FIELD_(offset, field) \
if (ptr_reg == PTR_W'(offset/BYTE_LANES)) begin \
m_axis_tdata_int[(offset%BYTE_LANES)*8 +: 8] = field; \
m_axis_tkeep_int[offset%BYTE_LANES] = 1'b1; \
end
`_HEADER_FIELD_(0, mcf_eth_dst[5*8 +: 8])
`_HEADER_FIELD_(1, mcf_eth_dst[4*8 +: 8])
`_HEADER_FIELD_(2, mcf_eth_dst[3*8 +: 8])
`_HEADER_FIELD_(3, mcf_eth_dst[2*8 +: 8])
`_HEADER_FIELD_(4, mcf_eth_dst[1*8 +: 8])
`_HEADER_FIELD_(5, mcf_eth_dst[0*8 +: 8])
`_HEADER_FIELD_(6, mcf_eth_src[5*8 +: 8])
`_HEADER_FIELD_(7, mcf_eth_src[4*8 +: 8])
`_HEADER_FIELD_(8, mcf_eth_src[3*8 +: 8])
`_HEADER_FIELD_(9, mcf_eth_src[2*8 +: 8])
`_HEADER_FIELD_(10, mcf_eth_src[1*8 +: 8])
`_HEADER_FIELD_(11, mcf_eth_src[0*8 +: 8])
`_HEADER_FIELD_(12, mcf_eth_type[1*8 +: 8])
`_HEADER_FIELD_(13, mcf_eth_type[0*8 +: 8])
`_HEADER_FIELD_(14, mcf_opcode[1*8 +: 8])
`_HEADER_FIELD_(15, mcf_opcode[0*8 +: 8])
for (integer k = 0; k < HDR_SIZE-16; k = k + 1) begin
if (ptr_reg == PTR_W'((16+k)/BYTE_LANES)) begin
if (k < MCF_PARAMS_SIZE) begin
m_axis_tdata_int[((16+k)%BYTE_LANES)*8 +: 8] = mcf_params[k*8 +: 8];
end else begin
m_axis_tdata_int[((16+k)%BYTE_LANES)*8 +: 8] = 0;
end
m_axis_tkeep_int[(16+k)%BYTE_LANES] = 1'b1;
end
end
if (ptr_reg == PTR_W'((HDR_SIZE-1)/BYTE_LANES)) begin
s_axis_tready_next = m_axis_tready_int_early && !tx_pause_req;
mcf_ready_next = 1'b0;
m_axis_tlast_int = 1'b1;
send_mcf_next = 1'b0;
stat_tx_mcf_next = 1'b1;
end else begin
mcf_ready_next = (ptr_next == PTR_W'(CYCLE_CNT-1)) && m_axis_tready_int_early;
end
`undef _HEADER_FIELD_
end
end
end
always_ff @(posedge clk) begin
send_data_reg <= send_data_next;
send_mcf_reg <= send_mcf_next;
ptr_reg <= ptr_next;
s_axis_tready_reg <= s_axis_tready_next;
mcf_ready_reg <= mcf_ready_next;
tx_pause_ack_reg <= tx_pause_ack_next;
stat_tx_mcf_reg <= stat_tx_mcf_next;
if (rst) begin
send_data_reg <= 1'b0;
send_mcf_reg <= 1'b0;
ptr_reg <= 0;
s_axis_tready_reg <= 1'b0;
mcf_ready_reg <= 1'b0;
tx_pause_ack_reg <= 1'b0;
stat_tx_mcf_reg <= 1'b0;
end
end
// output datapath logic
reg [DATA_W-1:0] m_axis_tdata_reg = '0;
reg [KEEP_W-1:0] m_axis_tkeep_reg = '0;
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg [ID_W-1:0] m_axis_tid_reg = '0;
reg [DEST_W-1:0] m_axis_tdest_reg = '0;
reg [USER_W-1:0] m_axis_tuser_reg = '0;
reg [DATA_W-1:0] temp_m_axis_tdata_reg = '0;
reg [KEEP_W-1:0] temp_m_axis_tkeep_reg = '0;
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg [ID_W-1:0] temp_m_axis_tid_reg = '0;
reg [DEST_W-1:0] temp_m_axis_tdest_reg = '0;
reg [USER_W-1:0] temp_m_axis_tuser_reg = '0;
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis.tdata = m_axis_tdata_reg;
assign m_axis.tkeep = KEEP_EN ? m_axis_tkeep_reg : '1;
assign m_axis.tstrb = m_axis.tkeep;
assign m_axis.tvalid = m_axis_tvalid_reg;
assign m_axis.tlast = m_axis_tlast_reg;
assign m_axis.tid = ID_EN ? m_axis_tid_reg : '0;
assign m_axis.tdest = DEST_EN ? m_axis_tdest_reg : '0;
assign m_axis.tuser = USER_EN ? m_axis_tuser_reg : '0;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis.tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always_comb begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis.tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis.tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always_ff @(posedge clk) begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tkeep_reg <= m_axis_tkeep_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tid_reg <= m_axis_tid_int;
m_axis_tdest_reg <= m_axis_tdest_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tkeep_reg <= temp_m_axis_tkeep_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tid_reg <= temp_m_axis_tid_reg;
m_axis_tdest_reg <= temp_m_axis_tdest_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tkeep_reg <= m_axis_tkeep_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tid_reg <= m_axis_tid_int;
temp_m_axis_tdest_reg <= m_axis_tdest_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,212 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2023-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* PFC and pause frame receive handling
*/
module taxi_mac_pause_ctrl_rx #
(
parameter MCF_PARAMS_SIZE = 18,
parameter logic PFC_EN = 1'b1
)
(
input wire logic clk,
input wire logic rst,
/*
* MAC control frame interface
*/
input wire logic mcf_valid,
input wire logic [47:0] mcf_eth_dst,
input wire logic [47:0] mcf_eth_src,
input wire logic [15:0] mcf_eth_type,
input wire logic [15:0] mcf_opcode,
input wire logic [MCF_PARAMS_SIZE*8-1:0] mcf_params,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic rx_lfc_en,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] rx_pfc_en,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack,
/*
* Configuration
*/
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0,
input wire logic [9:0] cfg_quanta_step,
input wire logic cfg_quanta_clk_en = 1'b1,
/*
* Status
*/
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused
);
localparam QW = 16;
localparam QFB = 8;
// check configuration
if (MCF_PARAMS_SIZE < (PFC_EN ? 18 : 2))
$fatal(0, "Error: MCF_PARAMS_SIZE too small for requested configuration (instance %m)");
logic lfc_req_reg = 1'b0, lfc_req_next;
logic [7:0] pfc_req_reg = 8'd0, pfc_req_next;
logic [QFB-1:0] quanta_cnt_reg = '0, quanta_cnt_next;
logic [1:0] quanta_inc_reg = '0, quanta_inc_next;
logic [QW-1:0] lfc_quanta_reg = '0, lfc_quanta_next;
logic [QW-1:0] pfc_quanta_reg[8], pfc_quanta_next[8];
logic stat_rx_lfc_pkt_reg = 1'b0, stat_rx_lfc_pkt_next;
logic stat_rx_lfc_xon_reg = 1'b0, stat_rx_lfc_xon_next;
logic stat_rx_lfc_xoff_reg = 1'b0, stat_rx_lfc_xoff_next;
logic stat_rx_pfc_pkt_reg = 1'b0, stat_rx_pfc_pkt_next;
logic [7:0] stat_rx_pfc_xon_reg = '0, stat_rx_pfc_xon_next;
logic [7:0] stat_rx_pfc_xoff_reg = '0, stat_rx_pfc_xoff_next;
assign rx_lfc_req = lfc_req_reg;
assign rx_pfc_req = pfc_req_reg;
assign stat_rx_lfc_pkt = stat_rx_lfc_pkt_reg;
assign stat_rx_lfc_xon = stat_rx_lfc_xon_reg;
assign stat_rx_lfc_xoff = stat_rx_lfc_xoff_reg;
assign stat_rx_lfc_paused = lfc_req_reg;
assign stat_rx_pfc_pkt = stat_rx_pfc_pkt_reg;
assign stat_rx_pfc_xon = stat_rx_pfc_xon_reg;
assign stat_rx_pfc_xoff = stat_rx_pfc_xoff_reg;
assign stat_rx_pfc_paused = pfc_req_reg;
initial begin
for (integer k = 0; k < 8; k = k + 1) begin
pfc_quanta_reg[k] = '0;
end
end
always_comb begin
stat_rx_lfc_pkt_next = 1'b0;
stat_rx_lfc_xon_next = 1'b0;
stat_rx_lfc_xoff_next = 1'b0;
stat_rx_pfc_pkt_next = 1'b0;
stat_rx_pfc_xon_next = '0;
stat_rx_pfc_xoff_next = '0;
quanta_cnt_next = quanta_cnt_reg;
quanta_inc_next = 0;
if (cfg_quanta_clk_en) begin
{quanta_inc_next, quanta_cnt_next} = (2+QFB)'(quanta_cnt_reg) + cfg_quanta_step;
end
if (rx_lfc_ack) begin
if (lfc_quanta_reg >= QW'(quanta_inc_reg)) begin
lfc_quanta_next = lfc_quanta_reg - QW'(quanta_inc_reg);
end else begin
lfc_quanta_next = '0;
end
end else begin
lfc_quanta_next = lfc_quanta_reg;
end
lfc_req_next = (lfc_quanta_reg != 0) && rx_lfc_en && cfg_rx_lfc_en;
for (integer k = 0; k < 8; k = k + 1) begin
if (rx_pfc_ack[k]) begin
if (pfc_quanta_reg[k] >= QW'(quanta_inc_reg)) begin
pfc_quanta_next[k] = pfc_quanta_reg[k] - QW'(quanta_inc_reg);
end else begin
pfc_quanta_next[k] = '0;
end
end else begin
pfc_quanta_next[k] = pfc_quanta_reg[k];
end
pfc_req_next[k] = (pfc_quanta_reg[k] != 0) && rx_pfc_en[k] && cfg_rx_pfc_en;
end
if (mcf_valid) begin
if (mcf_opcode == cfg_rx_lfc_opcode && cfg_rx_lfc_en) begin
stat_rx_lfc_pkt_next = 1'b1;
stat_rx_lfc_xon_next = {mcf_params[7:0], mcf_params[15:8]} == 0;
stat_rx_lfc_xoff_next = {mcf_params[7:0], mcf_params[15:8]} != 0;
lfc_quanta_next = {mcf_params[7:0], mcf_params[15:8]};
end else if (PFC_EN && mcf_opcode == cfg_rx_pfc_opcode && cfg_rx_pfc_en) begin
stat_rx_pfc_pkt_next = 1'b1;
for (integer k = 0; k < 8; k = k + 1) begin
if (mcf_params[k+8]) begin
stat_rx_pfc_xon_next[k] = {mcf_params[16+(k*QW)+0 +: 8], mcf_params[16+(k*QW)+8 +: 8]} == 0;
stat_rx_pfc_xoff_next[k] = {mcf_params[16+(k*QW)+0 +: 8], mcf_params[16+(k*QW)+8 +: 8]} != 0;
pfc_quanta_next[k] = {mcf_params[16+(k*QW)+0 +: 8], mcf_params[16+(k*QW)+8 +: 8]};
end
end
end
end
end
always_ff @(posedge clk) begin
lfc_req_reg <= lfc_req_next;
pfc_req_reg <= pfc_req_next;
quanta_cnt_reg <= quanta_cnt_next;
quanta_inc_reg <= quanta_inc_next;
lfc_quanta_reg <= lfc_quanta_next;
for (integer k = 0; k < 8; k = k + 1) begin
pfc_quanta_reg[k] <= pfc_quanta_next[k];
end
stat_rx_lfc_pkt_reg <= stat_rx_lfc_pkt_next;
stat_rx_lfc_xon_reg <= stat_rx_lfc_xon_next;
stat_rx_lfc_xoff_reg <= stat_rx_lfc_xoff_next;
stat_rx_pfc_pkt_reg <= stat_rx_pfc_pkt_next;
stat_rx_pfc_xon_reg <= stat_rx_pfc_xon_next;
stat_rx_pfc_xoff_reg <= stat_rx_pfc_xoff_next;
if (rst) begin
lfc_req_reg <= 1'b0;
pfc_req_reg <= 8'd0;
lfc_quanta_reg <= '0;
for (integer k = 0; k < 8; k = k + 1) begin
pfc_quanta_reg[k] <= '0;
end
stat_rx_lfc_pkt_reg <= 1'b0;
stat_rx_lfc_xon_reg <= 1'b0;
stat_rx_lfc_xoff_reg <= 1'b0;
stat_rx_pfc_pkt_reg <= 1'b0;
stat_rx_pfc_xon_reg <= '0;
stat_rx_pfc_xoff_reg <= '0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,300 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2023-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* PFC and pause frame transmit handling
*/
module taxi_mac_pause_ctrl_tx #
(
parameter MCF_PARAMS_SIZE = 18,
parameter logic PFC_EN = 1'b1
)
(
input wire logic clk,
input wire logic rst,
/*
* MAC control frame interface
*/
output wire logic mcf_valid,
input wire logic mcf_ready,
output wire logic [47:0] mcf_eth_dst,
output wire logic [47:0] mcf_eth_src,
output wire logic [15:0] mcf_eth_type,
output wire logic [15:0] mcf_opcode,
output wire logic [MCF_PARAMS_SIZE*8-1:0] mcf_params,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req,
input wire logic tx_lfc_resend,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D)
*/
input wire logic [7:0] tx_pfc_req,
input wire logic tx_pfc_resend,
/*
* Configuration
*/
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [9:0] cfg_quanta_step,
input wire logic cfg_quanta_clk_en = 1'b1,
/*
* Status
*/
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused
);
localparam QW = 16;
localparam QFB = 8;
// check configuration
if (MCF_PARAMS_SIZE < (PFC_EN ? 18 : 2))
$fatal(0, "Error: MCF_PARAMS_SIZE too small for requested configuration (instance %m)");
logic lfc_req_reg = 1'b0, lfc_req_next;
logic lfc_act_reg = 1'b0, lfc_act_next;
logic lfc_send_reg = 1'b0, lfc_send_next;
logic [7:0] pfc_req_reg = 8'd0, pfc_req_next;
logic [7:0] pfc_act_reg = 8'd0, pfc_act_next;
logic [7:0] pfc_en_reg = 8'd0, pfc_en_next;
logic pfc_send_reg = 1'b0, pfc_send_next;
logic [QFB-1:0] quanta_cnt_reg = '0, quanta_cnt_next;
logic [1:0] quanta_inc_reg = '0, quanta_inc_next;
logic [QW-1:0] lfc_refresh_reg = '0, lfc_refresh_next;
logic [QW-1:0] pfc_refresh_reg[8], pfc_refresh_next[8];
logic stat_tx_lfc_pkt_reg = 1'b0, stat_tx_lfc_pkt_next;
logic stat_tx_lfc_xon_reg = 1'b0, stat_tx_lfc_xon_next;
logic stat_tx_lfc_xoff_reg = 1'b0, stat_tx_lfc_xoff_next;
logic stat_tx_pfc_pkt_reg = 1'b0, stat_tx_pfc_pkt_next;
logic [7:0] stat_tx_pfc_xon_reg = '0, stat_tx_pfc_xon_next;
logic [7:0] stat_tx_pfc_xoff_reg = '0, stat_tx_pfc_xoff_next;
// MAC control interface
logic mcf_pfc_sel_reg = PFC_EN != 0, mcf_pfc_sel_next;
logic mcf_valid_reg = 1'b0, mcf_valid_next;
wire [2*8-1:0] mcf_lfc_params;
assign mcf_lfc_params[QW*0 +: QW] = lfc_req_reg ? {cfg_tx_lfc_quanta[0 +: 8], cfg_tx_lfc_quanta[8 +: 8]} : '0;
wire [18*8-1:0] mcf_pfc_params;
assign mcf_pfc_params[QW*0 +: QW] = {pfc_en_reg, 8'd0};
assign mcf_pfc_params[QW*1 +: QW] = pfc_req_reg[0] ? {cfg_tx_pfc_quanta[0][0 +: 8], cfg_tx_pfc_quanta[0][8 +: 8]} : '0;
assign mcf_pfc_params[QW*2 +: QW] = pfc_req_reg[1] ? {cfg_tx_pfc_quanta[1][0 +: 8], cfg_tx_pfc_quanta[1][8 +: 8]} : '0;
assign mcf_pfc_params[QW*3 +: QW] = pfc_req_reg[2] ? {cfg_tx_pfc_quanta[2][0 +: 8], cfg_tx_pfc_quanta[2][8 +: 8]} : '0;
assign mcf_pfc_params[QW*4 +: QW] = pfc_req_reg[3] ? {cfg_tx_pfc_quanta[3][0 +: 8], cfg_tx_pfc_quanta[3][8 +: 8]} : '0;
assign mcf_pfc_params[QW*5 +: QW] = pfc_req_reg[4] ? {cfg_tx_pfc_quanta[4][0 +: 8], cfg_tx_pfc_quanta[4][8 +: 8]} : '0;
assign mcf_pfc_params[QW*6 +: QW] = pfc_req_reg[5] ? {cfg_tx_pfc_quanta[5][0 +: 8], cfg_tx_pfc_quanta[5][8 +: 8]} : '0;
assign mcf_pfc_params[QW*7 +: QW] = pfc_req_reg[6] ? {cfg_tx_pfc_quanta[6][0 +: 8], cfg_tx_pfc_quanta[6][8 +: 8]} : '0;
assign mcf_pfc_params[QW*8 +: QW] = pfc_req_reg[7] ? {cfg_tx_pfc_quanta[7][0 +: 8], cfg_tx_pfc_quanta[7][8 +: 8]} : '0;
assign mcf_valid = mcf_valid_reg;
assign mcf_eth_dst = (PFC_EN && mcf_pfc_sel_reg) ? cfg_tx_pfc_eth_dst : cfg_tx_lfc_eth_dst;
assign mcf_eth_src = (PFC_EN && mcf_pfc_sel_reg) ? cfg_tx_pfc_eth_src : cfg_tx_lfc_eth_src;
assign mcf_eth_type = (PFC_EN && mcf_pfc_sel_reg) ? cfg_tx_pfc_eth_type : cfg_tx_lfc_eth_type;
assign mcf_opcode = (PFC_EN && mcf_pfc_sel_reg) ? cfg_tx_pfc_opcode : cfg_tx_lfc_opcode;
if (PFC_EN) begin
assign mcf_params = mcf_pfc_sel_reg ? mcf_pfc_params : (MCF_PARAMS_SIZE*8)'(mcf_lfc_params);
end else begin
assign mcf_params = mcf_lfc_params;
end
assign stat_tx_lfc_pkt = stat_tx_lfc_pkt_reg;
assign stat_tx_lfc_xon = stat_tx_lfc_xon_reg;
assign stat_tx_lfc_xoff = stat_tx_lfc_xoff_reg;
assign stat_tx_lfc_paused = lfc_req_reg;
assign stat_tx_pfc_pkt = stat_tx_pfc_pkt_reg;
assign stat_tx_pfc_xon = stat_tx_pfc_xon_reg;
assign stat_tx_pfc_xoff = stat_tx_pfc_xoff_reg;
assign stat_tx_pfc_paused = pfc_req_reg;
initial begin
for (integer k = 0; k < 8; k = k + 1) begin
pfc_refresh_reg[k] = 0;
end
end
always_comb begin
lfc_req_next = lfc_req_reg;
lfc_act_next = lfc_act_reg;
lfc_send_next = lfc_send_reg | tx_lfc_resend;
pfc_req_next = pfc_req_reg;
pfc_act_next = pfc_act_reg;
pfc_en_next = pfc_en_reg;
pfc_send_next = pfc_send_reg | tx_pfc_resend;
mcf_pfc_sel_next = mcf_pfc_sel_reg;
mcf_valid_next = mcf_valid_reg && !mcf_ready;
stat_tx_lfc_pkt_next = 1'b0;
stat_tx_lfc_xon_next = 1'b0;
stat_tx_lfc_xoff_next = 1'b0;
stat_tx_pfc_pkt_next = 1'b0;
stat_tx_pfc_xon_next = '0;
stat_tx_pfc_xoff_next = '0;
quanta_cnt_next = quanta_cnt_reg;
quanta_inc_next = 0;
if (cfg_quanta_clk_en) begin
{quanta_inc_next, quanta_cnt_next} = (2+QFB)'(quanta_cnt_reg) + cfg_quanta_step;
end
if (lfc_refresh_reg >= QW'(quanta_inc_reg)) begin
lfc_refresh_next = lfc_refresh_reg - QW'(quanta_inc_reg);
end else begin
lfc_refresh_next = '0;
if (lfc_req_reg) begin
lfc_send_next = 1'b1;
end
end
for (integer k = 0; k < 8; k = k + 1) begin
if (pfc_refresh_reg[k] >= QW'(quanta_inc_reg)) begin
pfc_refresh_next[k] = pfc_refresh_reg[k] - QW'(quanta_inc_reg);
end else begin
pfc_refresh_next[k] = '0;
if (pfc_req_reg[k]) begin
pfc_send_next = 1'b1;
end
end
end
if (cfg_tx_lfc_en) begin
if (!mcf_valid_reg) begin
if (lfc_req_reg != tx_lfc_req) begin
lfc_req_next = tx_lfc_req;
lfc_act_next = lfc_act_reg | tx_lfc_req;
lfc_send_next = 1'b1;
end
if (lfc_send_reg && !(PFC_EN && cfg_tx_pfc_en && pfc_send_reg)) begin
mcf_pfc_sel_next = 1'b0;
mcf_valid_next = lfc_act_reg;
lfc_act_next = lfc_req_reg;
lfc_refresh_next = lfc_req_reg ? cfg_tx_lfc_refresh : '0;
lfc_send_next = 1'b0;
stat_tx_lfc_pkt_next = lfc_act_reg;
stat_tx_lfc_xon_next = lfc_act_reg && !lfc_req_reg;
stat_tx_lfc_xoff_next = lfc_act_reg && lfc_req_reg;
end
end
end
if (PFC_EN && cfg_tx_pfc_en) begin
if (!mcf_valid_reg) begin
if (pfc_req_reg != tx_pfc_req) begin
pfc_req_next = tx_pfc_req;
pfc_act_next = pfc_act_reg | tx_pfc_req;
pfc_send_next = 1'b1;
end
if (pfc_send_reg) begin
mcf_pfc_sel_next = 1'b1;
mcf_valid_next = pfc_act_reg != 0;
pfc_en_next = pfc_act_reg;
pfc_act_next = pfc_req_reg;
for (integer k = 0; k < 8; k = k + 1) begin
pfc_refresh_next[k] = pfc_req_reg[k] ? cfg_tx_pfc_refresh[k] : '0;
end
pfc_send_next = 1'b0;
stat_tx_pfc_pkt_next = pfc_act_reg != 0;
stat_tx_pfc_xon_next = pfc_act_reg & ~pfc_req_reg;
stat_tx_pfc_xoff_next = pfc_act_reg & pfc_req_reg;
end
end
end
end
always_ff @(posedge clk) begin
lfc_req_reg <= lfc_req_next;
lfc_act_reg <= lfc_act_next;
lfc_send_reg <= lfc_send_next;
pfc_req_reg <= pfc_req_next;
pfc_act_reg <= pfc_act_next;
pfc_en_reg <= pfc_en_next;
pfc_send_reg <= pfc_send_next;
mcf_pfc_sel_reg <= mcf_pfc_sel_next;
mcf_valid_reg <= mcf_valid_next;
quanta_cnt_reg <= quanta_cnt_next;
quanta_inc_reg <= quanta_inc_next;
lfc_refresh_reg <= lfc_refresh_next;
for (integer k = 0; k < 8; k = k + 1) begin
pfc_refresh_reg[k] <= pfc_refresh_next[k];
end
stat_tx_lfc_pkt_reg <= stat_tx_lfc_pkt_next;
stat_tx_lfc_xon_reg <= stat_tx_lfc_xon_next;
stat_tx_lfc_xoff_reg <= stat_tx_lfc_xoff_next;
stat_tx_pfc_pkt_reg <= stat_tx_pfc_pkt_next;
stat_tx_pfc_xon_reg <= stat_tx_pfc_xon_next;
stat_tx_pfc_xoff_reg <= stat_tx_pfc_xoff_next;
if (rst) begin
lfc_req_reg <= 1'b0;
lfc_act_reg <= 1'b0;
lfc_send_reg <= 1'b0;
pfc_req_reg <= '0;
pfc_act_reg <= '0;
pfc_send_reg <= '0;
mcf_pfc_sel_reg <= PFC_EN != 0;
mcf_valid_reg <= 1'b0;
lfc_refresh_reg <= '0;
for (integer k = 0; k < 8; k = k + 1) begin
pfc_refresh_reg[k] <= '0;
end
stat_tx_lfc_pkt_reg <= 1'b0;
stat_tx_lfc_xon_reg <= 1'b0;
stat_tx_lfc_xoff_reg <= 1'b0;
stat_tx_pfc_pkt_reg <= 1'b0;
stat_tx_pfc_xon_reg <= '0;
stat_tx_pfc_xoff_reg <= '0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,3 @@
taxi_mii_phy_if.sv
../lib/taxi/src/io/rtl/taxi_ssio_sdr_in.sv
../lib/taxi/src/sync/rtl/taxi_sync_reset.sv

View File

@@ -0,0 +1,126 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2019-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* MII PHY interface
*/
module taxi_mii_phy_if #
(
// simulation (set to avoid vendor primitives)
parameter logic SIM = 1'b0,
// vendor ("GENERIC", "XILINX", "ALTERA")
parameter VENDOR = "XILINX",
// device family
parameter FAMILY = "virtex7"
)
(
input wire logic rst,
/*
* MII interface to MAC
*/
output wire logic mac_mii_rx_clk,
output wire logic mac_mii_rx_rst,
output wire logic [3:0] mac_mii_rxd,
output wire logic mac_mii_rx_dv,
output wire logic mac_mii_rx_er,
output wire logic mac_mii_tx_clk,
output wire logic mac_mii_tx_rst,
input wire logic [3:0] mac_mii_txd,
input wire logic mac_mii_tx_en,
input wire logic mac_mii_tx_er,
/*
* MII interface to PHY
*/
input wire logic phy_mii_rx_clk,
input wire logic [3:0] phy_mii_rxd,
input wire logic phy_mii_rx_dv,
input wire logic phy_mii_rx_er,
input wire logic phy_mii_tx_clk,
output wire logic [3:0] phy_mii_txd,
output wire logic phy_mii_tx_en,
output wire logic phy_mii_tx_er
);
taxi_ssio_sdr_in #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.WIDTH(6)
)
rx_ssio_sdr_inst (
.input_clk(phy_mii_rx_clk),
.input_d({phy_mii_rxd, phy_mii_rx_dv, phy_mii_rx_er}),
.output_clk(mac_mii_rx_clk),
.output_q({mac_mii_rxd, mac_mii_rx_dv, mac_mii_rx_er})
);
(* IOB = "TRUE" *)
reg [3:0] phy_mii_txd_reg = 4'd0;
(* IOB = "TRUE" *)
reg phy_mii_tx_en_reg = 1'b0, phy_mii_tx_er_reg = 1'b0;
assign phy_mii_txd = phy_mii_txd_reg;
assign phy_mii_tx_en = phy_mii_tx_en_reg;
assign phy_mii_tx_er = phy_mii_tx_er_reg;
always_ff @(posedge mac_mii_tx_clk) begin
phy_mii_txd_reg <= mac_mii_txd;
phy_mii_tx_en_reg <= mac_mii_tx_en;
phy_mii_tx_er_reg <= mac_mii_tx_er;
end
generate
if (!SIM && VENDOR == "XILINX") begin
// Xilinx/AMD device support
BUFG
mii_bufg_inst (
.I(phy_mii_tx_clk),
.O(mac_mii_tx_clk)
);
end else begin
// generic/simulation implementation (no vendor primitives)
assign mac_mii_tx_clk = phy_mii_tx_clk;
end
endgenerate
// reset sync
taxi_sync_reset #(
.N(4)
)
tx_reset_sync_inst (
.clk(mac_mii_tx_clk),
.rst(rst),
.out(mac_mii_tx_rst)
);
taxi_sync_reset #(
.N(4)
)
rx_reset_sync_inst (
.clk(mac_mii_rx_clk),
.rst(rst),
.out(mac_mii_rx_rst)
);
endmodule
`resetall

View File

@@ -0,0 +1,5 @@
taxi_rgmii_phy_if.sv
../lib/taxi/src/io/rtl/taxi_ssio_ddr_in.sv
../lib/taxi/src/io/rtl/taxi_iddr.sv
../lib/taxi/src/io/rtl/taxi_oddr.sv
../lib/taxi/src/sync/rtl/taxi_sync_reset.sv

View File

@@ -0,0 +1,230 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2015-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* RGMII PHY interface
*/
module taxi_rgmii_phy_if #
(
// simulation (set to avoid vendor primitives)
parameter logic SIM = 1'b0,
// vendor ("GENERIC", "XILINX", "ALTERA")
parameter VENDOR = "XILINX",
// device family
parameter FAMILY = "virtex7",
// Use 90 degree clock for RGMII transmit
parameter logic USE_CLK90 = 1'b1
)
(
input wire logic gtx_clk,
input wire logic gtx_clk90,
input wire logic gtx_rst,
/*
* GMII interface to MAC
*/
output wire logic mac_gmii_rx_clk,
output wire logic mac_gmii_rx_rst,
output wire logic [7:0] mac_gmii_rxd,
output wire logic mac_gmii_rx_dv,
output wire logic mac_gmii_rx_er,
output wire logic mac_gmii_tx_clk,
output wire logic mac_gmii_tx_rst,
output wire logic mac_gmii_tx_clk_en,
input wire logic [7:0] mac_gmii_txd,
input wire logic mac_gmii_tx_en,
input wire logic mac_gmii_tx_er,
/*
* RGMII interface to PHY
*/
input wire logic phy_rgmii_rx_clk,
input wire logic [3:0] phy_rgmii_rxd,
input wire logic phy_rgmii_rx_ctl,
output wire logic phy_rgmii_tx_clk,
output wire logic [3:0] phy_rgmii_txd,
output wire logic phy_rgmii_tx_ctl,
/*
* Control
*/
input wire logic [1:0] speed
);
// receive
wire rgmii_rx_ctl_1;
wire rgmii_rx_ctl_2;
taxi_ssio_ddr_in #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.WIDTH(5)
)
rx_ssio_ddr_inst (
.input_clk(phy_rgmii_rx_clk),
.input_d({phy_rgmii_rxd, phy_rgmii_rx_ctl}),
.output_clk(mac_gmii_rx_clk),
.output_q1({mac_gmii_rxd[3:0], rgmii_rx_ctl_1}),
.output_q2({mac_gmii_rxd[7:4], rgmii_rx_ctl_2})
);
assign mac_gmii_rx_dv = rgmii_rx_ctl_1;
assign mac_gmii_rx_er = rgmii_rx_ctl_1 ^ rgmii_rx_ctl_2;
// transmit
logic rgmii_tx_clk_1_reg = 1'b1;
logic rgmii_tx_clk_2_reg = 1'b0;
logic rgmii_tx_clk_en_reg = 1'b1;
logic [5:0] count_reg = 6'd0, count_next;
always_ff @(posedge gtx_clk) begin
rgmii_tx_clk_1_reg <= rgmii_tx_clk_2_reg;
if (speed == 2'b00) begin
// 10M
count_reg <= count_reg + 1;
rgmii_tx_clk_en_reg <= 1'b0;
if (count_reg == 24) begin
rgmii_tx_clk_1_reg <= 1'b1;
rgmii_tx_clk_2_reg <= 1'b1;
end else if (count_reg >= 49) begin
rgmii_tx_clk_2_reg <= 1'b0;
rgmii_tx_clk_en_reg <= 1'b1;
count_reg <= 0;
end
end else if (speed == 2'b01) begin
// 100M
count_reg <= count_reg + 1;
rgmii_tx_clk_en_reg <= 1'b0;
if (count_reg == 2) begin
rgmii_tx_clk_1_reg <= 1'b1;
rgmii_tx_clk_2_reg <= 1'b1;
end else if (count_reg >= 4) begin
rgmii_tx_clk_2_reg <= 1'b0;
rgmii_tx_clk_en_reg <= 1'b1;
count_reg <= 0;
end
end else begin
// 1000M
rgmii_tx_clk_1_reg <= 1'b1;
rgmii_tx_clk_2_reg <= 1'b0;
rgmii_tx_clk_en_reg <= 1'b1;
end
if (gtx_rst) begin
rgmii_tx_clk_1_reg <= 1'b1;
rgmii_tx_clk_2_reg <= 1'b0;
rgmii_tx_clk_en_reg <= 1'b1;
count_reg <= 0;
end
end
logic [3:0] rgmii_txd_1;
logic [3:0] rgmii_txd_2;
logic rgmii_tx_ctl_1;
logic rgmii_tx_ctl_2;
logic gmii_clk_en;
always_comb begin
if (speed == 2'b00) begin
// 10M
rgmii_txd_1 = mac_gmii_txd[3:0];
rgmii_txd_2 = mac_gmii_txd[3:0];
if (rgmii_tx_clk_1_reg) begin
rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er;
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
end else begin
rgmii_tx_ctl_1 = mac_gmii_tx_en;
rgmii_tx_ctl_2 = mac_gmii_tx_en;
end
gmii_clk_en = rgmii_tx_clk_en_reg;
end else if (speed == 2'b01) begin
// 100M
rgmii_txd_1 = mac_gmii_txd[3:0];
rgmii_txd_2 = mac_gmii_txd[3:0];
if (rgmii_tx_clk_1_reg) begin
rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er;
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
end else begin
rgmii_tx_ctl_1 = mac_gmii_tx_en;
rgmii_tx_ctl_2 = mac_gmii_tx_en;
end
gmii_clk_en = rgmii_tx_clk_en_reg;
end else begin
// 1000M
rgmii_txd_1 = mac_gmii_txd[3:0];
rgmii_txd_2 = mac_gmii_txd[7:4];
rgmii_tx_ctl_1 = mac_gmii_tx_en;
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
gmii_clk_en = 1'b1;
end
end
taxi_oddr #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.WIDTH(1)
)
clk_oddr_inst (
.clk(USE_CLK90 ? gtx_clk90 : gtx_clk),
.d1(rgmii_tx_clk_1_reg),
.d2(rgmii_tx_clk_2_reg),
.q(phy_rgmii_tx_clk)
);
taxi_oddr #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.WIDTH(5)
)
data_oddr_inst (
.clk(gtx_clk),
.d1({rgmii_txd_1, rgmii_tx_ctl_1}),
.d2({rgmii_txd_2, rgmii_tx_ctl_2}),
.q({phy_rgmii_txd, phy_rgmii_tx_ctl})
);
assign mac_gmii_tx_clk = gtx_clk;
assign mac_gmii_tx_clk_en = gmii_clk_en;
// reset sync
taxi_sync_reset #(
.N(4)
)
tx_reset_sync_inst (
.clk(mac_gmii_tx_clk),
.rst(gtx_rst),
.out(mac_gmii_tx_rst)
);
taxi_sync_reset #(
.N(4)
)
rx_reset_sync_inst (
.clk(mac_gmii_rx_clk),
.rst(gtx_rst),
.out(mac_gmii_rx_rst)
);
endmodule
`resetall

View File

@@ -0,0 +1,393 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* XGMII 10GBASE-R decoder
*/
module taxi_xgmii_baser_dec_64 #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter HDR_W = 2
)
(
input wire logic clk,
input wire logic rst,
/*
* 10GBASE-R encoded input
*/
input wire logic [DATA_W-1:0] encoded_rx_data,
input wire logic [HDR_W-1:0] encoded_rx_hdr,
/*
* XGMII interface
*/
output wire logic [DATA_W-1:0] xgmii_rxd,
output wire logic [CTRL_W-1:0] xgmii_rxc,
/*
* Status
*/
output wire logic rx_bad_block,
output wire logic rx_sequence_error
);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64");
if (CTRL_W * 8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
localparam [7:0]
XGMII_IDLE = 8'h07,
XGMII_LPI = 8'h06,
XGMII_START = 8'hfb,
XGMII_TERM = 8'hfd,
XGMII_ERROR = 8'hfe,
XGMII_SEQ_OS = 8'h9c,
XGMII_RES_0 = 8'h1c,
XGMII_RES_1 = 8'h3c,
XGMII_RES_2 = 8'h7c,
XGMII_RES_3 = 8'hbc,
XGMII_RES_4 = 8'hdc,
XGMII_RES_5 = 8'hf7,
XGMII_SIG_OS = 8'h5c;
localparam [6:0]
CTRL_IDLE = 7'h00,
CTRL_LPI = 7'h06,
CTRL_ERROR = 7'h1e,
CTRL_RES_0 = 7'h2d,
CTRL_RES_1 = 7'h33,
CTRL_RES_2 = 7'h4b,
CTRL_RES_3 = 7'h55,
CTRL_RES_4 = 7'h66,
CTRL_RES_5 = 7'h78;
localparam [3:0]
O_SEQ_OS = 4'h0,
O_SIG_OS = 4'hf;
localparam [1:0]
SYNC_DATA = 2'b10,
SYNC_CTRL = 2'b01;
localparam [7:0]
BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT
BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT
BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT
BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT
BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT
BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT
BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT
BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT
BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT
BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT
logic [DATA_W-1:0] decoded_ctrl;
logic [CTRL_W-1:0] decode_err;
logic [DATA_W-1:0] xgmii_rxd_reg = '0, xgmii_rxd_next;
logic [CTRL_W-1:0] xgmii_rxc_reg = '0, xgmii_rxc_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;
assign xgmii_rxd = xgmii_rxd_reg;
assign xgmii_rxc = xgmii_rxc_reg;
assign rx_bad_block = rx_bad_block_reg;
assign rx_sequence_error = rx_sequence_error_reg;
always_comb begin
xgmii_rxd_next = {8{XGMII_ERROR}};
xgmii_rxc_next = 8'hff;
rx_bad_block_next = 1'b0;
rx_sequence_error_next = 1'b0;
frame_next = frame_reg;
for (integer i = 0; i < CTRL_W; i = i + 1) begin
case (encoded_rx_data[7*i+8 +: 7])
CTRL_IDLE: begin
decoded_ctrl[8*i +: 8] = XGMII_IDLE;
decode_err[i] = 1'b0;
end
CTRL_LPI: begin
decoded_ctrl[8*i +: 8] = XGMII_LPI;
decode_err[i] = 1'b0;
end
CTRL_ERROR: begin
decoded_ctrl[8*i +: 8] = XGMII_ERROR;
decode_err[i] = 1'b0;
end
CTRL_RES_0: begin
decoded_ctrl[8*i +: 8] = XGMII_RES_0;
decode_err[i] = 1'b0;
end
CTRL_RES_1: begin
decoded_ctrl[8*i +: 8] = XGMII_RES_1;
decode_err[i] = 1'b0;
end
CTRL_RES_2: begin
decoded_ctrl[8*i +: 8] = XGMII_RES_2;
decode_err[i] = 1'b0;
end
CTRL_RES_3: begin
decoded_ctrl[8*i +: 8] = XGMII_RES_3;
decode_err[i] = 1'b0;
end
CTRL_RES_4: begin
decoded_ctrl[8*i +: 8] = XGMII_RES_4;
decode_err[i] = 1'b0;
end
CTRL_RES_5: begin
decoded_ctrl[8*i +: 8] = XGMII_RES_5;
decode_err[i] = 1'b0;
end
default: begin
decoded_ctrl[8*i +: 8] = XGMII_ERROR;
decode_err[i] = 1'b1;
end
endcase
end
// use only four bits of block type for reduced fanin
if (encoded_rx_hdr[0] == 0) begin
xgmii_rxd_next = encoded_rx_data;
xgmii_rxc_next = 8'h00;
rx_bad_block_next = 1'b0;
end else begin
case (encoded_rx_data[7:4])
BLOCK_TYPE_CTRL[7:4]: begin
// C7 C6 C5 C4 C3 C2 C1 C0 BT
xgmii_rxd_next = decoded_ctrl;
xgmii_rxc_next = 8'hff;
rx_bad_block_next = decode_err != 0;
end
BLOCK_TYPE_OS_4[7:4]: begin
// D7 D6 D5 O4 C3 C2 C1 C0 BT
xgmii_rxd_next[31:0] = decoded_ctrl[31:0];
xgmii_rxc_next[3:0] = 4'hf;
xgmii_rxd_next[63:40] = encoded_rx_data[63:40];
xgmii_rxc_next[7:4] = 4'h1;
if (encoded_rx_data[39:36] == O_SEQ_OS) begin
xgmii_rxd_next[39:32] = XGMII_SEQ_OS;
rx_bad_block_next = decode_err[3:0] != 0;
end else begin
xgmii_rxd_next[39:32] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
end
end
BLOCK_TYPE_START_4[7:4]: begin
// D7 D6 D5 C3 C2 C1 C0 BT
xgmii_rxd_next = {encoded_rx_data[63:40], XGMII_START, decoded_ctrl[31:0]};
xgmii_rxc_next = 8'h1f;
rx_bad_block_next = decode_err[3:0] != 0;
rx_sequence_error_next = frame_reg;
frame_next = 1'b1;
end
BLOCK_TYPE_OS_START[7:4]: begin
// D7 D6 D5 O0 D3 D2 D1 BT
xgmii_rxd_next[31:8] = encoded_rx_data[31:8];
xgmii_rxc_next[3:0] = 4'h1;
if (encoded_rx_data[35:32] == O_SEQ_OS) begin
xgmii_rxd_next[7:0] = XGMII_SEQ_OS;
rx_bad_block_next = 1'b0;
end else begin
xgmii_rxd_next[7:0] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
end
xgmii_rxd_next[63:32] = {encoded_rx_data[63:40], XGMII_START};
xgmii_rxc_next[7:4] = 4'h1;
rx_sequence_error_next = frame_reg;
frame_next = 1'b1;
end
BLOCK_TYPE_OS_04[7:4]: begin
// D7 D6 D5 O4 O0 D3 D2 D1 BT
rx_bad_block_next = 1'b0;
xgmii_rxd_next[31:8] = encoded_rx_data[31:8];
xgmii_rxc_next[3:0] = 4'h1;
if (encoded_rx_data[35:32] == O_SEQ_OS) begin
xgmii_rxd_next[7:0] = XGMII_SEQ_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[63:40];
xgmii_rxc_next[7:4] = 4'h1;
if (encoded_rx_data[39:36] == O_SEQ_OS) begin
xgmii_rxd_next[39:32] = XGMII_SEQ_OS;
end else begin
xgmii_rxd_next[39:32] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
end
end
BLOCK_TYPE_START_0[7:4]: begin
// D7 D6 D5 D4 D3 D2 D1 BT
xgmii_rxd_next = {encoded_rx_data[63:8], XGMII_START};
xgmii_rxc_next = 8'h01;
rx_bad_block_next = 1'b0;
rx_sequence_error_next = frame_reg;
frame_next = 1'b1;
end
BLOCK_TYPE_OS_0[7:4]: begin
// C7 C6 C5 C4 O0 D3 D2 D1 BT
xgmii_rxd_next[31:8] = encoded_rx_data[31:8];
xgmii_rxc_next[3:0] = 4'h1;
if (encoded_rx_data[35:32] == O_SEQ_OS) begin
xgmii_rxd_next[7:0] = XGMII_SEQ_OS;
rx_bad_block_next = decode_err[7:4] != 0;
end else begin
xgmii_rxd_next[7:0] = XGMII_ERROR;
rx_bad_block_next = 1'b1;
end
xgmii_rxd_next[63:32] = decoded_ctrl[63:32];
xgmii_rxc_next[7:4] = 4'hf;
end
BLOCK_TYPE_TERM_0[7:4]: begin
// C7 C6 C5 C4 C3 C2 C1 BT
xgmii_rxd_next = {decoded_ctrl[63:8], XGMII_TERM};
xgmii_rxc_next = 8'hff;
rx_bad_block_next = decode_err[7:1] != 0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
BLOCK_TYPE_TERM_1[7:4]: begin
// C7 C6 C5 C4 C3 C2 D0 BT
xgmii_rxd_next = {decoded_ctrl[63:16], XGMII_TERM, encoded_rx_data[15:8]};
xgmii_rxc_next = 8'hfe;
rx_bad_block_next = decode_err[7:2] != 0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
BLOCK_TYPE_TERM_2[7:4]: begin
// C7 C6 C5 C4 C3 D1 D0 BT
xgmii_rxd_next = {decoded_ctrl[63:24], XGMII_TERM, encoded_rx_data[23:8]};
xgmii_rxc_next = 8'hfc;
rx_bad_block_next = decode_err[7:3] != 0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
BLOCK_TYPE_TERM_3[7:4]: begin
// C7 C6 C5 C4 D2 D1 D0 BT
xgmii_rxd_next = {decoded_ctrl[63:32], XGMII_TERM, encoded_rx_data[31:8]};
xgmii_rxc_next = 8'hf8;
rx_bad_block_next = decode_err[7:4] != 0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
BLOCK_TYPE_TERM_4[7:4]: begin
// C7 C6 C5 D3 D2 D1 D0 BT
xgmii_rxd_next = {decoded_ctrl[63:40], XGMII_TERM, encoded_rx_data[39:8]};
xgmii_rxc_next = 8'hf0;
rx_bad_block_next = decode_err[7:5] != 0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
BLOCK_TYPE_TERM_5[7:4]: begin
// C7 C6 D4 D3 D2 D1 D0 BT
xgmii_rxd_next = {decoded_ctrl[63:48], XGMII_TERM, encoded_rx_data[47:8]};
xgmii_rxc_next = 8'he0;
rx_bad_block_next = decode_err[7:6] != 0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
BLOCK_TYPE_TERM_6[7:4]: begin
// C7 D5 D4 D3 D2 D1 D0 BT
xgmii_rxd_next = {decoded_ctrl[63:56], XGMII_TERM, encoded_rx_data[55:8]};
xgmii_rxc_next = 8'hc0;
rx_bad_block_next = decode_err[7] != 0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
BLOCK_TYPE_TERM_7[7:4]: begin
// D6 D5 D4 D3 D2 D1 D0 BT
xgmii_rxd_next = {XGMII_TERM, encoded_rx_data[63:8]};
xgmii_rxc_next = 8'h80;
rx_bad_block_next = 1'b0;
rx_sequence_error_next = !frame_reg;
frame_next = 1'b0;
end
default: begin
// invalid block type
xgmii_rxd_next = {8{XGMII_ERROR}};
xgmii_rxc_next = 8'hff;
rx_bad_block_next = 1'b1;
end
endcase
end
// check all block type bits to detect bad encodings
if (encoded_rx_hdr == SYNC_DATA) begin
// data - nothing encoded
end else if (encoded_rx_hdr == SYNC_CTRL) begin
// control - check for bad block types
case (encoded_rx_data[7:0])
BLOCK_TYPE_CTRL: begin end
BLOCK_TYPE_OS_4: begin end
BLOCK_TYPE_START_4: begin end
BLOCK_TYPE_OS_START: begin end
BLOCK_TYPE_OS_04: begin end
BLOCK_TYPE_START_0: begin end
BLOCK_TYPE_OS_0: begin end
BLOCK_TYPE_TERM_0: begin end
BLOCK_TYPE_TERM_1: begin end
BLOCK_TYPE_TERM_2: begin end
BLOCK_TYPE_TERM_3: begin end
BLOCK_TYPE_TERM_4: begin end
BLOCK_TYPE_TERM_5: begin end
BLOCK_TYPE_TERM_6: begin end
BLOCK_TYPE_TERM_7: begin end
default: begin
// invalid block type
xgmii_rxd_next = {8{XGMII_ERROR}};
xgmii_rxc_next = 8'hff;
rx_bad_block_next = 1'b1;
end
endcase
end else begin
// invalid header
xgmii_rxd_next = {8{XGMII_ERROR}};
xgmii_rxc_next = 8'hff;
rx_bad_block_next = 1'b1;
end
end
always_ff @(posedge clk) begin
xgmii_rxd_reg <= xgmii_rxd_next;
xgmii_rxc_reg <= xgmii_rxc_next;
rx_bad_block_reg <= rx_bad_block_next;
rx_sequence_error_reg <= rx_sequence_error_next;
frame_reg <= frame_next;
if (rst) begin
frame_reg <= 1'b0;
end
end
endmodule
`resetall

View File

@@ -0,0 +1,258 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2018-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* XGMII 10GBASE-R encoder
*/
module taxi_xgmii_baser_enc_64 #
(
parameter DATA_W = 64,
parameter CTRL_W = (DATA_W/8),
parameter HDR_W = 2
)
(
input wire logic clk,
input wire logic rst,
/*
* XGMII interface
*/
input wire logic [DATA_W-1:0] xgmii_txd,
input wire logic [CTRL_W-1:0] xgmii_txc,
/*
* 10GBASE-R encoded interface
*/
output wire logic [DATA_W-1:0] encoded_tx_data,
output wire logic [HDR_W-1:0] encoded_tx_hdr,
/*
* Status
*/
output wire logic tx_bad_block
);
// check configuration
if (DATA_W != 64)
$fatal(0, "Error: Interface width must be 64");
if (CTRL_W * 8 != DATA_W)
$fatal(0, "Error: Interface requires byte (8-bit) granularity");
if (HDR_W != 2)
$fatal(0, "Error: HDR_W must be 2");
localparam [7:0]
XGMII_IDLE = 8'h07,
XGMII_LPI = 8'h06,
XGMII_START = 8'hfb,
XGMII_TERM = 8'hfd,
XGMII_ERROR = 8'hfe,
XGMII_SEQ_OS = 8'h9c,
XGMII_RES_0 = 8'h1c,
XGMII_RES_1 = 8'h3c,
XGMII_RES_2 = 8'h7c,
XGMII_RES_3 = 8'hbc,
XGMII_RES_4 = 8'hdc,
XGMII_RES_5 = 8'hf7,
XGMII_SIG_OS = 8'h5c;
localparam [6:0]
CTRL_IDLE = 7'h00,
CTRL_LPI = 7'h06,
CTRL_ERROR = 7'h1e,
CTRL_RES_0 = 7'h2d,
CTRL_RES_1 = 7'h33,
CTRL_RES_2 = 7'h4b,
CTRL_RES_3 = 7'h55,
CTRL_RES_4 = 7'h66,
CTRL_RES_5 = 7'h78;
localparam [3:0]
O_SEQ_OS = 4'h0,
O_SIG_OS = 4'hf;
localparam [1:0]
SYNC_DATA = 2'b10,
SYNC_CTRL = 2'b01;
localparam [7:0]
BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT
BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT
BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT
BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT
BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT
BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT
BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT
BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT
BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT
BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT
BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT
BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT
logic [DATA_W*7/8-1:0] encoded_ctrl;
logic [CTRL_W-1:0] encode_err;
logic [DATA_W-1:0] encoded_tx_data_reg = '0, encoded_tx_data_next;
logic [HDR_W-1:0] encoded_tx_hdr_reg = '0, encoded_tx_hdr_next;
logic tx_bad_block_reg = 1'b0, tx_bad_block_next;
assign encoded_tx_data = encoded_tx_data_reg;
assign encoded_tx_hdr = encoded_tx_hdr_reg;
assign tx_bad_block = tx_bad_block_reg;
always_comb begin
tx_bad_block_next = 1'b0;
for (integer i = 0; i < CTRL_W; i = i + 1) begin
if (xgmii_txc[i]) begin
// control
case (xgmii_txd[8*i +: 8])
XGMII_IDLE: begin
encoded_ctrl[7*i +: 7] = CTRL_IDLE;
encode_err[i] = 1'b0;
end
XGMII_LPI: begin
encoded_ctrl[7*i +: 7] = CTRL_LPI;
encode_err[i] = 1'b0;
end
XGMII_ERROR: begin
encoded_ctrl[7*i +: 7] = CTRL_ERROR;
encode_err[i] = 1'b0;
end
XGMII_RES_0: begin
encoded_ctrl[7*i +: 7] = CTRL_RES_0;
encode_err[i] = 1'b0;
end
XGMII_RES_1: begin
encoded_ctrl[7*i +: 7] = CTRL_RES_1;
encode_err[i] = 1'b0;
end
XGMII_RES_2: begin
encoded_ctrl[7*i +: 7] = CTRL_RES_2;
encode_err[i] = 1'b0;
end
XGMII_RES_3: begin
encoded_ctrl[7*i +: 7] = CTRL_RES_3;
encode_err[i] = 1'b0;
end
XGMII_RES_4: begin
encoded_ctrl[7*i +: 7] = CTRL_RES_4;
encode_err[i] = 1'b0;
end
XGMII_RES_5: begin
encoded_ctrl[7*i +: 7] = CTRL_RES_5;
encode_err[i] = 1'b0;
end
default: begin
encoded_ctrl[7*i +: 7] = CTRL_ERROR;
encode_err[i] = 1'b1;
end
endcase
end else begin
// data (always invalid as control)
encoded_ctrl[7*i +: 7] = CTRL_ERROR;
encode_err[i] = 1'b1;
end
end
if (xgmii_txc == 8'h00) begin
encoded_tx_data_next = xgmii_txd;
encoded_tx_hdr_next = SYNC_DATA;
tx_bad_block_next = 1'b0;
end else begin
if (xgmii_txc == 8'h1f && xgmii_txd[39:32] == XGMII_SEQ_OS) begin
// ordered set in lane 4
encoded_tx_data_next = {xgmii_txd[63:40], O_SEQ_OS, encoded_ctrl[27:0], BLOCK_TYPE_OS_4};
tx_bad_block_next = encode_err[3:0] != 0;
end else if (xgmii_txc == 8'h1f && xgmii_txd[39:32] == XGMII_START) begin
// start in lane 4
encoded_tx_data_next = {xgmii_txd[63:40], 4'd0, encoded_ctrl[27:0], BLOCK_TYPE_START_4};
tx_bad_block_next = encode_err[3:0] != 0;
end else if (xgmii_txc == 8'h11 && xgmii_txd[7:0] == XGMII_SEQ_OS && xgmii_txd[39:32] == XGMII_START) begin
// ordered set in lane 0, start in lane 4
encoded_tx_data_next = {xgmii_txd[63:40], 4'd0, O_SEQ_OS, xgmii_txd[31:8], BLOCK_TYPE_OS_START};
tx_bad_block_next = 1'b0;
end else if (xgmii_txc == 8'h11 && xgmii_txd[7:0] == XGMII_SEQ_OS && xgmii_txd[39:32] == XGMII_SEQ_OS) begin
// ordered set in lane 0 and lane 4
encoded_tx_data_next = {xgmii_txd[63:40], O_SEQ_OS, O_SEQ_OS, xgmii_txd[31:8], BLOCK_TYPE_OS_04};
tx_bad_block_next = 1'b0;
end else if (xgmii_txc == 8'h01 && xgmii_txd[7:0] == XGMII_START) begin
// start in lane 0
encoded_tx_data_next = {xgmii_txd[63:8], BLOCK_TYPE_START_0};
tx_bad_block_next = 1'b0;
end else if (xgmii_txc == 8'hf1 && xgmii_txd[7:0] == XGMII_SEQ_OS) begin
// ordered set in lane 0
encoded_tx_data_next = {encoded_ctrl[55:28], O_SEQ_OS, xgmii_txd[31:8], BLOCK_TYPE_OS_0};
tx_bad_block_next = encode_err[7:4] != 0;
end else if (xgmii_txc == 8'hff && xgmii_txd[7:0] == XGMII_TERM) begin
// terminate in lane 0
encoded_tx_data_next = {encoded_ctrl[55:7], 7'd0, BLOCK_TYPE_TERM_0};
tx_bad_block_next = encode_err[7:1] != 0;
end else if (xgmii_txc == 8'hfe && xgmii_txd[15:8] == XGMII_TERM) begin
// terminate in lane 1
encoded_tx_data_next = {encoded_ctrl[55:14], 6'd0, xgmii_txd[7:0], BLOCK_TYPE_TERM_1};
tx_bad_block_next = encode_err[7:2] != 0;
end else if (xgmii_txc == 8'hfc && xgmii_txd[23:16] == XGMII_TERM) begin
// terminate in lane 2
encoded_tx_data_next = {encoded_ctrl[55:21], 5'd0, xgmii_txd[15:0], BLOCK_TYPE_TERM_2};
tx_bad_block_next = encode_err[7:3] != 0;
end else if (xgmii_txc == 8'hf8 && xgmii_txd[31:24] == XGMII_TERM) begin
// terminate in lane 3
encoded_tx_data_next = {encoded_ctrl[55:28], 4'd0, xgmii_txd[23:0], BLOCK_TYPE_TERM_3};
tx_bad_block_next = encode_err[7:4] != 0;
end else if (xgmii_txc == 8'hf0 && xgmii_txd[39:32] == XGMII_TERM) begin
// terminate in lane 4
encoded_tx_data_next = {encoded_ctrl[55:35], 3'd0, xgmii_txd[31:0], BLOCK_TYPE_TERM_4};
tx_bad_block_next = encode_err[7:5] != 0;
end else if (xgmii_txc == 8'he0 && xgmii_txd[47:40] == XGMII_TERM) begin
// terminate in lane 5
encoded_tx_data_next = {encoded_ctrl[55:42], 2'd0, xgmii_txd[39:0], BLOCK_TYPE_TERM_5};
tx_bad_block_next = encode_err[7:6] != 0;
end else if (xgmii_txc == 8'hc0 && xgmii_txd[55:48] == XGMII_TERM) begin
// terminate in lane 6
encoded_tx_data_next = {encoded_ctrl[55:49], 1'd0, xgmii_txd[47:0], BLOCK_TYPE_TERM_6};
tx_bad_block_next = encode_err[7] != 0;
end else if (xgmii_txc == 8'h80 && xgmii_txd[63:56] == XGMII_TERM) begin
// terminate in lane 7
encoded_tx_data_next = {xgmii_txd[55:0], BLOCK_TYPE_TERM_7};
tx_bad_block_next = 1'b0;
end else if (xgmii_txc == 8'hff) begin
// all control
encoded_tx_data_next = {encoded_ctrl, BLOCK_TYPE_CTRL};
tx_bad_block_next = encode_err != 0;
end else begin
// no corresponding block format
encoded_tx_data_next = {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL};
tx_bad_block_next = 1'b1;
end
encoded_tx_hdr_next = SYNC_CTRL;
end
end
always_ff @(posedge clk) begin
encoded_tx_data_reg <= encoded_tx_data_next;
encoded_tx_hdr_reg <= encoded_tx_hdr_next;
tx_bad_block_reg <= tx_bad_block_next;
end
endmodule
`resetall

View File

@@ -0,0 +1,4 @@
taxi_eth_mac_25g_us.sv
taxi_eth_mac_25g_us_ch.sv
taxi_eth_phy_25g_us_gt.f
../taxi_eth_mac_phy_10g.f

View File

@@ -0,0 +1,528 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* Transceiver and MAC/PHY quad wrapper for UltraScale/UltraScale+
*/
module taxi_eth_mac_25g_us #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtexuplus",
parameter CNT = 4,
// GT type
parameter string GT_TYPE = "GTY",
// GT parameters
parameter logic [CNT-1:0] GT_TX_POLARITY = '0,
parameter logic [CNT-1:0] GT_RX_POLARITY = '0,
// MAC/PHY parameters
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic PRBS31_EN = 1'b0,
parameter TX_SERDES_PIPELINE = 1,
parameter RX_SERDES_PIPELINE = 1,
parameter BITSLIP_HIGH_CYCLES = 0,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = 1,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR[CNT] = '{CNT{"MAC"}}
)
(
input wire logic xcvr_ctrl_clk,
input wire logic xcvr_ctrl_rst,
/*
* Common
*/
output wire logic xcvr_gtpowergood_out,
input wire logic xcvr_gtrefclk00_in,
output wire logic xcvr_qpll0lock_out,
output wire logic xcvr_qpll0clk_out,
output wire logic xcvr_qpll0refclk_out,
/*
* Serial data
*/
output wire logic [CNT-1:0] xcvr_txp,
output wire logic [CNT-1:0] xcvr_txn,
input wire logic [CNT-1:0] xcvr_rxp,
input wire logic [CNT-1:0] xcvr_rxn,
/*
* MAC clocks
*/
output wire logic [CNT-1:0] rx_clk,
input wire logic [CNT-1:0] rx_rst_in,
output wire logic [CNT-1:0] rx_rst_out,
output wire logic [CNT-1:0] tx_clk,
input wire logic [CNT-1:0] tx_rst_in,
output wire logic [CNT-1:0] tx_rst_out,
input wire logic [CNT-1:0] ptp_sample_clk,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx[CNT],
taxi_axis_if.src m_axis_tx_cpl[CNT],
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx[CNT],
/*
* PTP clock
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts[CNT] = '{CNT{'0}},
input wire logic [CNT-1:0] tx_ptp_ts_step = '0,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts[CNT] = '{CNT{'0}},
input wire logic [CNT-1:0] rx_ptp_ts_step = '0,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic [CNT-1:0] tx_lfc_req = '0,
input wire logic [CNT-1:0] tx_lfc_resend = '0,
input wire logic [CNT-1:0] rx_lfc_en = '0,
output wire logic [CNT-1:0] rx_lfc_req,
input wire logic [CNT-1:0] rx_lfc_ack = '0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req[CNT] = '{CNT{'0}},
input wire logic [CNT-1:0] tx_pfc_resend = '0,
input wire logic [7:0] rx_pfc_en[CNT] = '{CNT{'0}},
output wire logic [7:0] rx_pfc_req[CNT],
input wire logic [7:0] rx_pfc_ack[CNT] = '{CNT{'0}},
/*
* Pause interface
*/
input wire logic [CNT-1:0] tx_lfc_pause_en = '0,
input wire logic [CNT-1:0] tx_pause_req = '0,
output wire logic [CNT-1:0] tx_pause_ack,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic [1:0] tx_start_packet[CNT],
output wire logic [3:0] stat_tx_byte[CNT],
output wire logic [15:0] stat_tx_pkt_len[CNT],
output wire logic [CNT-1:0] stat_tx_pkt_ucast,
output wire logic [CNT-1:0] stat_tx_pkt_mcast,
output wire logic [CNT-1:0] stat_tx_pkt_bcast,
output wire logic [CNT-1:0] stat_tx_pkt_vlan,
output wire logic [CNT-1:0] stat_tx_pkt_good,
output wire logic [CNT-1:0] stat_tx_pkt_bad,
output wire logic [CNT-1:0] stat_tx_err_oversize,
output wire logic [CNT-1:0] stat_tx_err_user,
output wire logic [CNT-1:0] stat_tx_err_underflow,
output wire logic [1:0] rx_start_packet[CNT],
output wire logic [6:0] rx_error_count[CNT],
output wire logic [CNT-1:0] rx_block_lock,
output wire logic [CNT-1:0] rx_high_ber,
output wire logic [CNT-1:0] rx_status,
output wire logic [3:0] stat_rx_byte[CNT],
output wire logic [15:0] stat_rx_pkt_len[CNT],
output wire logic [CNT-1:0] stat_rx_pkt_fragment,
output wire logic [CNT-1:0] stat_rx_pkt_jabber,
output wire logic [CNT-1:0] stat_rx_pkt_ucast,
output wire logic [CNT-1:0] stat_rx_pkt_mcast,
output wire logic [CNT-1:0] stat_rx_pkt_bcast,
output wire logic [CNT-1:0] stat_rx_pkt_vlan,
output wire logic [CNT-1:0] stat_rx_pkt_good,
output wire logic [CNT-1:0] stat_rx_pkt_bad,
output wire logic [CNT-1:0] stat_rx_err_oversize,
output wire logic [CNT-1:0] stat_rx_err_bad_fcs,
output wire logic [CNT-1:0] stat_rx_err_bad_block,
output wire logic [CNT-1:0] stat_rx_err_framing,
output wire logic [CNT-1:0] stat_rx_err_preamble,
input wire logic [CNT-1:0] stat_rx_fifo_drop = '0,
output wire logic [CNT-1:0] stat_tx_mcf,
output wire logic [CNT-1:0] stat_rx_mcf,
output wire logic [CNT-1:0] stat_tx_lfc_pkt,
output wire logic [CNT-1:0] stat_tx_lfc_xon,
output wire logic [CNT-1:0] stat_tx_lfc_xoff,
output wire logic [CNT-1:0] stat_tx_lfc_paused,
output wire logic [CNT-1:0] stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon[CNT],
output wire logic [7:0] stat_tx_pfc_xoff[CNT],
output wire logic [7:0] stat_tx_pfc_paused[CNT],
output wire logic [CNT-1:0] stat_rx_lfc_pkt,
output wire logic [CNT-1:0] stat_rx_lfc_xon,
output wire logic [CNT-1:0] stat_rx_lfc_xoff,
output wire logic [CNT-1:0] stat_rx_lfc_paused,
output wire logic [CNT-1:0] stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon[CNT],
output wire logic [7:0] stat_rx_pfc_xoff[CNT],
output wire logic [7:0] stat_rx_pfc_paused[CNT],
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len[CNT] = '{CNT{16'd1518}},
input wire logic [7:0] cfg_tx_ifg[CNT] = '{CNT{8'd12}},
input wire logic [CNT-1:0] cfg_tx_enable = '1,
input wire logic [15:0] cfg_rx_max_pkt_len[CNT] = '{CNT{16'd1518}},
input wire logic [CNT-1:0] cfg_rx_enable = '1,
input wire logic [7:0] cfg_ifg[CNT] = '{CNT{8'd12}},
input wire logic [CNT-1:0] cfg_tx_prbs31_enable = '0,
input wire logic [CNT-1:0] cfg_rx_prbs31_enable = '0,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast[CNT] = '{CNT{48'h01_80_C2_00_00_01}},
input wire logic [CNT-1:0] cfg_mcf_rx_check_eth_dst_mcast = '1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast[CNT] = '{CNT{48'd0}},
input wire logic [CNT-1:0] cfg_mcf_rx_check_eth_dst_ucast = '0,
input wire logic [47:0] cfg_mcf_rx_eth_src[CNT] = '{CNT{48'd0}},
input wire logic [CNT-1:0] cfg_mcf_rx_check_eth_src = '0,
input wire logic [15:0] cfg_mcf_rx_eth_type[CNT] = '{CNT{16'h8808}},
input wire logic [15:0] cfg_mcf_rx_opcode_lfc[CNT] = '{CNT{16'h0001}},
input wire logic [CNT-1:0] cfg_mcf_rx_check_opcode_lfc = '1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc[CNT] = '{CNT{16'h0101}},
input wire logic [CNT-1:0] cfg_mcf_rx_check_opcode_pfc = '1,
input wire logic [CNT-1:0] cfg_mcf_rx_forward = '0,
input wire logic [CNT-1:0] cfg_mcf_rx_enable = '0,
input wire logic [47:0] cfg_tx_lfc_eth_dst[CNT] = '{CNT{48'h01_80_C2_00_00_01}},
input wire logic [47:0] cfg_tx_lfc_eth_src[CNT] = '{CNT{48'h80_23_31_43_54_4C}},
input wire logic [15:0] cfg_tx_lfc_eth_type[CNT] = '{CNT{16'h8808}},
input wire logic [15:0] cfg_tx_lfc_opcode[CNT] = '{CNT{16'h0001}},
input wire logic [CNT-1:0] cfg_tx_lfc_en = '0,
input wire logic [15:0] cfg_tx_lfc_quanta[CNT] = '{CNT{16'hffff}},
input wire logic [15:0] cfg_tx_lfc_refresh[CNT] = '{CNT{16'h7fff}},
input wire logic [47:0] cfg_tx_pfc_eth_dst[CNT] = '{CNT{48'h01_80_C2_00_00_01}},
input wire logic [47:0] cfg_tx_pfc_eth_src[CNT] = '{CNT{48'h80_23_31_43_54_4C}},
input wire logic [15:0] cfg_tx_pfc_eth_type[CNT] = '{CNT{16'h8808}},
input wire logic [15:0] cfg_tx_pfc_opcode[CNT] = '{CNT{16'h0101}},
input wire logic [CNT-1:0] cfg_tx_pfc_en = '0,
input wire logic [15:0] cfg_tx_pfc_quanta[CNT][8] = '{CNT{'{8{16'hffff}}}},
input wire logic [15:0] cfg_tx_pfc_refresh[CNT][8] = '{CNT{'{8{16'h7fff}}}},
input wire logic [15:0] cfg_rx_lfc_opcode[CNT] = '{CNT{16'h0001}},
input wire logic [CNT-1:0] cfg_rx_lfc_en = '0,
input wire logic [15:0] cfg_rx_pfc_opcode[CNT] = '{CNT{16'h0101}},
input wire logic [CNT-1:0] cfg_rx_pfc_en = '0
);
// statistics
localparam STAT_TX_CNT = STAT_TX_LEVEL == 0 ? 8 : (STAT_TX_LEVEL == 1 ? 16: 32);
localparam STAT_RX_CNT = STAT_RX_LEVEL == 0 ? 8 : (STAT_RX_LEVEL == 1 ? 16: 32);
taxi_axis_if #(
.DATA_W(m_axis_stat.DATA_W),
.KEEP_EN(m_axis_stat.KEEP_EN),
.KEEP_W(m_axis_stat.KEEP_W),
.LAST_EN(m_axis_stat.LAST_EN),
.ID_EN(m_axis_stat.ID_EN),
.ID_W(m_axis_stat.ID_W),
.USER_EN(m_axis_stat.USER_EN),
.USER_W(m_axis_stat.USER_W)
)
axis_stat_int[CNT]();
if (STAT_EN) begin : stats
taxi_axis_arb_mux #(
.S_COUNT(CNT),
.UPDATE_TID(1'b0),
.ARB_ROUND_ROBIN(1'b1),
.ARB_LSB_HIGH_PRIO(1'b0)
)
stat_mux_inst (
.clk(stat_clk),
.rst(stat_rst),
/*
* AXI4-Stream inputs (sink)
*/
.s_axis(axis_stat_int),
/*
* AXI4-Stream output (source)
*/
.m_axis(m_axis_stat)
);
end
for (genvar n = 0; n < CNT; n = n + 1) begin : ch
localparam HAS_COMMON = n == 0;
wire ch_gtpowergood_out;
wire ch_qpll0lock_out;
wire ch_qpll0clk_out;
wire ch_qpll0refclk_out;
if (HAS_COMMON) begin
// drive outputs from common
assign xcvr_gtpowergood_out = ch_gtpowergood_out;
assign xcvr_qpll0lock_out = ch_qpll0lock_out;
assign xcvr_qpll0clk_out = ch_qpll0clk_out;
assign xcvr_qpll0refclk_out = ch_qpll0refclk_out;
end
taxi_eth_mac_25g_us_ch #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.HAS_COMMON(HAS_COMMON),
// GT type
.GT_TYPE(GT_TYPE),
// GT parameters
.GT_TX_POLARITY(GT_TX_POLARITY[n]),
.GT_RX_POLARITY(GT_RX_POLARITY[n]),
// MAC/PHY parameters
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.PRBS31_EN(PRBS31_EN),
.TX_SERDES_PIPELINE(TX_SERDES_PIPELINE),
.RX_SERDES_PIPELINE(RX_SERDES_PIPELINE),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES),
.COUNT_125US(COUNT_125US),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE + n*(STAT_TX_CNT+STAT_RX_CNT)),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR[n])
)
ch_inst (
.xcvr_ctrl_clk(xcvr_ctrl_clk),
.xcvr_ctrl_rst(xcvr_ctrl_rst),
/*
* Common
*/
.xcvr_gtpowergood_out(ch_gtpowergood_out),
/*
* PLL out
*/
.xcvr_gtrefclk00_in(xcvr_gtrefclk00_in),
.xcvr_qpll0lock_out(ch_qpll0lock_out),
.xcvr_qpll0clk_out(ch_qpll0clk_out),
.xcvr_qpll0refclk_out(ch_qpll0refclk_out),
/*
* PLL in
*/
.xcvr_qpll0lock_in(xcvr_qpll0lock_out),
.xcvr_qpll0reset_out(),
.xcvr_qpll0clk_in(xcvr_qpll0clk_out),
.xcvr_qpll0refclk_in(xcvr_qpll0refclk_out),
/*
* Serial data
*/
.xcvr_txp(xcvr_txp[n]),
.xcvr_txn(xcvr_txn[n]),
.xcvr_rxp(xcvr_rxp[n]),
.xcvr_rxn(xcvr_rxn[n]),
/*
* MAC clocks
*/
.rx_clk(rx_clk[n]),
.rx_rst_in(rx_rst_in[n]),
.rx_rst_out(rx_rst_out[n]),
.tx_clk(tx_clk[n]),
.tx_rst_in(tx_rst_in[n]),
.tx_rst_out(tx_rst_out[n]),
.ptp_sample_clk(ptp_sample_clk[n]),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(s_axis_tx[n]),
.m_axis_tx_cpl(m_axis_tx_cpl[n]),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(m_axis_rx[n]),
/*
* PTP clock
*/
.tx_ptp_ts(tx_ptp_ts[n]),
.tx_ptp_ts_step(tx_ptp_ts_step[n]),
.rx_ptp_ts(rx_ptp_ts[n]),
.rx_ptp_ts_step(rx_ptp_ts_step[n]),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(tx_lfc_req[n]),
.tx_lfc_resend(tx_lfc_resend[n]),
.rx_lfc_en(rx_lfc_en[n]),
.rx_lfc_req(rx_lfc_req[n]),
.rx_lfc_ack(rx_lfc_ack[n]),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(tx_pfc_req[n]),
.tx_pfc_resend(tx_pfc_resend[n]),
.rx_pfc_en(rx_pfc_en[n]),
.rx_pfc_req(rx_pfc_req[n]),
.rx_pfc_ack(rx_pfc_ack[n]),
/*
* Pause interface
*/
.tx_lfc_pause_en(tx_lfc_pause_en[n]),
.tx_pause_req(tx_pause_req[n]),
.tx_pause_ack(tx_pause_ack[n]),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(axis_stat_int[n]),
/*
* Status
*/
.tx_start_packet(tx_start_packet[n]),
.stat_tx_byte(stat_tx_byte[n]),
.stat_tx_pkt_len(stat_tx_pkt_len[n]),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast[n]),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast[n]),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast[n]),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan[n]),
.stat_tx_pkt_good(stat_tx_pkt_good[n]),
.stat_tx_pkt_bad(stat_tx_pkt_bad[n]),
.stat_tx_err_oversize(stat_tx_err_oversize[n]),
.stat_tx_err_user(stat_tx_err_user[n]),
.stat_tx_err_underflow(stat_tx_err_underflow[n]),
.rx_start_packet(rx_start_packet[n]),
.rx_error_count(rx_error_count[n]),
.rx_block_lock(rx_block_lock[n]),
.rx_high_ber(rx_high_ber[n]),
.rx_status(rx_status[n]),
.stat_rx_byte(stat_rx_byte[n]),
.stat_rx_pkt_len(stat_rx_pkt_len[n]),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment[n]),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber[n]),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast[n]),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast[n]),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast[n]),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan[n]),
.stat_rx_pkt_good(stat_rx_pkt_good[n]),
.stat_rx_pkt_bad(stat_rx_pkt_bad[n]),
.stat_rx_err_oversize(stat_rx_err_oversize[n]),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs[n]),
.stat_rx_err_bad_block(stat_rx_err_bad_block[n]),
.stat_rx_err_framing(stat_rx_err_framing[n]),
.stat_rx_err_preamble(stat_rx_err_preamble[n]),
.stat_rx_fifo_drop(stat_rx_fifo_drop[n]),
.stat_tx_mcf(stat_tx_mcf[n]),
.stat_rx_mcf(stat_rx_mcf[n]),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt[n]),
.stat_tx_lfc_xon(stat_tx_lfc_xon[n]),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff[n]),
.stat_tx_lfc_paused(stat_tx_lfc_paused[n]),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt[n]),
.stat_tx_pfc_xon(stat_tx_pfc_xon[n]),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff[n]),
.stat_tx_pfc_paused(stat_tx_pfc_paused[n]),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt[n]),
.stat_rx_lfc_xon(stat_rx_lfc_xon[n]),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff[n]),
.stat_rx_lfc_paused(stat_rx_lfc_paused[n]),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt[n]),
.stat_rx_pfc_xon(stat_rx_pfc_xon[n]),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff[n]),
.stat_rx_pfc_paused(stat_rx_pfc_paused[n]),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len[n]),
.cfg_tx_ifg(cfg_tx_ifg[n]),
.cfg_tx_enable(cfg_tx_enable[n]),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len[n]),
.cfg_rx_enable(cfg_rx_enable[n]),
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable[n]),
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable[n]),
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast[n]),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast[n]),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast[n]),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast[n]),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src[n]),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src[n]),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type[n]),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc[n]),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc[n]),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc[n]),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc[n]),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward[n]),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable[n]),
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst[n]),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src[n]),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type[n]),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode[n]),
.cfg_tx_lfc_en(cfg_tx_lfc_en[n]),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta[n]),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh[n]),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst[n]),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src[n]),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type[n]),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode[n]),
.cfg_tx_pfc_en(cfg_tx_pfc_en[n]),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta[n]),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh[n]),
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode[n]),
.cfg_rx_lfc_en(cfg_rx_lfc_en[n]),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode[n]),
.cfg_rx_pfc_en(cfg_rx_pfc_en[n])
);
end
endmodule
`resetall

View File

@@ -0,0 +1,529 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* Transceiver and MAC/PHY wrapper for UltraScale/UltraScale+
*/
module taxi_eth_mac_25g_us_ch #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtexuplus",
parameter logic HAS_COMMON = 1'b1,
// GT type
parameter string GT_TYPE = "GTY",
// GT parameters
parameter logic GT_TX_POLARITY = 1'b0,
parameter logic GT_RX_POLARITY = 1'b0,
// MAC/PHY parameters
parameter logic PADDING_EN = 1'b1,
parameter logic DIC_EN = 1'b1,
parameter MIN_FRAME_LEN = 64,
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,
parameter logic PRBS31_EN = 1'b0,
parameter TX_SERDES_PIPELINE = 1,
parameter RX_SERDES_PIPELINE = 1,
parameter BITSLIP_HIGH_CYCLES = 0,
parameter BITSLIP_LOW_CYCLES = 7,
parameter COUNT_125US = 125000/6.4,
parameter logic STAT_EN = 1'b0,
parameter STAT_TX_LEVEL = 1,
parameter STAT_RX_LEVEL = 1,
parameter STAT_ID_BASE = 0,
parameter STAT_UPDATE_PERIOD = 1024,
parameter logic STAT_STR_EN = 1'b0,
parameter logic [8*8-1:0] STAT_PREFIX_STR = "MAC"
)
(
input wire logic xcvr_ctrl_clk,
input wire logic xcvr_ctrl_rst,
/*
* Common
*/
output wire logic xcvr_gtpowergood_out,
/*
* PLL out
*/
input wire logic xcvr_gtrefclk00_in,
output wire logic xcvr_qpll0lock_out,
output wire logic xcvr_qpll0clk_out,
output wire logic xcvr_qpll0refclk_out,
/*
* PLL in
*/
input wire logic xcvr_qpll0lock_in,
output wire logic xcvr_qpll0reset_out,
input wire logic xcvr_qpll0clk_in,
input wire logic xcvr_qpll0refclk_in,
/*
* Serial data
*/
output wire logic xcvr_txp,
output wire logic xcvr_txn,
input wire logic xcvr_rxp,
input wire logic xcvr_rxn,
/*
* MAC clocks
*/
output wire logic rx_clk,
input wire logic rx_rst_in,
output wire logic rx_rst_out,
output wire logic tx_clk,
input wire logic tx_rst_in,
output wire logic tx_rst_out,
input wire logic ptp_sample_clk,
/*
* Transmit interface (AXI stream)
*/
taxi_axis_if.snk s_axis_tx,
taxi_axis_if.src m_axis_tx_cpl,
/*
* Receive interface (AXI stream)
*/
taxi_axis_if.src m_axis_rx,
/*
* PTP clock
*/
input wire logic [PTP_TS_W-1:0] tx_ptp_ts = '0,
input wire logic tx_ptp_ts_step = 1'b0,
input wire logic [PTP_TS_W-1:0] rx_ptp_ts = '0,
input wire logic rx_ptp_ts_step = 1'b0,
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
input wire logic tx_lfc_req = 1'b0,
input wire logic tx_lfc_resend = 1'b0,
input wire logic rx_lfc_en = 1'b0,
output wire logic rx_lfc_req,
input wire logic rx_lfc_ack = 1'b0,
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
input wire logic [7:0] tx_pfc_req = '0,
input wire logic tx_pfc_resend = 1'b0,
input wire logic [7:0] rx_pfc_en = '0,
output wire logic [7:0] rx_pfc_req,
input wire logic [7:0] rx_pfc_ack = '0,
/*
* Pause interface
*/
input wire logic tx_lfc_pause_en = 1'b0,
input wire logic tx_pause_req = 1'b0,
output wire logic tx_pause_ack,
/*
* Statistics
*/
input wire logic stat_clk,
input wire logic stat_rst,
taxi_axis_if.src m_axis_stat,
/*
* Status
*/
output wire logic [1:0] tx_start_packet,
output wire logic [3:0] stat_tx_byte,
output wire logic [15:0] stat_tx_pkt_len,
output wire logic stat_tx_pkt_ucast,
output wire logic stat_tx_pkt_mcast,
output wire logic stat_tx_pkt_bcast,
output wire logic stat_tx_pkt_vlan,
output wire logic stat_tx_pkt_good,
output wire logic stat_tx_pkt_bad,
output wire logic stat_tx_err_oversize,
output wire logic stat_tx_err_user,
output wire logic stat_tx_err_underflow,
output wire logic [1:0] rx_start_packet,
output wire logic [6:0] rx_error_count,
output wire logic rx_block_lock,
output wire logic rx_high_ber,
output wire logic rx_status,
output wire logic [3:0] stat_rx_byte,
output wire logic [15:0] stat_rx_pkt_len,
output wire logic stat_rx_pkt_fragment,
output wire logic stat_rx_pkt_jabber,
output wire logic stat_rx_pkt_ucast,
output wire logic stat_rx_pkt_mcast,
output wire logic stat_rx_pkt_bcast,
output wire logic stat_rx_pkt_vlan,
output wire logic stat_rx_pkt_good,
output wire logic stat_rx_pkt_bad,
output wire logic stat_rx_err_oversize,
output wire logic stat_rx_err_bad_fcs,
output wire logic stat_rx_err_bad_block,
output wire logic stat_rx_err_framing,
output wire logic stat_rx_err_preamble,
input wire logic stat_rx_fifo_drop = 1'b0,
output wire logic stat_tx_mcf,
output wire logic stat_rx_mcf,
output wire logic stat_tx_lfc_pkt,
output wire logic stat_tx_lfc_xon,
output wire logic stat_tx_lfc_xoff,
output wire logic stat_tx_lfc_paused,
output wire logic stat_tx_pfc_pkt,
output wire logic [7:0] stat_tx_pfc_xon,
output wire logic [7:0] stat_tx_pfc_xoff,
output wire logic [7:0] stat_tx_pfc_paused,
output wire logic stat_rx_lfc_pkt,
output wire logic stat_rx_lfc_xon,
output wire logic stat_rx_lfc_xoff,
output wire logic stat_rx_lfc_paused,
output wire logic stat_rx_pfc_pkt,
output wire logic [7:0] stat_rx_pfc_xon,
output wire logic [7:0] stat_rx_pfc_xoff,
output wire logic [7:0] stat_rx_pfc_paused,
/*
* Configuration
*/
input wire logic [15:0] cfg_tx_max_pkt_len = 16'd1518,
input wire logic [7:0] cfg_tx_ifg = 8'd12,
input wire logic cfg_tx_enable = 1'b1,
input wire logic [15:0] cfg_rx_max_pkt_len = 16'd1518,
input wire logic cfg_rx_enable = 1'b1,
input wire logic cfg_tx_prbs31_enable = 1'b0,
input wire logic cfg_rx_prbs31_enable = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_dst_mcast = 48'h01_80_C2_00_00_01,
input wire logic cfg_mcf_rx_check_eth_dst_mcast = 1'b1,
input wire logic [47:0] cfg_mcf_rx_eth_dst_ucast = 48'd0,
input wire logic cfg_mcf_rx_check_eth_dst_ucast = 1'b0,
input wire logic [47:0] cfg_mcf_rx_eth_src = 48'd0,
input wire logic cfg_mcf_rx_check_eth_src = 1'b0,
input wire logic [15:0] cfg_mcf_rx_eth_type = 16'h8808,
input wire logic [15:0] cfg_mcf_rx_opcode_lfc = 16'h0001,
input wire logic cfg_mcf_rx_check_opcode_lfc = 1'b1,
input wire logic [15:0] cfg_mcf_rx_opcode_pfc = 16'h0101,
input wire logic cfg_mcf_rx_check_opcode_pfc = 1'b1,
input wire logic cfg_mcf_rx_forward = 1'b0,
input wire logic cfg_mcf_rx_enable = 1'b0,
input wire logic [47:0] cfg_tx_lfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_lfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_lfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_lfc_opcode = 16'h0001,
input wire logic cfg_tx_lfc_en = 1'b0,
input wire logic [15:0] cfg_tx_lfc_quanta = 16'hffff,
input wire logic [15:0] cfg_tx_lfc_refresh = 16'h7fff,
input wire logic [47:0] cfg_tx_pfc_eth_dst = 48'h01_80_C2_00_00_01,
input wire logic [47:0] cfg_tx_pfc_eth_src = 48'h80_23_31_43_54_4C,
input wire logic [15:0] cfg_tx_pfc_eth_type = 16'h8808,
input wire logic [15:0] cfg_tx_pfc_opcode = 16'h0101,
input wire logic cfg_tx_pfc_en = 1'b0,
input wire logic [15:0] cfg_tx_pfc_quanta[8] = '{8{16'hffff}},
input wire logic [15:0] cfg_tx_pfc_refresh[8] = '{8{16'h7fff}},
input wire logic [15:0] cfg_rx_lfc_opcode = 16'h0001,
input wire logic cfg_rx_lfc_en = 1'b0,
input wire logic [15:0] cfg_rx_pfc_opcode = 16'h0101,
input wire logic cfg_rx_pfc_en = 1'b0
);
localparam DATA_W = 64;
localparam HDR_W = 2;
wire rx_reset_req;
wire [5:0] gt_txheader;
wire [63:0] gt_txdata;
wire gt_rxgearboxslip;
wire [5:0] gt_rxheader;
wire [1:0] gt_rxheadervalid;
wire [63:0] gt_rxdata;
wire [1:0] gt_rxdatavalid;
taxi_eth_phy_25g_us_gt #(
.SIM(SIM),
.VENDOR(VENDOR),
.FAMILY(FAMILY),
.HAS_COMMON(HAS_COMMON),
.GT_TYPE(GT_TYPE),
.GT_TX_POLARITY(GT_TX_POLARITY),
.GT_RX_POLARITY(GT_RX_POLARITY)
)
gt_inst (
.xcvr_ctrl_clk(xcvr_ctrl_clk),
.xcvr_ctrl_rst(xcvr_ctrl_rst),
/*
* Common
*/
.xcvr_gtpowergood_out(xcvr_gtpowergood_out),
/*
* PLL out
*/
.xcvr_gtrefclk00_in(xcvr_gtrefclk00_in),
.xcvr_qpll0lock_out(xcvr_qpll0lock_out),
.xcvr_qpll0clk_out(xcvr_qpll0clk_out),
.xcvr_qpll0refclk_out(xcvr_qpll0refclk_out),
/*
* PLL in
*/
.xcvr_qpll0lock_in(xcvr_qpll0lock_in),
.xcvr_qpll0reset_out(xcvr_qpll0reset_out),
.xcvr_qpll0clk_in(xcvr_qpll0clk_in),
.xcvr_qpll0refclk_in(xcvr_qpll0refclk_in),
/*
* Serial data
*/
.xcvr_txp(xcvr_txp),
.xcvr_txn(xcvr_txn),
.xcvr_rxp(xcvr_rxp),
.xcvr_rxn(xcvr_rxn),
/*
* GT user clocks
*/
.rx_clk(rx_clk),
.rx_rst_in(rx_rst_in || rx_reset_req),
.rx_rst_out(rx_rst_out),
.tx_clk(tx_clk),
.tx_rst_in(tx_rst_in),
.tx_rst_out(tx_rst_out),
/*
* Serdes interface
*/
.gt_txheader(gt_txheader),
.gt_txdata(gt_txdata),
.gt_rxgearboxslip(gt_rxgearboxslip),
.gt_rxheader(gt_rxheader),
.gt_rxheadervalid(gt_rxheadervalid),
.gt_rxdata(gt_rxdata),
.gt_rxdatavalid(gt_rxdatavalid)
);
wire [DATA_W-1:0] serdes_tx_data;
wire [HDR_W-1:0] serdes_tx_hdr;
wire [DATA_W-1:0] serdes_rx_data;
wire [HDR_W-1:0] serdes_rx_hdr;
wire serdes_rx_bitslip;
assign gt_txdata = serdes_tx_data;
assign gt_txheader = {4'd0, serdes_tx_hdr};
assign gt_rxgearboxslip = serdes_rx_bitslip;
if (!SIM) begin
assign serdes_rx_data = gt_rxdata;
assign serdes_rx_hdr = gt_rxheader[1:0];
end
taxi_eth_mac_phy_10g #(
.DATA_W(DATA_W),
.HDR_W(HDR_W),
.PADDING_EN(PADDING_EN),
.DIC_EN(DIC_EN),
.MIN_FRAME_LEN(MIN_FRAME_LEN),
.PTP_TS_EN(PTP_TS_EN),
.PTP_TS_FMT_TOD(PTP_TS_FMT_TOD),
.PTP_TS_W(PTP_TS_W),
.BIT_REVERSE(1'b1),
.SCRAMBLER_DISABLE(1'b0),
.PRBS31_EN(PRBS31_EN),
.TX_SERDES_PIPELINE(TX_SERDES_PIPELINE),
.RX_SERDES_PIPELINE(RX_SERDES_PIPELINE),
.BITSLIP_HIGH_CYCLES(BITSLIP_HIGH_CYCLES),
.BITSLIP_LOW_CYCLES(BITSLIP_LOW_CYCLES),
.COUNT_125US(COUNT_125US),
.STAT_EN(STAT_EN),
.STAT_TX_LEVEL(STAT_TX_LEVEL),
.STAT_RX_LEVEL(STAT_RX_LEVEL),
.STAT_ID_BASE(STAT_ID_BASE),
.STAT_UPDATE_PERIOD(STAT_UPDATE_PERIOD),
.STAT_STR_EN(STAT_STR_EN),
.STAT_PREFIX_STR(STAT_PREFIX_STR)
)
eth_mac_phy_10g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst_out),
.rx_clk(rx_clk),
.rx_rst(rx_rst_out),
/*
* Transmit interface (AXI stream)
*/
.s_axis_tx(s_axis_tx),
.m_axis_tx_cpl(m_axis_tx_cpl),
/*
* Receive interface (AXI stream)
*/
.m_axis_rx(m_axis_rx),
/*
* Serdes interface
*/
.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),
.serdes_rx_reset_req(rx_reset_req),
/*
* PTP
*/
.tx_ptp_ts(tx_ptp_ts),
.rx_ptp_ts(rx_ptp_ts),
/*
* Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE)
*/
.tx_lfc_req(tx_lfc_req),
.tx_lfc_resend(tx_lfc_resend),
.rx_lfc_en(rx_lfc_en),
.rx_lfc_req(rx_lfc_req),
.rx_lfc_ack(rx_lfc_ack),
/*
* Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC)
*/
.tx_pfc_req(tx_pfc_req),
.tx_pfc_resend(tx_pfc_resend),
.rx_pfc_en(rx_pfc_en),
.rx_pfc_req(rx_pfc_req),
.rx_pfc_ack(rx_pfc_ack),
/*
* Pause interface
*/
.tx_lfc_pause_en(tx_lfc_pause_en),
.tx_pause_req(tx_pause_req),
.tx_pause_ack(tx_pause_ack),
/*
* Statistics
*/
.stat_clk(stat_clk),
.stat_rst(stat_rst),
.m_axis_stat(m_axis_stat),
/*
* Status
*/
.tx_start_packet(tx_start_packet),
.stat_tx_byte(stat_tx_byte),
.stat_tx_pkt_len(stat_tx_pkt_len),
.stat_tx_pkt_ucast(stat_tx_pkt_ucast),
.stat_tx_pkt_mcast(stat_tx_pkt_mcast),
.stat_tx_pkt_bcast(stat_tx_pkt_bcast),
.stat_tx_pkt_vlan(stat_tx_pkt_vlan),
.stat_tx_pkt_good(stat_tx_pkt_good),
.stat_tx_pkt_bad(stat_tx_pkt_bad),
.stat_tx_err_oversize(stat_tx_err_oversize),
.stat_tx_err_user(stat_tx_err_user),
.stat_tx_err_underflow(stat_tx_err_underflow),
.rx_start_packet(rx_start_packet),
.rx_error_count(rx_error_count),
.rx_block_lock(rx_block_lock),
.rx_high_ber(rx_high_ber),
.rx_status(rx_status),
.stat_rx_byte(stat_rx_byte),
.stat_rx_pkt_len(stat_rx_pkt_len),
.stat_rx_pkt_fragment(stat_rx_pkt_fragment),
.stat_rx_pkt_jabber(stat_rx_pkt_jabber),
.stat_rx_pkt_ucast(stat_rx_pkt_ucast),
.stat_rx_pkt_mcast(stat_rx_pkt_mcast),
.stat_rx_pkt_bcast(stat_rx_pkt_bcast),
.stat_rx_pkt_vlan(stat_rx_pkt_vlan),
.stat_rx_pkt_good(stat_rx_pkt_good),
.stat_rx_pkt_bad(stat_rx_pkt_bad),
.stat_rx_err_oversize(stat_rx_err_oversize),
.stat_rx_err_bad_fcs(stat_rx_err_bad_fcs),
.stat_rx_err_bad_block(stat_rx_err_bad_block),
.stat_rx_err_framing(stat_rx_err_framing),
.stat_rx_err_preamble(stat_rx_err_preamble),
.stat_rx_fifo_drop(stat_rx_fifo_drop),
.stat_tx_mcf(stat_tx_mcf),
.stat_rx_mcf(stat_rx_mcf),
.stat_tx_lfc_pkt(stat_tx_lfc_pkt),
.stat_tx_lfc_xon(stat_tx_lfc_xon),
.stat_tx_lfc_xoff(stat_tx_lfc_xoff),
.stat_tx_lfc_paused(stat_tx_lfc_paused),
.stat_tx_pfc_pkt(stat_tx_pfc_pkt),
.stat_tx_pfc_xon(stat_tx_pfc_xon),
.stat_tx_pfc_xoff(stat_tx_pfc_xoff),
.stat_tx_pfc_paused(stat_tx_pfc_paused),
.stat_rx_lfc_pkt(stat_rx_lfc_pkt),
.stat_rx_lfc_xon(stat_rx_lfc_xon),
.stat_rx_lfc_xoff(stat_rx_lfc_xoff),
.stat_rx_lfc_paused(stat_rx_lfc_paused),
.stat_rx_pfc_pkt(stat_rx_pfc_pkt),
.stat_rx_pfc_xon(stat_rx_pfc_xon),
.stat_rx_pfc_xoff(stat_rx_pfc_xoff),
.stat_rx_pfc_paused(stat_rx_pfc_paused),
/*
* Configuration
*/
.cfg_tx_max_pkt_len(cfg_tx_max_pkt_len),
.cfg_tx_ifg(cfg_tx_ifg),
.cfg_tx_enable(cfg_tx_enable),
.cfg_rx_max_pkt_len(cfg_rx_max_pkt_len),
.cfg_rx_enable(cfg_rx_enable),
.cfg_tx_prbs31_enable(cfg_tx_prbs31_enable),
.cfg_rx_prbs31_enable(cfg_rx_prbs31_enable),
.cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast),
.cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast),
.cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast),
.cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast),
.cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src),
.cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src),
.cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type),
.cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc),
.cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc),
.cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc),
.cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc),
.cfg_mcf_rx_forward(cfg_mcf_rx_forward),
.cfg_mcf_rx_enable(cfg_mcf_rx_enable),
.cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst),
.cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src),
.cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type),
.cfg_tx_lfc_opcode(cfg_tx_lfc_opcode),
.cfg_tx_lfc_en(cfg_tx_lfc_en),
.cfg_tx_lfc_quanta(cfg_tx_lfc_quanta),
.cfg_tx_lfc_refresh(cfg_tx_lfc_refresh),
.cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst),
.cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src),
.cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type),
.cfg_tx_pfc_opcode(cfg_tx_pfc_opcode),
.cfg_tx_pfc_en(cfg_tx_pfc_en),
.cfg_tx_pfc_quanta(cfg_tx_pfc_quanta),
.cfg_tx_pfc_refresh(cfg_tx_pfc_refresh),
.cfg_rx_lfc_opcode(cfg_rx_lfc_opcode),
.cfg_rx_lfc_en(cfg_rx_lfc_en),
.cfg_rx_pfc_opcode(cfg_rx_pfc_opcode),
.cfg_rx_pfc_en(cfg_rx_pfc_en)
);
endmodule
`resetall

View File

@@ -0,0 +1,2 @@
taxi_eth_phy_25g_us_gt.sv
../lib/taxi/src/sync/rtl/taxi_sync_reset.sv

View File

@@ -0,0 +1,533 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* Transceiver wrapper for UltraScale/UltraScale+
*/
module taxi_eth_phy_25g_us_gt #
(
parameter logic SIM = 1'b0,
parameter string VENDOR = "XILINX",
parameter string FAMILY = "virtexuplus",
parameter logic HAS_COMMON = 1'b1,
// GT type
parameter string GT_TYPE = "GTY",
// GT parameters
parameter logic GT_TX_POLARITY = 1'b0,
parameter logic GT_RX_POLARITY = 1'b0
)
(
input wire logic xcvr_ctrl_clk,
input wire logic xcvr_ctrl_rst,
/*
* Common
*/
output wire logic xcvr_gtpowergood_out,
/*
* PLL out
*/
input wire logic xcvr_gtrefclk00_in,
output wire logic xcvr_qpll0lock_out,
output wire logic xcvr_qpll0clk_out,
output wire logic xcvr_qpll0refclk_out,
/*
* PLL in
*/
input wire logic xcvr_qpll0lock_in,
output wire logic xcvr_qpll0reset_out,
input wire logic xcvr_qpll0clk_in,
input wire logic xcvr_qpll0refclk_in,
/*
* Serial data
*/
output wire logic xcvr_txp,
output wire logic xcvr_txn,
input wire logic xcvr_rxp,
input wire logic xcvr_rxn,
/*
* GT user clocks
*/
output wire logic rx_clk,
input wire logic rx_rst_in,
output wire logic rx_rst_out,
output wire logic tx_clk,
input wire logic tx_rst_in,
output wire logic tx_rst_out,
/*
* Serdes interface
*/
input wire logic [5:0] gt_txheader,
input wire logic [63:0] gt_txdata,
input wire logic gt_rxgearboxslip,
output wire logic [5:0] gt_rxheader,
output wire logic [1:0] gt_rxheadervalid,
output wire logic [63:0] gt_rxdata,
output wire logic [1:0] gt_rxdatavalid
);
localparam GT_USP = FAMILY == "kintexuplus" || FAMILY == "virtexuplus" || FAMILY == "virtexuplusHBM"
|| FAMILY == "virtexuplus58G" || FAMILY == "zynquplus" || FAMILY == "zynquplusRFSOC";
wire gt_reset_tx_datapath = tx_rst_in;
wire gt_reset_rx_datapath = rx_rst_in;
wire gt_reset_tx_done;
wire gt_reset_rx_done;
taxi_sync_reset #(
.N(4)
)
tx_reset_sync_inst (
.clk(tx_clk),
.rst(!gt_reset_tx_done || tx_rst_in),
.out(tx_rst_out)
);
taxi_sync_reset #(
.N(4)
)
rx_reset_sync_inst (
.clk(rx_clk),
.rst(!gt_reset_rx_done || rx_rst_in),
.out(rx_rst_out)
);
if (SIM) begin : xcvr
// simulation (no GT core)
assign xcvr_gtpowergood_out = 1'b1;
assign xcvr_qpll0lock_out = 1'b1;
assign xcvr_qpll0clk_out = 1'b0;
assign xcvr_qpll0refclk_out = 1'b0;
assign gt_reset_tx_done = !xcvr_ctrl_rst;
assign gt_reset_rx_done = !xcvr_ctrl_rst;
end else if (HAS_COMMON && GT_TYPE == "GTY" && GT_USP) begin : xcvr
// UltraScale+ GTY (with common)
taxi_eth_phy_25g_us_gty_full
taxi_eth_phy_25g_us_gty_full_inst (
// Common
.gtwiz_reset_clk_freerun_in(xcvr_ctrl_clk),
.gtwiz_reset_all_in(xcvr_ctrl_rst),
.gtpowergood_out(xcvr_gtpowergood_out),
// PLL
.gtrefclk00_in(xcvr_gtrefclk00_in),
.qpll0lock_out(xcvr_qpll0lock_out),
.qpll0outclk_out(xcvr_qpll0clk_out),
.qpll0outrefclk_out(xcvr_qpll0refclk_out),
// Serial data
.gtytxp_out(xcvr_txp),
.gtytxn_out(xcvr_txn),
.gtyrxp_in(xcvr_rxp),
.gtyrxn_in(xcvr_rxn),
// Transmit
.gtwiz_userclk_tx_reset_in(1'b0),
.gtwiz_userclk_tx_srcclk_out(),
.gtwiz_userclk_tx_usrclk_out(),
.gtwiz_userclk_tx_usrclk2_out(tx_clk),
.gtwiz_userclk_tx_active_out(),
.gtwiz_reset_tx_pll_and_datapath_in(1'b0),
.gtwiz_reset_tx_datapath_in(gt_reset_tx_datapath),
.gtwiz_reset_tx_done_out(gt_reset_tx_done),
.txpmaresetdone_out(),
.txprgdivresetdone_out(),
.txpolarity_in(GT_TX_POLARITY),
.gtwiz_userdata_tx_in(gt_txdata),
.txheader_in(gt_txheader),
.txsequence_in(7'b0),
// Receive
.gtwiz_userclk_rx_reset_in(1'b0),
.gtwiz_userclk_rx_srcclk_out(),
.gtwiz_userclk_rx_usrclk_out(),
.gtwiz_userclk_rx_usrclk2_out(rx_clk),
.gtwiz_userclk_rx_active_out(),
.gtwiz_reset_rx_pll_and_datapath_in(1'b0),
.gtwiz_reset_rx_datapath_in(gt_reset_rx_datapath),
.gtwiz_reset_rx_cdr_stable_out(),
.gtwiz_reset_rx_done_out(gt_reset_rx_done),
.rxpmaresetdone_out(),
.rxprgdivresetdone_out(),
.rxpolarity_in(GT_RX_POLARITY),
.rxgearboxslip_in(gt_rxgearboxslip),
.gtwiz_userdata_rx_out(gt_rxdata),
.rxdatavalid_out(gt_rxdatavalid),
.rxheader_out(gt_rxheader),
.rxheadervalid_out(gt_rxheadervalid),
.rxstartofseq_out()
);
assign xcvr_qpll0reset_out = 1'b0;
end else if (HAS_COMMON && GT_TYPE == "GTH" && GT_USP) begin : xcvr
// UltraScale+ GTH (with common)
taxi_eth_phy_25g_us_gth_full
taxi_eth_phy_25g_us_gth_full_inst (
// Common
.gtwiz_reset_clk_freerun_in(xcvr_ctrl_clk),
.gtwiz_reset_all_in(xcvr_ctrl_rst),
.gtpowergood_out(xcvr_gtpowergood_out),
// PLL
.gtrefclk00_in(xcvr_gtrefclk00_in),
.qpll0lock_out(xcvr_qpll0lock_out),
.qpll0outclk_out(xcvr_qpll0clk_out),
.qpll0outrefclk_out(xcvr_qpll0refclk_out),
// Serial data
.gthtxp_out(xcvr_txp),
.gthtxn_out(xcvr_txn),
.gthrxp_in(xcvr_rxp),
.gthrxn_in(xcvr_rxn),
// Transmit
.gtwiz_userclk_tx_reset_in(1'b0),
.gtwiz_userclk_tx_srcclk_out(),
.gtwiz_userclk_tx_usrclk_out(),
.gtwiz_userclk_tx_usrclk2_out(tx_clk),
.gtwiz_userclk_tx_active_out(),
.gtwiz_reset_tx_pll_and_datapath_in(1'b0),
.gtwiz_reset_tx_datapath_in(gt_reset_tx_datapath),
.gtwiz_reset_tx_done_out(gt_reset_tx_done),
.txpmaresetdone_out(),
.txprgdivresetdone_out(),
.txpolarity_in(GT_TX_POLARITY),
.gtwiz_userdata_tx_in(gt_txdata),
.txheader_in(gt_txheader),
.txsequence_in(7'b0),
// Receive
.gtwiz_userclk_rx_reset_in(1'b0),
.gtwiz_userclk_rx_srcclk_out(),
.gtwiz_userclk_rx_usrclk_out(),
.gtwiz_userclk_rx_usrclk2_out(rx_clk),
.gtwiz_userclk_rx_active_out(),
.gtwiz_reset_rx_pll_and_datapath_in(1'b0),
.gtwiz_reset_rx_datapath_in(gt_reset_rx_datapath),
.gtwiz_reset_rx_cdr_stable_out(),
.gtwiz_reset_rx_done_out(gt_reset_rx_done),
.rxpmaresetdone_out(),
.rxprgdivresetdone_out(),
.rxpolarity_in(GT_RX_POLARITY),
.rxgearboxslip_in(gt_rxgearboxslip),
.gtwiz_userdata_rx_out(gt_rxdata),
.rxdatavalid_out(gt_rxdatavalid),
.rxheader_out(gt_rxheader),
.rxheadervalid_out(gt_rxheadervalid),
.rxstartofseq_out()
);
assign xcvr_qpll0reset_out = 1'b0;
end else if (HAS_COMMON && GT_TYPE == "GTY" && !GT_USP) begin : xcvr
// UltraScale GTY (with common)
taxi_eth_phy_25g_us_gty_full
taxi_eth_phy_25g_us_gty_full_inst (
// Common
.gtwiz_reset_clk_freerun_in(xcvr_ctrl_clk),
.gtwiz_reset_all_in(xcvr_ctrl_rst),
.gtpowergood_out(xcvr_gtpowergood_out),
// PLL
.gtrefclk00_in(xcvr_gtrefclk00_in),
.qpll0lock_out(xcvr_qpll0lock_out),
.qpll0outclk_out(xcvr_qpll0clk_out),
.qpll0outrefclk_out(xcvr_qpll0refclk_out),
// Serial data
.gtytxp_out(xcvr_txp),
.gtytxn_out(xcvr_txn),
.gtyrxp_in(xcvr_rxp),
.gtyrxn_in(xcvr_rxn),
// Transmit
.gtwiz_userclk_tx_reset_in(1'b0),
.gtwiz_userclk_tx_srcclk_out(),
.gtwiz_userclk_tx_usrclk_out(),
.gtwiz_userclk_tx_usrclk2_out(tx_clk),
.gtwiz_userclk_tx_active_out(),
.gtwiz_reset_tx_pll_and_datapath_in(1'b0),
.gtwiz_reset_tx_datapath_in(gt_reset_tx_datapath),
.gtwiz_reset_tx_done_out(gt_reset_tx_done),
.txpmaresetdone_out(),
.txprgdivresetdone_out(),
.txpolarity_in(GT_TX_POLARITY),
.gtwiz_userdata_tx_in(gt_txdata),
.txheader_in(gt_txheader),
.txsequence_in(7'b0),
// Receive
.gtwiz_userclk_rx_reset_in(1'b0),
.gtwiz_userclk_rx_srcclk_out(),
.gtwiz_userclk_rx_usrclk_out(),
.gtwiz_userclk_rx_usrclk2_out(rx_clk),
.gtwiz_userclk_rx_active_out(),
.gtwiz_reset_rx_pll_and_datapath_in(1'b0),
.gtwiz_reset_rx_datapath_in(gt_reset_rx_datapath),
.gtwiz_reset_rx_cdr_stable_out(),
.gtwiz_reset_rx_done_out(gt_reset_rx_done),
.rxpmaresetdone_out(),
.rxprgdivresetdone_out(),
.rxpolarity_in(GT_RX_POLARITY),
.rxgearboxslip_in(gt_rxgearboxslip),
.gtwiz_userdata_rx_out(gt_rxdata),
.rxdatavalid_out(gt_rxdatavalid),
.rxheader_out(gt_rxheader),
.rxheadervalid_out(gt_rxheadervalid),
.rxstartofseq_out()
);
assign xcvr_qpll0reset_out = 1'b0;
end else if (HAS_COMMON && GT_TYPE == "GTH" && !GT_USP) begin : xcvr
// UltraScale GTH (with common)
taxi_eth_phy_25g_us_gth_full
taxi_eth_phy_25g_us_gth_full_inst (
// Common
.gtwiz_reset_clk_freerun_in(xcvr_ctrl_clk),
.gtwiz_reset_all_in(xcvr_ctrl_rst),
.gtpowergood_out(xcvr_gtpowergood_out),
// PLL
.gtrefclk00_in(xcvr_gtrefclk00_in),
.qpll0lock_out(xcvr_qpll0lock_out),
.qpll0outclk_out(xcvr_qpll0clk_out),
.qpll0outrefclk_out(xcvr_qpll0refclk_out),
// Serial data
.gthtxp_out(xcvr_txp),
.gthtxn_out(xcvr_txn),
.gthrxp_in(xcvr_rxp),
.gthrxn_in(xcvr_rxn),
// Transmit
.gtwiz_userclk_tx_reset_in(1'b0),
.gtwiz_userclk_tx_srcclk_out(),
.gtwiz_userclk_tx_usrclk_out(),
.gtwiz_userclk_tx_usrclk2_out(tx_clk),
.gtwiz_userclk_tx_active_out(),
.gtwiz_reset_tx_pll_and_datapath_in(1'b0),
.gtwiz_reset_tx_datapath_in(gt_reset_tx_datapath),
.gtwiz_reset_tx_done_out(gt_reset_tx_done),
.txpmaresetdone_out(),
.txprgdivresetdone_out(),
.txpolarity_in(GT_TX_POLARITY),
.gtwiz_userdata_tx_in(gt_txdata),
.txheader_in(gt_txheader),
.txsequence_in(7'b0),
// Receive
.gtwiz_userclk_rx_reset_in(1'b0),
.gtwiz_userclk_rx_srcclk_out(),
.gtwiz_userclk_rx_usrclk_out(),
.gtwiz_userclk_rx_usrclk2_out(rx_clk),
.gtwiz_userclk_rx_active_out(),
.gtwiz_reset_rx_pll_and_datapath_in(1'b0),
.gtwiz_reset_rx_datapath_in(gt_reset_rx_datapath),
.gtwiz_reset_rx_cdr_stable_out(),
.gtwiz_reset_rx_done_out(gt_reset_rx_done),
.rxpmaresetdone_out(),
.rxprgdivresetdone_out(),
.rxpolarity_in(GT_RX_POLARITY),
.rxgearboxslip_in(gt_rxgearboxslip),
.gtwiz_userdata_rx_out(gt_rxdata),
.rxdatavalid_out(gt_rxdatavalid),
.rxheader_out(gt_rxheader),
.rxheadervalid_out(gt_rxheadervalid),
.rxstartofseq_out()
);
assign xcvr_qpll0reset_out = 1'b0;
end else if (!HAS_COMMON && GT_TYPE == "GTY") begin : xcvr
// UltraScale/UltraScale+ GTY (channel only)
taxi_eth_phy_25g_us_gty_channel
taxi_eth_phy_25g_us_gty_channel_inst (
// Common
.gtwiz_reset_clk_freerun_in(xcvr_ctrl_clk),
.gtwiz_reset_all_in(xcvr_ctrl_rst),
.gtpowergood_out(xcvr_gtpowergood_out),
// PLL
.gtwiz_reset_qpll0lock_in(xcvr_qpll0lock_in),
.gtwiz_reset_qpll0reset_out(xcvr_qpll0reset_out),
.qpll0clk_in(xcvr_qpll0clk_in),
.qpll0refclk_in(xcvr_qpll0refclk_in),
.qpll1clk_in(1'b0),
.qpll1refclk_in(1'b0),
// Serial data
.gtytxp_out(xcvr_txp),
.gtytxn_out(xcvr_txn),
.gtyrxp_in(xcvr_rxp),
.gtyrxn_in(xcvr_rxn),
// Transmit
.gtwiz_userclk_tx_reset_in(1'b0),
.gtwiz_userclk_tx_srcclk_out(),
.gtwiz_userclk_tx_usrclk_out(),
.gtwiz_userclk_tx_usrclk2_out(tx_clk),
.gtwiz_userclk_tx_active_out(),
.gtwiz_reset_tx_pll_and_datapath_in(1'b0),
.gtwiz_reset_tx_datapath_in(gt_reset_tx_datapath),
.gtwiz_reset_tx_done_out(gt_reset_tx_done),
.txpmaresetdone_out(),
.txprgdivresetdone_out(),
.txpolarity_in(GT_TX_POLARITY),
.gtwiz_userdata_tx_in(gt_txdata),
.txheader_in(gt_txheader),
.txsequence_in(7'b0),
// Receive
.gtwiz_userclk_rx_reset_in(1'b0),
.gtwiz_userclk_rx_srcclk_out(),
.gtwiz_userclk_rx_usrclk_out(),
.gtwiz_userclk_rx_usrclk2_out(rx_clk),
.gtwiz_userclk_rx_active_out(),
.gtwiz_reset_rx_pll_and_datapath_in(1'b0),
.gtwiz_reset_rx_datapath_in(gt_reset_rx_datapath),
.gtwiz_reset_rx_cdr_stable_out(),
.gtwiz_reset_rx_done_out(gt_reset_rx_done),
.rxpmaresetdone_out(),
.rxprgdivresetdone_out(),
.rxpolarity_in(GT_RX_POLARITY),
.rxgearboxslip_in(gt_rxgearboxslip),
.gtwiz_userdata_rx_out(gt_rxdata),
.rxdatavalid_out(gt_rxdatavalid),
.rxheader_out(gt_rxheader),
.rxheadervalid_out(gt_rxheadervalid),
.rxstartofseq_out()
);
assign xcvr_qpll0lock_out = 1'b0;
assign xcvr_qpll0clk_out = 1'b0;
assign xcvr_qpll0refclk_out = 1'b0;
end else if (!HAS_COMMON && GT_TYPE == "GTH") begin : xcvr
// UltraScale/UltraScale+ GTY (channel only)
taxi_eth_phy_25g_us_gth_channel
taxi_eth_phy_25g_us_gth_channel_inst (
// Common
.gtwiz_reset_clk_freerun_in(xcvr_ctrl_clk),
.gtwiz_reset_all_in(xcvr_ctrl_rst),
.gtpowergood_out(xcvr_gtpowergood_out),
// PLL
.gtwiz_reset_qpll0lock_in(xcvr_qpll0lock_in),
.gtwiz_reset_qpll0reset_out(xcvr_qpll0reset_out),
.qpll0clk_in(xcvr_qpll0clk_in),
.qpll0refclk_in(xcvr_qpll0refclk_in),
.qpll1clk_in(1'b0),
.qpll1refclk_in(1'b0),
// Serial data
.gthtxp_out(xcvr_txp),
.gthtxn_out(xcvr_txn),
.gthrxp_in(xcvr_rxp),
.gthrxn_in(xcvr_rxn),
// Transmit
.gtwiz_userclk_tx_reset_in(1'b0),
.gtwiz_userclk_tx_srcclk_out(),
.gtwiz_userclk_tx_usrclk_out(),
.gtwiz_userclk_tx_usrclk2_out(tx_clk),
.gtwiz_userclk_tx_active_out(),
.gtwiz_reset_tx_pll_and_datapath_in(1'b0),
.gtwiz_reset_tx_datapath_in(gt_reset_tx_datapath),
.gtwiz_reset_tx_done_out(gt_reset_tx_done),
.txpmaresetdone_out(),
.txprgdivresetdone_out(),
.txpolarity_in(GT_TX_POLARITY),
.gtwiz_userdata_tx_in(gt_txdata),
.txheader_in(gt_txheader),
.txsequence_in(7'b0),
// Receive
.gtwiz_userclk_rx_reset_in(1'b0),
.gtwiz_userclk_rx_srcclk_out(),
.gtwiz_userclk_rx_usrclk_out(),
.gtwiz_userclk_rx_usrclk2_out(rx_clk),
.gtwiz_userclk_rx_active_out(),
.gtwiz_reset_rx_pll_and_datapath_in(1'b0),
.gtwiz_reset_rx_datapath_in(gt_reset_rx_datapath),
.gtwiz_reset_rx_cdr_stable_out(),
.gtwiz_reset_rx_done_out(gt_reset_rx_done),
.rxpmaresetdone_out(),
.rxprgdivresetdone_out(),
.rxpolarity_in(GT_RX_POLARITY),
.rxgearboxslip_in(gt_rxgearboxslip),
.gtwiz_userdata_rx_out(gt_rxdata),
.rxdatavalid_out(gt_rxdatavalid),
.rxheader_out(gt_rxheader),
.rxheadervalid_out(gt_rxheadervalid),
.rxstartofseq_out()
);
assign xcvr_qpll0lock_out = 1'b0;
assign xcvr_qpll0clk_out = 1'b0;
assign xcvr_qpll0refclk_out = 1'b0;
end else begin
$fatal(0, "Error: invalid configuration (%m)");
end
endmodule
`resetall

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gth}
set preset {GTH-10GBASE-R}
set freerun_freq {125}
set line_rate {10.3125}
set refclk_freq {156.25}
set sec_line_rate {0}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width {32}
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gth}
set preset {GTH-10GBASE-R}
set freerun_freq {125}
set line_rate {10.3125}
set refclk_freq {161.1328125}
set sec_line_rate {0}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width {32}
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gth}
set preset {GTH-10GBASE-R}
set freerun_freq {125}
set line_rate {10.3125}
set refclk_freq {322.265625}
set sec_line_rate {0}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width {32}
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gty}
set preset {GTY-10GBASE-R}
set freerun_freq {125}
set line_rate {10.3125}
set refclk_freq {156.25}
set sec_line_rate {0}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width $user_data_width
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gty}
set preset {GTY-10GBASE-R}
set freerun_freq {125}
set line_rate {10.3125}
set refclk_freq {161.1328125}
set sec_line_rate {0}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width $user_data_width
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gty}
set preset {GTY-10GBASE-R}
set freerun_freq {125}
set line_rate {10.3125}
set refclk_freq {322.265625}
set sec_line_rate {0}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width $user_data_width
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gty}
set preset {GTY-10GBASE-R}
set freerun_freq {125}
set line_rate {25.78125}
set refclk_freq {156.25}
set sec_line_rate {10.3125}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width $user_data_width
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gty}
set preset {GTY-10GBASE-R}
set freerun_freq {125}
set line_rate {25.78125}
set refclk_freq {161.1328125}
set sec_line_rate {10.3125}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width $user_data_width
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config

View File

@@ -0,0 +1,79 @@
# SPDX-License-Identifier: CERN-OHL-S-2.0
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
set base_name {taxi_eth_phy_25g_us_gty}
set preset {GTY-10GBASE-R}
set freerun_freq {125}
set line_rate {25.78125}
set refclk_freq {322.265625}
set sec_line_rate {10.3125}
set sec_refclk_freq $refclk_freq
set qpll_fracn [expr {int(fmod($line_rate*1000/2 / $refclk_freq, 1)*pow(2, 24))}]
set sec_qpll_fracn [expr {int(fmod($sec_line_rate*1000/2 / $sec_refclk_freq, 1)*pow(2, 24))}]
set user_data_width {64}
set int_data_width $user_data_width
set rx_eq_mode {DFE}
set extra_ports [list]
set extra_pll_ports [list {qpll0lock_out}]
# channel polarity
lappend extra_ports txpolarity_in rxpolarity_in
set config [dict create]
dict set config TX_LINE_RATE $line_rate
dict set config TX_REFCLK_FREQUENCY $refclk_freq
dict set config TX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config TX_USER_DATA_WIDTH $user_data_width
dict set config TX_INT_DATA_WIDTH $int_data_width
dict set config RX_LINE_RATE $line_rate
dict set config RX_REFCLK_FREQUENCY $refclk_freq
dict set config RX_QPLL_FRACN_NUMERATOR $qpll_fracn
dict set config RX_USER_DATA_WIDTH $user_data_width
dict set config RX_INT_DATA_WIDTH $int_data_width
dict set config RX_EQ_MODE $rx_eq_mode
if {$sec_line_rate != 0} {
dict set config SECONDARY_QPLL_ENABLE true
dict set config SECONDARY_QPLL_FRACN_NUMERATOR $sec_qpll_fracn
dict set config SECONDARY_QPLL_LINE_RATE $sec_line_rate
dict set config SECONDARY_QPLL_REFCLK_FREQUENCY $sec_refclk_freq
} else {
dict set config SECONDARY_QPLL_ENABLE false
}
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {CORE}
dict set config LOCATE_RESET_CONTROLLER {CORE}
dict set config LOCATE_TX_USER_CLOCKING {CORE}
dict set config LOCATE_RX_USER_CLOCKING {CORE}
dict set config LOCATE_USER_DATA_WIDTH_SIZING {CORE}
dict set config FREERUN_FREQUENCY $freerun_freq
dict set config DISABLE_LOC_XDC {1}
proc create_gtwizard_ip {name preset config} {
create_ip -name gtwizard_ultrascale -vendor xilinx.com -library ip -module_name $name
set ip [get_ips $name]
set_property CONFIG.preset $preset $ip
set config_list {}
dict for {name value} $config {
lappend config_list "CONFIG.${name}" $value
}
set_property -dict $config_list $ip
}
# variant with channel and common
dict set config ENABLE_OPTIONAL_PORTS [concat $extra_pll_ports $extra_ports]
dict set config LOCATE_COMMON {CORE}
create_gtwizard_ip "${base_name}_full" $preset $config
# variant with channel only
dict set config ENABLE_OPTIONAL_PORTS $extra_ports
dict set config LOCATE_COMMON {EXAMPLE_DESIGN}
create_gtwizard_ip "${base_name}_channel" $preset $config