1///////////////////////////////////////////////////////////////////// 2// 3// Copyright 2013-2020 Ettus Research, A National Instruments Company 4// 5// SPDX-License-Identifier: LGPL-3.0-or-later 6// 7// Module: xge_mac_wrapper 8// Description: 9// Wrap XGE MAC + optional wishbone interface 10// 11// *) Signals are crossed between the MAC's own 156.25MHz clock domain and the 12// main FPGA clock domain. 13// *) 6 byte Padding is added at RX, including metadata so that IP headers become aligned. 14// *) 6 Byte padding is stripped at TX, so that Eth header data starts immediately. 15// *) TX & RX can buffer at least an MTU sized packet 16// *) On TX, to not start an Ethernet Tx until a complete packet is present in the 17// last Tx FIFO so that the MAC doesn't underrun. 18// 19// Parameters: 20// - PORTNUM: Refers to which ethernet port is being built. Added to padding but not used 21// - WISHBONE: If set use wishbone implementation 22// - ADD_PREAMBLE: If set add/remove 6 byte padding used in old ethernet_interface 23// - CROSS_TO_SYSCLK: If set cross AXI streams to the sys_clk domain. 24// - CUT_THROUGH: If > 0, how many words to wait before starting to transmit. 25///////////////////////////////////////////////////////////////////// 26 27module xge_mac_wrapper #( 28 parameter PORTNUM = 8'd0, 29 parameter WISHBONE = 1, 30 parameter ADD_PREAMBLE = 1, 31 parameter CROSS_TO_SYSCLK = 1, 32 parameter CUT_THROUGH = 0 33)( 34 // XGMII 35 input xgmii_clk, 36 output [63:0] xgmii_txd, 37 output [7:0] xgmii_txc, 38 input [63:0] xgmii_rxd, 39 input [7:0] xgmii_rxc, 40 // Client FIFO Interfaces 41 input sys_clk, 42 input sys_rst, // From sys_clk domain. 43 output [63:0] rx_tdata, 44 output [3:0] rx_tuser, 45 output rx_tlast, 46 output rx_tvalid, 47 input rx_tready, 48 input [63:0] tx_tdata, 49 input [3:0] tx_tuser, // Bit[3] (error) is ignored for now. 50 input tx_tlast, 51 input tx_tvalid, 52 output tx_tready, 53 // Control and Status 54 input phy_ready, 55 input ctrl_tx_enable, 56 output status_crc_error, 57 output status_fragment_error, 58 output status_txdfifo_ovflow, 59 output status_txdfifo_udflow, 60 output status_rxdfifo_ovflow, 61 output status_rxdfifo_udflow, 62 output status_pause_frame_rx, 63 output status_local_fault, 64 output status_remote_fault, 65 // MDIO 66 output mdc, 67 output mdio_in, 68 input mdio_out, 69 // Wishbone interface 70 input [7:0] wb_adr_i, // To wishbone_if0 of wishbone_if.v 71 input wb_clk_i, // To sync_clk_wb0 of sync_clk_wb.v, ... 72 input wb_cyc_i, // To wishbone_if0 of wishbone_if.v 73 input [31:0] wb_dat_i, // To wishbone_if0 of wishbone_if.v 74 input wb_rst_i, // To sync_clk_wb0 of sync_clk_wb.v, ... 75 input wb_stb_i, // To wishbone_if0 of wishbone_if.v 76 input wb_we_i, // To wishbone_if0 of wishbone_if.v 77 output wb_ack_o, // From wishbone_if0 of wishbone_if.v 78 output [31:0] wb_dat_o, // From wishbone_if0 of wishbone_if.v 79 output wb_int_o // From wishbone_if0 of wishbone_if.v 80); 81 82 // 83 // Generate 156MHz synchronized sys_rst locally 84 // 85 86 wire xgmii_reset, ctrl_tx_enable_xclk; 87 wire phy_ready_xgmiiclk, sys_rst_xgmiiclk; 88 89 synchronizer #( 90 .INITIAL_VAL(1'b0), .STAGES(3) 91 ) phy_ready_sync_i ( 92 .clk(xgmii_clk), .rst(1'b0 /* no reset */), .in(phy_ready), .out(phy_ready_xgmiiclk) 93 ); 94 95 if (CROSS_TO_SYSCLK) begin : reset_cross 96 synchronizer #( 97 .INITIAL_VAL(1'b1), .STAGES(3) 98 ) sys_rst_sync_i ( 99 .clk(xgmii_clk), .rst(1'b0 /* no reset */), .in(sys_rst), .out(sys_rst_xgmiiclk) 100 ); 101 assign xgmii_reset = !phy_ready_xgmiiclk || sys_rst_xgmiiclk; 102 end else begin : reset_no_cross 103 assign xgmii_reset = !phy_ready_xgmiiclk; 104 end 105 synchronizer #( 106 .INITIAL_VAL(1'b1), .STAGES(3) 107 ) tx_enabled_sync_i ( 108 .clk(xgmii_clk), .rst(1'b0 /* no reset */), .in(ctrl_tx_enable), .out(ctrl_tx_enable_xclk) 109 ); 110 111 // 112 // 10G MAC 113 // 114 wire [63:0] eth_rx_data; 115 wire eth_rx_avail; 116 wire eth_rx_eof; 117 wire eth_rx_err; 118 wire [2:0] eth_rx_occ; 119 wire eth_rx_sof; 120 wire eth_rx_valid; 121 wire eth_rx_ren; 122 123 wire eth_tx_full; 124 wire [63:0] eth_tx_data; 125 wire eth_tx_eof; 126 wire [2:0] eth_tx_occ; 127 wire eth_tx_sof; 128 wire eth_tx_valid; 129 130 generate if (WISHBONE == 1) begin : wishbone_mac 131 xge_mac_wb xge_mac_wb ( 132 // Clocks and Resets 133 .clk_156m25 (xgmii_clk), 134 .clk_xgmii_rx (xgmii_clk), 135 .clk_xgmii_tx (xgmii_clk), 136 .reset_156m25_n (~xgmii_reset), 137 .reset_xgmii_rx_n (~xgmii_reset), 138 .reset_xgmii_tx_n (~xgmii_reset), 139 // XGMII 140 .xgmii_txc (xgmii_txc[7:0]), 141 .xgmii_txd (xgmii_txd[63:0]), 142 .xgmii_rxc (xgmii_rxc[7:0]), 143 .xgmii_rxd (xgmii_rxd[63:0]), 144 // MDIO 145 .mdc (mdc), 146 .mdio_out (mdio_in),// Switch sense of in and out here for master and slave. 147 .mdio_tri (mdio_tri), 148 .xge_gpo (), 149 .mdio_in (mdio_out), // Switch sense of in and out here for master and slave. 150 .xge_gpi (/*{2'b00,align_status,mgt_tx_ready,sync_status[3:0]}*/0), 151 // Packet interface 152 .pkt_rx_avail (eth_rx_avail), 153 .pkt_rx_data (eth_rx_data), 154 .pkt_rx_eop (eth_rx_eof), 155 .pkt_rx_err (eth_rx_err), 156 .pkt_rx_mod (eth_rx_occ), 157 .pkt_rx_sop (eth_rx_sof), 158 .pkt_rx_val (eth_rx_valid), 159 .pkt_tx_full (eth_tx_full), 160 // Inputs 161 .pkt_rx_ren (eth_rx_ren), 162 .pkt_tx_data (eth_tx_data), 163 .pkt_tx_eop (eth_tx_eof), 164 .pkt_tx_mod (eth_tx_occ), 165 .pkt_tx_sop (eth_tx_sof), 166 .pkt_tx_val (eth_tx_valid), 167 .wb_ack_o (wb_ack_o), 168 .wb_dat_o (wb_dat_o), 169 .wb_adr_i (wb_adr_i[7:0]), 170 .wb_clk_i (wb_clk_i), 171 .wb_cyc_i (wb_cyc_i), 172 .wb_dat_i (wb_dat_i), 173 .wb_rst_i (wb_rst_i), 174 .wb_stb_i (wb_stb_i), 175 .wb_we_i (wb_we_i), 176 .wb_int_o (xge_int) 177 ); 178 179 assign status_crc_error = 1'b0; 180 assign status_fragment_error = 1'b0; 181 assign status_txdfifo_ovflow = 1'b0; 182 assign status_txdfifo_udflow = 1'b0; 183 assign status_rxdfifo_ovflow = 1'b0; 184 assign status_rxdfifo_udflow = 1'b0; 185 assign status_pause_frame_rx = 1'b0; 186 assign status_local_fault = 1'b0; 187 assign status_remote_fault = 1'b0; 188 189 end else begin : xge_mac 190 xge_mac xge_mac ( 191 // Clocks and Resets 192 .clk_156m25 (xgmii_clk), 193 .clk_xgmii_rx (xgmii_clk), 194 .clk_xgmii_tx (xgmii_clk), 195 .reset_156m25_n (~xgmii_reset), 196 .reset_xgmii_rx_n (~xgmii_reset), 197 .reset_xgmii_tx_n (~xgmii_reset), 198 // XGMII 199 .xgmii_txc (xgmii_txc[7:0]), 200 .xgmii_txd (xgmii_txd[63:0]), 201 .xgmii_rxc (xgmii_rxc[7:0]), 202 .xgmii_rxd (xgmii_rxd[63:0]), 203 // Packet interface 204 .pkt_rx_avail (eth_rx_avail), 205 .pkt_rx_data (eth_rx_data), 206 .pkt_rx_eop (eth_rx_eof), 207 .pkt_rx_err (eth_rx_err), 208 .pkt_rx_mod (eth_rx_occ), 209 .pkt_rx_sop (eth_rx_sof), 210 .pkt_rx_val (eth_rx_valid), 211 .pkt_tx_full (eth_tx_full), 212 // Inputs 213 .pkt_rx_ren (eth_rx_ren), 214 .pkt_tx_data (eth_tx_data), 215 .pkt_tx_eop (eth_tx_eof), 216 .pkt_tx_mod (eth_tx_occ), 217 .pkt_tx_sop (eth_tx_sof), 218 .pkt_tx_val (eth_tx_valid), 219 // Control and Status 220 .ctrl_tx_enable (ctrl_tx_enable_xclk), 221 .status_crc_error (status_crc_error), 222 .status_fragment_error (status_fragment_error), 223 .status_txdfifo_ovflow (status_txdfifo_ovflow), 224 .status_txdfifo_udflow (status_txdfifo_udflow), 225 .status_rxdfifo_ovflow (status_rxdfifo_ovflow), 226 .status_rxdfifo_udflow (status_rxdfifo_udflow), 227 .status_pause_frame_rx (status_pause_frame_rx), 228 .status_local_fault (status_local_fault), 229 .status_remote_fault (status_remote_fault) 230 ); 231 232 assign wb_ack_o = 1'b0; 233 assign wb_dat_o = 1'b0; 234 assign wb_int_o = 1'b0; 235 assign mdio_in = 1'b0; 236 assign mdc = 1'b0; 237 end 238 endgenerate 239 240 /////////////////////////////////////////////////////////////////////////////////////// 241 // RX FIFO Chain 242 /////////////////////////////////////////////////////////////////////////////////////// 243 wire [63:0] rx_tdata_int; 244 wire [3:0] rx_tuser_int; 245 wire rx_tlast_int; 246 wire rx_tvalid_int; 247 wire rx_tready_int; 248 249 // 250 // Logic to drive pkt_rx_ren on XGE MAC 251 // 252 xge_handshake xge_handshake ( 253 .clk(xgmii_clk), 254 .reset(xgmii_reset), 255 .pkt_rx_ren(eth_rx_ren), 256 .pkt_rx_avail(eth_rx_avail), 257 .pkt_rx_eop(eth_rx_eof) 258 ); 259 260 261 if (ADD_PREAMBLE) begin : rx_preamble 262 // 263 // Add pad of 6 empty bytes before MAC addresses of new Rxed packet so that IP 264 // headers are aligned. Also put metadata in first octet of pad that shows 265 // ingress port. 266 // 267 xge64_to_axi64 #( 268 .LABEL(PORTNUM) 269 ) xge64_to_axi64 ( 270 .clk(xgmii_clk), 271 .reset(xgmii_reset), 272 .clear(1'b0), 273 .datain(eth_rx_data), 274 .occ(eth_rx_occ), 275 .sof(eth_rx_sof), 276 .eof(eth_rx_eof), 277 .err(eth_rx_err), 278 .valid(eth_rx_valid), 279 .axis_tdata(rx_tdata_int), 280 .axis_tuser(rx_tuser_int), 281 .axis_tlast(rx_tlast_int), 282 .axis_tvalid(rx_tvalid_int), 283 .axis_tready(rx_tready_int) 284 ); 285 end else begin : rx_no_preamble 286 assign rx_tdata_int = eth_rx_data; 287 assign rx_tuser_int[3] = eth_rx_err; 288 assign rx_tuser_int[2:0] = eth_rx_occ; 289 assign rx_tlast_int = eth_rx_eof; 290 assign rx_tvalid_int = eth_rx_valid; 291 // there is no holdoff so ignore rx_tready_int 292 end 293 294 295 if (CROSS_TO_SYSCLK) begin : rx_cross 296 // 297 // Large FIFO must be able to run input side at 64b@156MHz to sustain 10Gb Rx. 298 // 299 axi64_4k_2clk_fifo rxfifo_2clk ( 300 .s_aresetn(~xgmii_reset), 301 .s_aclk(xgmii_clk), 302 .s_axis_tvalid(rx_tvalid_int), 303 .s_axis_tready(rx_tready_int), 304 .s_axis_tdata(rx_tdata_int), 305 .s_axis_tlast(rx_tlast_int), 306 .s_axis_tuser(rx_tuser_int), 307 .axis_wr_data_count(), 308 309 .m_aclk(sys_clk), 310 .m_axis_tvalid(rx_tvalid), 311 .m_axis_tready(rx_tready), 312 .m_axis_tdata(rx_tdata), 313 .m_axis_tlast(rx_tlast), 314 .m_axis_tuser(rx_tuser), 315 .axis_rd_data_count() 316 ); 317 end else begin : rx_no_cross 318 assign rx_tdata = rx_tdata_int; 319 assign rx_tuser = rx_tuser_int; 320 assign rx_tlast = rx_tlast_int; 321 assign rx_tvalid = rx_tvalid_int; 322 assign rx_tready_int = rx_tready; 323 end 324 325 /////////////////////////////////////////////////////////////////////////////////////// 326 // TX FIFO Chain 327 /////////////////////////////////////////////////////////////////////////////////////// 328 329 wire [63:0] tx_tdata_int; 330 wire [3:0] tx_tuser_int; 331 wire tx_tlast_int; 332 wire tx_tvalid_int; 333 wire tx_tready_int; 334 335 wire [63:0] tx_tdata_int2; 336 wire [3:0] tx_tuser_int2; 337 wire tx_tlast_int2; 338 wire tx_tvalid_int2; 339 wire tx_tready_int2; 340 341 wire tx_tvalid_int3; 342 wire tx_tready_int3; 343 wire tx_sof_int3; 344 wire enable_tx; 345 346 if (CROSS_TO_SYSCLK) begin : tx_cross 347 axi64_4k_2clk_fifo txfifo_2clk_1x ( 348 .s_aresetn(~xgmii_reset), 349 .s_aclk(sys_clk), 350 .s_axis_tvalid(tx_tvalid), 351 .s_axis_tready(tx_tready), 352 .s_axis_tdata(tx_tdata), 353 .s_axis_tlast(tx_tlast), 354 .s_axis_tuser(tx_tuser), 355 .axis_wr_data_count(), 356 357 .m_aclk(xgmii_clk), 358 .m_axis_tvalid(tx_tvalid_int), 359 .m_axis_tready(tx_tready_int), 360 .m_axis_tdata(tx_tdata_int), 361 .m_axis_tlast(tx_tlast_int), 362 .m_axis_tuser(tx_tuser_int), 363 .axis_rd_data_count() 364 ); 365 end else begin : tx_no_cross 366 assign tx_tdata_int = tx_tdata; 367 assign tx_tuser_int = tx_tuser; 368 assign tx_tlast_int = tx_tlast; 369 assign tx_tvalid_int = tx_tvalid; 370 assign tx_tready = tx_tready_int; 371 end 372 373 374 if (ADD_PREAMBLE) begin : tx_preamble 375 // 376 // Strip the 6 octet ethernet padding we used internally. 377 // Put SOF into bit[3] of tuser. 378 // 379 axi64_to_xge64 axi64_to_xge64 ( 380 .clk(xgmii_clk), 381 .reset(xgmii_reset), 382 .clear(1'b0), 383 .s_axis_tdata(tx_tdata_int), 384 .s_axis_tuser(tx_tuser_int), 385 .s_axis_tlast(tx_tlast_int), 386 .s_axis_tvalid(tx_tvalid_int), 387 .s_axis_tready(tx_tready_int), 388 .m_axis_tdata(tx_tdata_int2), 389 .m_axis_tuser(tx_tuser_int2), 390 .m_axis_tlast(tx_tlast_int2), 391 .m_axis_tvalid(tx_tvalid_int2), 392 .m_axis_tready(tx_tready_int2) 393 ); 394 end else begin : tx_no_preamble 395 reg sof = 1'b1; 396 397 // Add SOF 398 always @(posedge xgmii_clk) begin : add_sof 399 if (xgmii_reset) begin 400 sof <= 1'b1; 401 end else if (tx_tvalid_int && tx_tready_int) begin 402 sof <= tx_tlast_int; 403 end 404 end 405 406 assign tx_tdata_int2 = tx_tdata_int; 407 assign tx_tuser_int2[3] = sof && tx_tvalid_int; 408 assign tx_tuser_int2[2:0] = tx_tuser_int[2:0]; 409 assign tx_tlast_int2 = tx_tlast_int; 410 assign tx_tvalid_int2 = tx_tvalid_int; 411 assign tx_tready_int = tx_tready_int2; 412 end 413 414 // 415 // Large FIFO can hold a max sized ethernet packet. 416 // 417 wire [15:0] tx_occupied; 418 419 localparam TX_FIFO_SIZE = CUT_THROUGH > 0 ? $clog2(CUT_THROUGH)+1 : 10; 420 421 axi_fifo #(.WIDTH(64+4+1), .SIZE(TX_FIFO_SIZE)) txfifo ( 422 .clk(xgmii_clk), .reset(xgmii_reset), .clear(1'b0), 423 .i_tdata({tx_tlast_int2, tx_tuser_int2, tx_tdata_int2}), 424 .i_tvalid(tx_tvalid_int2), 425 .i_tready(tx_tready_int2), 426 .o_tvalid(tx_tvalid_int3), 427 .o_tready(tx_tready_int3), 428 .o_tdata({eth_tx_eof,tx_sof_int3,eth_tx_occ,eth_tx_data}), 429 .space(), .occupied(tx_occupied) 430 ); 431 432 // 433 // add cut through if we have "enough" data buffered up 434 // 435 reg cut_through; 436 437 if (CUT_THROUGH > 0) begin : yes_cut_through 438 439 wire cut_start; 440 wire cut_end; 441 442 assign cut_start = tx_occupied > CUT_THROUGH; 443 assign cut_end = eth_tx_eof && eth_tx_valid; 444 445 // Add SOF 446 always @(posedge xgmii_clk) begin : cut_through_dff 447 if (xgmii_reset) begin 448 cut_through <= 1'b0; 449 end else begin 450 if (cut_start) begin 451 cut_through <= 1'b1; 452 end else if (cut_end) begin 453 cut_through <= 1'b0; 454 end 455 end 456 end 457 end else begin : no_cut_through 458 always @(*) cut_through <= 0; 459 end 460 461 // 462 // Monitor number of Ethernet packets in tx_fifo2 463 // 464 axi_count_packets_in_fifo axi_count_packets_in_fifo ( 465 .clk(xgmii_clk), 466 .reset(xgmii_reset), 467 .in_axis_tvalid(tx_tvalid_int2), 468 .in_axis_tready(tx_tready_int2), 469 .in_axis_tlast(tx_tlast_int2), 470 .out_axis_tvalid(tx_tvalid_int3), 471 .out_axis_tready(tx_tready_int3), 472 .out_axis_tlast(eth_tx_eof), 473 .pkt_tx_full(eth_tx_full), 474 .enable_tx(enable_tx) 475 ); 476 477 // 478 // 479 // Suppress FIFO flags to stop overflow of MAC in Tx direction 480 // 481 assign tx_tready_int3 = (enable_tx || cut_through); 482 assign eth_tx_valid = (enable_tx || cut_through) & tx_tvalid_int3; 483 assign eth_tx_sof = (enable_tx || cut_through) & tx_sof_int3; 484 485endmodule 486