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