UberDDR3/delete_later/rtl/net/netpath.v

477 lines
12 KiB
Verilog

////////////////////////////////////////////////////////////////////////////////
//
// Filename: netpath.v
// {{{
// Project: 10Gb Ethernet switch
//
// Purpose: RTL for the digital path between the packet handling within the
// design, and the packet handling required to feed and drive the
// 10GBASE-T 66/64B encoders and decoders. This particular file is little
// more than glue logic, containing the following paths.
//
// From RX GTX to FPGA:
// {{{
// p64bscrambler -- Descrambles the incoming 66-bit words
// p642pkt -- Decodes the 66 bit words into 64 bit words, with
// active byte counts and a LAST word in packet
// indicator. This is where the packets are first converted to
// the AXIN protocol. That said, there's still no buffering
// available. Any drop in READY will likely cause a packet to be
// ABORTed here.
// dropshort -- ABORTs any packet that doesn't have a full 64 Bytes
// to it.
// crc_axin -- ABORTs any packet with a failing CRC.
// axinwidth -- Converts from a 64b width to a 128b width. This is
// a necessary first step before crossing clock domains.
// axincdc -- Crosses from the clock domain of the GTX receive
// transceiver to the system clock domain. The prior
// jump in width allows us to do this with no more than a
// guarantee that the system clock has to be faster than half the
// RX clock. The AXIN stream resulting from this CDC crossing
// then goes to the system.
//
// rx_activity -- A quick and dirty counter, used to light an LED
// indicating packets have been successfully read.
// }}}
// From FPGA to TX GTX:
// {{{
// axincdc -- Cross clock domains from system to tx clock. There's
// no speed requirement here, although the system clock
// should generall be fast enough to keep a long packet going if
// it cannot fit in the following buffers.
// axinwidth -- Once we move to the tx clock domain, we can downsize
// to the width required by the comms port: 64 bits.
// pktgate -- Once we get past this point, outgoing packets cannot
// be interrupted without getting dropped by the system
// following. To ensure we have a bit of a buffer lest things
// get interrupted, the packet gate waits for either 1) a full
// packet, or 2) a full buffer before forwarding a packet for
// transmission. The purpose here is to be able to ride through
// any hiccups in the source that might follow--due to clock
// mismatches or anything else.
// pkt2p64b -- Convert from our 64-bit AXI network packet protocol
// to the 66-bit word protocol used by the 10GBASE-T
// protocol. This includes 66/64b encoding.
// p64bscrambler -- As a second half of the encoding step, we scramble
// the incoming packet.
// }}}
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2023, Gisselquist Technology, LLC
// {{{
// This file is part of the ETH10G project.
//
// The ETH10G project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License"). You may not use this project,
// or this file, except in compliance with the License. You may obtain a copy
// of the License at
// }}}
// http://www.apache.org/licenses/LICENSE-2.0
// {{{
// Unless required by applicable law or agreed to in writing, files
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
`default_nettype none
// }}}
module netpath #(
parameter LGPKTGATE=6
) (
// {{{
input wire i_rx_clk, i_tx_clk, i_reset_n,
input wire i_sys_clk,
output wire o_link_up, o_activity,
// PHY interface
// {{{
input wire i_phy_fault,
input wire i_raw_valid,
input wire [65:0] i_raw_data,
//
input wire i_raw_ready,
output wire [65:0] o_raw_data,
// }}}
// Incoming packets to send
// {{{
input wire S_VALID,
output wire S_READY,
input wire [127:0] S_DATA,
input wire [3:0] S_BYTES,
input wire S_LAST,
input wire S_ABORT,
// }}}
// Outgoing received packets
// {{{
output wire M_VALID,
input wire M_READY,
output wire [127:0] M_DATA,
output wire [3:0] M_BYTES,
output wire M_LAST,
output wire M_ABORT
// }}}
// }}}
);
// Local declarations
// {{{
// The clock speed is nominally (10GHz * 66/64) / 64
// or about 156.25MHz (or 161.1428..MHz)
// Packets should keep the LED high for about 1/10th of a second or so
// or about 16M clock cyles. Our counter requires twice that much,
// so ...
//
localparam ACTMSB = 24;
// Verilator lint_off SYNCASYNCNET
reg rx_reset_n, tx_reset_n;
// Verilator lint_on SYNCASYNCNET
reg [1:0] rx_reset_pipe, tx_reset_pipe;
wire ign_rx_raw_ready;
wire rx_valid, rx_ready;
wire [65:0] rx_data;
wire remote_fault, local_fault;
wire rx_link_up, tx_link_up;
wire SRC_VALID, SRC_READY, SRC_LAST, SRC_ABORT;
wire [63:0] SRC_DATA;
wire [2:0] SRC_BYTES;
wire PKT_VALID, PKT_READY, PKT_LAST, PKT_ABORT, ign_high;
wire [63:0] PKT_DATA;
wire [2:0] PKT_BYTES;
wire ign_tx_raw_valid, tx_ready, ign_tx_high, ign_rx_high;
wire [65:0] tx_data;
reg [ACTMSB:0] tx_activity, rx_activity;
wire TXCK_VALID, TXCK_READY, TXCK_LAST, TXCK_ABORT;
wire [127:0] TXCK_DATA;
wire [3:0] TXCK_BYTES;
wire TXWD_VALID, TXWD_READY, TXWD_LAST, TXWD_ABORT;
wire [63:0] TXWD_DATA;
wire [2:0] TXWD_BYTES;
wire FULL_VALID, FULL_READY, FULL_LAST, FULL_ABORT;
wire [63:0] FULL_DATA;
wire [2:0] FULL_BYTES;
wire CRC_VALID, CRC_READY, CRC_LAST, CRC_ABORT;
wire [63:0] CRC_DATA;
wire [2:0] CRC_BYTES;
wire RXWD_VALID, RXWD_READY, RXWD_LAST, RXWD_ABORT;
wire [127:0] RXWD_DATA;
wire [3:0] RXWD_BYTES;
// wire RXCK_VALID, RXCK_READY, RXCK_LAST, RXCK_ABORT;
// wire [127:0] RXCK_DATA;
// wire [3:0] RXCK_BYTES;
// }}}
////////////////////////////////////////////////////////////////////////
//
// Synchronize resets
// {{{
always @(posedge i_rx_clk or negedge i_reset_n)
if (!i_reset_n)
{ rx_reset_n, rx_reset_pipe } <= 0;
else
{ rx_reset_n, rx_reset_pipe } <= { rx_reset_pipe, i_reset_n };
always @(posedge i_tx_clk or negedge i_reset_n)
if (!i_reset_n)
{ tx_reset_n, tx_reset_pipe } <= 0;
else
{ tx_reset_n, tx_reset_pipe } <= { tx_reset_pipe, i_reset_n };
// }}}
////////////////////////////////////////////////////////////////////////
//
// Incoming packet -> M_* processing
// {{{
p64bscrambler #(
.OPT_RX(1'b1)
) u_descrambler (
// {{{
.i_clk(i_rx_clk), .i_reset_n(rx_reset_n),
.i_valid(i_raw_valid),
.o_ready(ign_rx_raw_ready),
.i_data(i_raw_data),
//
.o_valid(rx_valid),
.i_ready(rx_ready),
.o_data(rx_data)
// }}}
);
p642pkt
u_p642pkt (
// {{{
.RX_CLK(i_rx_clk), .S_ARESETN(rx_reset_n),
//
.i_phy_fault(i_phy_fault),
.o_remote_fault(remote_fault),
.o_local_fault(local_fault),
.o_link_up(rx_link_up),
//
.RX_VALID(rx_valid),
.RX_DATA(rx_data),
//
.M_VALID(SRC_VALID),
.M_READY(SRC_READY),
.M_DATA(SRC_DATA),
.M_BYTES(SRC_BYTES),
.M_ABORT(SRC_ABORT),
.M_LAST(SRC_LAST)
// }}}
);
assign rx_ready = 1'b1;
dropshort #(
.DW(64)
) u_dropshort (
// {{{
.S_CLK(i_rx_clk), .S_ARESETN(rx_reset_n),
//
.S_VALID(SRC_VALID),
.S_READY(SRC_READY),
.S_DATA( SRC_DATA),
.S_BYTES(SRC_BYTES),
.S_ABORT(SRC_ABORT),
.S_LAST( SRC_LAST),
//
.M_VALID(PKT_VALID),
.M_READY(PKT_READY),
.M_DATA( PKT_DATA),
.M_BYTES(PKT_BYTES),
.M_ABORT(PKT_ABORT),
.M_LAST( PKT_LAST)
// }}}
);
crc_axin #(
.DATA_WIDTH(64), .OPT_SKIDBUFFER(1'b1), .OPT_LOWPOWER(1'b0)
) u_check_crc (
// {{{
.ACLK(i_rx_clk), .ARESETN(rx_reset_n),
.i_cfg_en(1'b1),
//
.S_AXIN_VALID(PKT_VALID),
.S_AXIN_READY(PKT_READY),
.S_AXIN_DATA( PKT_DATA),
.S_AXIN_BYTES({ (PKT_BYTES==0), PKT_BYTES }),
.S_AXIN_LAST( PKT_LAST),
.S_AXIN_ABORT(PKT_ABORT),
//
.M_AXIN_VALID(CRC_VALID),
.M_AXIN_READY(CRC_READY),
.M_AXIN_DATA( CRC_DATA),
.M_AXIN_BYTES({ ign_high, CRC_BYTES }),
.M_AXIN_LAST( CRC_LAST),
.M_AXIN_ABORT(CRC_ABORT)
// }}}
);
axinwidth #(
.IW(64), .OW(128)
) u_rxwidth (
// {{{
.ACLK(i_rx_clk), .ARESETN(rx_reset_n),
//
.S_AXIN_VALID(CRC_VALID),
.S_AXIN_READY(CRC_READY),
.S_AXIN_DATA( CRC_DATA),
.S_AXIN_BYTES({ (CRC_BYTES==0), CRC_BYTES }),
.S_AXIN_LAST( CRC_LAST),
.S_AXIN_ABORT(CRC_ABORT),
//
.M_AXIN_VALID(RXWD_VALID),
.M_AXIN_READY(RXWD_READY),
.M_AXIN_DATA( RXWD_DATA),
.M_AXIN_BYTES({ ign_rx_high, RXWD_BYTES }),
.M_AXIN_LAST( RXWD_LAST),
.M_AXIN_ABORT(RXWD_ABORT)
// }}}
);
axincdc #(
.DW(128),
.LGFIFO(5) // Sweet spot for Xilinx distributed RAM
) u_rxcdc (
// {{{
.S_CLK(i_rx_clk), .S_ARESETN(rx_reset_n),
//
.S_VALID(RXWD_VALID),
.S_READY(RXWD_READY),
.S_DATA( RXWD_DATA),
.S_BYTES(RXWD_BYTES),
.S_LAST( RXWD_LAST),
.S_ABORT(RXWD_ABORT),
//
.M_CLK(i_sys_clk), .M_ARESETN(i_reset_n),
//
.M_VALID(M_VALID),
.M_READY(M_READY),
.M_DATA( M_DATA),
.M_BYTES(M_BYTES),
.M_LAST( M_LAST),
.M_ABORT(M_ABORT)
// }}}
);
always @(posedge i_rx_clk or negedge rx_reset_n)
if (!rx_reset_n)
rx_activity <= 0;
else if (M_VALID && M_LAST && !M_ABORT)
rx_activity <= -1;
else if (rx_activity != 0)
rx_activity <= rx_activity - 1;
// }}}
////////////////////////////////////////////////////////////////////////
//
// Outgoing S_* -> packet processing
// {{{
axincdc #(
.DW(128),
.LGFIFO(5) // Sweet spot for Xilinx distributed RAM
) u_txcdc (
// {{{
.S_CLK(i_sys_clk), .S_ARESETN(i_reset_n),
//
.S_VALID(S_VALID),
.S_READY(S_READY),
.S_DATA( S_DATA),
.S_BYTES(S_BYTES),
.S_LAST( S_LAST),
.S_ABORT(S_ABORT),
//
.M_CLK(i_tx_clk), .M_ARESETN(tx_reset_n),
//
.M_VALID(TXCK_VALID),
.M_READY(TXCK_READY),
.M_DATA( TXCK_DATA),
.M_BYTES(TXCK_BYTES),
.M_LAST( TXCK_LAST),
.M_ABORT(TXCK_ABORT)
// }}}
);
axinwidth #(
.IW(128), .OW(64)
) u_txwidth (
// {{{
.ACLK(i_sys_clk), .ARESETN(tx_reset_n),
//
.S_AXIN_VALID(TXCK_VALID),
.S_AXIN_READY(TXCK_READY),
.S_AXIN_DATA( TXCK_DATA),
.S_AXIN_BYTES({ (TXCK_BYTES==0), TXCK_BYTES }),
.S_AXIN_LAST( TXCK_LAST),
.S_AXIN_ABORT(TXCK_ABORT),
//
.M_AXIN_VALID(TXWD_VALID),
.M_AXIN_READY(TXWD_READY),
.M_AXIN_DATA( TXWD_DATA),
.M_AXIN_BYTES({ ign_tx_high, TXWD_BYTES }),
.M_AXIN_LAST( TXWD_LAST),
.M_AXIN_ABORT(TXWD_ABORT)
// }}}
);
pktgate #(
.DW(64), .LGFLEN(LGPKTGATE)
) u_pktgate (
// {{{
.S_AXI_ACLK(i_tx_clk), .S_AXI_ARESETN(tx_reset_n),
//
.S_AXIN_VALID(TXWD_VALID),
.S_AXIN_READY(TXWD_READY),
.S_AXIN_DATA( TXWD_DATA),
.S_AXIN_BYTES(TXWD_BYTES),
.S_AXIN_LAST( TXWD_LAST),
.S_AXIN_ABORT(TXWD_ABORT),
//
.M_AXIN_VALID(FULL_VALID),
.M_AXIN_READY(FULL_READY),
.M_AXIN_DATA( FULL_DATA),
.M_AXIN_BYTES(FULL_BYTES),
.M_AXIN_LAST( FULL_LAST),
.M_AXIN_ABORT(FULL_ABORT)
// }}}
);
pkt2p64b
u_pkt2p64b (
// {{{
.TX_CLK(i_tx_clk), .S_ARESETN(tx_reset_n),
//
.i_remote_fault(remote_fault),
.i_local_fault(local_fault || rx_reset_n),
//
.S_VALID(FULL_VALID),
.S_READY(FULL_READY),
.S_DATA( FULL_DATA),
.S_BYTES(FULL_BYTES),
.S_LAST( FULL_LAST),
.S_ABORT(FULL_ABORT),
//
.TXREADY(tx_ready),
.TXDATA(tx_data)
// }}}
);
p64bscrambler #(
.OPT_RX(1'b0)
) u_scrambler (
// {{{
.i_clk(i_tx_clk), .i_reset_n(tx_reset_n),
//
.i_valid(1'b1),
.o_ready(tx_ready),
.i_data( tx_data),
//
.o_valid(ign_tx_raw_valid),
.i_ready(i_raw_ready),
.o_data( o_raw_data)
// }}}
);
assign tx_link_up = tx_reset_n;
always @(posedge i_tx_clk or negedge tx_reset_n)
if (!tx_reset_n)
tx_activity <= 0;
else if (S_VALID)
tx_activity <= -1;
else if (tx_activity != 0)
tx_activity <= tx_activity - 1;
// }}}
assign o_link_up = rx_link_up && tx_link_up;
assign o_activity = rx_activity[ACTMSB] || tx_activity[ACTMSB];
// Keep Verilator happy
// {{{
// Verilator lint_on UNUSED
// Verilator coverage_off
wire unused;
assign unused = &{ 1'b0, ign_high, ign_rx_raw_ready,
ign_tx_raw_valid, ign_tx_high, ign_rx_high };
// Verilator coverage_on
// Verialtor lint_off UNUSED
// }}}
endmodule