diff options
Diffstat (limited to 'fpga/usrp3/lib/simple_gemac')
24 files changed, 3146 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/simple_gemac/.gitignore b/fpga/usrp3/lib/simple_gemac/.gitignore new file mode 100644 index 000000000..17f35e962 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/.gitignore @@ -0,0 +1,4 @@ +/a.out +/*.vcd +simple_gemac_wrapper_tb + diff --git a/fpga/usrp3/lib/simple_gemac/Makefile.srcs b/fpga/usrp3/lib/simple_gemac/Makefile.srcs new file mode 100644 index 000000000..c94a8b08c --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/Makefile.srcs @@ -0,0 +1,25 @@ +# +# Copyright 2010 Ettus Research LLC +# + +################################################## +# Simple GEMAC Sources +################################################## +SIMPLE_GEMAC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/simple_gemac/, \ +simple_gemac_wrapper.v \ +simple_gemac.v \ +simple_gemac_tx.v \ +simple_gemac_rx.v \ +crc.v \ +delay_line.v \ +flow_ctrl_tx.v \ +flow_ctrl_rx.v \ +address_filter.v \ +address_filter_promisc.v \ +ll8_to_txmac.v \ +rxmac_to_ll8.v \ +ll8_to_axi64.v \ +axi64_to_ll8.v \ +gmii_to_axis.v \ +mdio.v \ +)) diff --git a/fpga/usrp3/lib/simple_gemac/address_filter.v b/fpga/usrp3/lib/simple_gemac/address_filter.v new file mode 100644 index 000000000..a31f35e17 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/address_filter.v @@ -0,0 +1,43 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + + +module address_filter + (input clk, + input reset, + input go, + input [7:0] data, + input [47:0] address, + output match, + output done); + + reg [2:0] af_state; + + always @(posedge clk) + if(reset) + af_state <= 0; + else + if(go) + af_state <= (data == address[47:40]) ? 1 : 7; + else + case(af_state) + 1 : af_state <= (data == address[39:32]) ? 2 : 7; + 2 : af_state <= (data == address[31:24]) ? 3 : 7; + 3 : af_state <= (data == address[23:16]) ? 4 : 7; + 4 : af_state <= (data == address[15:8]) ? 5 : 7; + 5 : af_state <= (data == address[7:0]) ? 6 : 7; + 6, 7 : af_state <= 0; + endcase // case (af_state) + + assign match = (af_state==6); + assign done = (af_state==6)|(af_state==7); + +endmodule // address_filter + + diff --git a/fpga/usrp3/lib/simple_gemac/address_filter_promisc.v b/fpga/usrp3/lib/simple_gemac/address_filter_promisc.v new file mode 100644 index 000000000..cf992f867 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/address_filter_promisc.v @@ -0,0 +1,40 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + + +module address_filter_promisc + (input clk, + input reset, + input go, + input [7:0] data, + output match, + output done); + + reg [2:0] af_state; + + always @(posedge clk) + if(reset) + af_state <= 0; + else + if(go) + af_state <= 1;//(data[0] == 1'b0) ? 1 : 7; + else + case(af_state) + 1 : af_state <= 2; + 2 : af_state <= 3; + 3 : af_state <= 4; + 4 : af_state <= 5; + 5 : af_state <= 6; + 6, 7 : af_state <= 0; + endcase // case (af_state) + + assign match = (af_state==6); + assign done = (af_state==6)|(af_state==7); + +endmodule // address_filter_promisc diff --git a/fpga/usrp3/lib/simple_gemac/axi64_to_ll8.v b/fpga/usrp3/lib/simple_gemac/axi64_to_ll8.v new file mode 100644 index 000000000..de0d6d8cc --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/axi64_to_ll8.v @@ -0,0 +1,61 @@ +// +// Copyright 2014 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + +module axi64_to_ll8 + #(parameter START_BYTE=6) + (input clk, input reset, input clear, + input [63:0] axi64_tdata, input axi64_tlast, input [3:0] axi64_tuser, input axi64_tvalid, output axi64_tready, + output [7:0] ll_data, output ll_eof, output ll_src_rdy, input ll_dst_rdy); + + reg [7:0] data_int; + wire eof_int, valid_int, ready_int; + + reg [2:0] state = START_BYTE; + reg eof, done; + reg [3:0] occ; + + wire [63:0] axi64_tdata_gated; + wire axi64_tlast_gated; + wire [3:0] axi64_tuser_gated; + wire axi64_tvalid_gated; + wire axi64_tready_gated; + + axi_packet_gate #( + .WIDTH(68), + .SIZE(10) + ) axi64_packet_gater ( + .clk(clk), .reset(reset), .clear(clear), + .i_tdata({axi64_tuser, axi64_tdata}), .i_tlast(axi64_tlast), + .i_tvalid(axi64_tvalid), .i_tready(axi64_tready), + .o_tdata({axi64_tuser_gated, axi64_tdata_gated}), .o_tlast(axi64_tlast_gated), + .o_tvalid(axi64_tvalid_gated), .o_tready(axi64_tready_gated) + ); + + always @(posedge clk) + if(reset | clear) + state <= START_BYTE; + else + if(valid_int & ready_int) + if(eof_int) + state <= START_BYTE; + else + state <= state + 3'd1; + + assign valid_int = axi64_tvalid_gated; + assign axi64_tready_gated = ready_int & (eof_int | state == 7); + assign eof_int = axi64_tlast_gated & (axi64_tuser_gated[2:0] == (state + 3'd1)); + + always @* + data_int <= axi64_tdata_gated[state*8 +: 8]; + + axi_fifo_short #(.WIDTH(9)) ll8_fifo + (.clk(clk), .reset(reset), .clear(0), + .i_tdata({eof_int, data_int}), .i_tvalid(valid_int), .i_tready(ready_int), + .o_tdata({ll_eof, ll_data}), .o_tvalid(ll_src_rdy), .o_tready(ll_dst_rdy), + .space(), .occupied()); + +endmodule // axi64_to_ll8 diff --git a/fpga/usrp3/lib/simple_gemac/crc.v b/fpga/usrp3/lib/simple_gemac/crc.v new file mode 100644 index 000000000..6e1250c55 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/crc.v @@ -0,0 +1,74 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +module crc + (input clk, + input reset, + input clear, + input [7:0] data, + input calc, + output [31:0] crc_out, + output match); + + function[31:0] NextCRC; + input[7:0] D; + input[31:0] C; + reg[31:0] NewCRC; + begin + NewCRC[0] = C[24]^C[30]^D[1]^D[7]; + NewCRC[1] = C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[2] = C[26]^D[5]^C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[3] = C[27]^D[4]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[4] = C[28]^D[3]^C[27]^D[4]^C[26]^D[5]^C[24]^C[30]^D[1]^D[7]; + NewCRC[5] = C[29]^D[2]^C[28]^D[3]^C[27]^D[4]^C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[6] = C[30]^D[1]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[7] = C[31]^D[0]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7]; + NewCRC[8] = C[0]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7]; + NewCRC[9] = C[1]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^D[6]; + NewCRC[10] = C[2]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7]; + NewCRC[11] = C[3]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7]; + NewCRC[12] = C[4]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[13] = C[5]^C[30]^D[1]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[14] = C[6]^C[31]^D[0]^C[30]^D[1]^C[28]^D[3]^C[27]^D[4]^C[26]^D[5]; + NewCRC[15] = C[7]^C[31]^D[0]^C[29]^D[2]^C[28]^D[3]^C[27]^D[4]; + NewCRC[16] = C[8]^C[29]^D[2]^C[28]^D[3]^C[24]^D[7]; + NewCRC[17] = C[9]^C[30]^D[1]^C[29]^D[2]^C[25]^D[6]; + NewCRC[18] = C[10]^C[31]^D[0]^C[30]^D[1]^C[26]^D[5]; + NewCRC[19] = C[11]^C[31]^D[0]^C[27]^D[4]; + NewCRC[20] = C[12]^C[28]^D[3]; + NewCRC[21] = C[13]^C[29]^D[2]; + NewCRC[22] = C[14]^C[24]^D[7]; + NewCRC[23] = C[15]^C[25]^D[6]^C[24]^C[30]^D[1]^D[7]; + NewCRC[24] = C[16]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6]; + NewCRC[25] = C[17]^C[27]^D[4]^C[26]^D[5]; + NewCRC[26] = C[18]^C[28]^D[3]^C[27]^D[4]^C[24]^C[30]^D[1]^D[7]; + NewCRC[27] = C[19]^C[29]^D[2]^C[28]^D[3]^C[25]^C[31]^D[0]^D[6]; + NewCRC[28] = C[20]^C[30]^D[1]^C[29]^D[2]^C[26]^D[5]; + NewCRC[29] = C[21]^C[31]^D[0]^C[30]^D[1]^C[27]^D[4]; + NewCRC[30] = C[22]^C[31]^D[0]^C[28]^D[3]; + NewCRC[31] = C[23]^C[29]^D[2]; + NextCRC = NewCRC; + end + endfunction + + reg [31:0] crc_reg; + always @ (posedge clk) + if (reset | clear) + crc_reg <= 32'hffffffff; + else if (calc) + crc_reg <= NextCRC(data,crc_reg); + + assign crc_out = ~{crc_reg[24],crc_reg[25],crc_reg[26],crc_reg[27],crc_reg[28],crc_reg[29],crc_reg[30],crc_reg[31], + crc_reg[16],crc_reg[17],crc_reg[18],crc_reg[19],crc_reg[20],crc_reg[21],crc_reg[22],crc_reg[23], + crc_reg[8],crc_reg[9],crc_reg[10],crc_reg[11],crc_reg[12],crc_reg[13],crc_reg[14],crc_reg[15], + crc_reg[0],crc_reg[1],crc_reg[2],crc_reg[3],crc_reg[4],crc_reg[5],crc_reg[6],crc_reg[7] }; + + assign match = (crc_reg == 32'hc704_dd7b); + +endmodule // crc diff --git a/fpga/usrp3/lib/simple_gemac/delay_line.v b/fpga/usrp3/lib/simple_gemac/delay_line.v new file mode 100644 index 000000000..9e081b484 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/delay_line.v @@ -0,0 +1,29 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + + +module delay_line + #(parameter WIDTH=32) + (input clk, + input [3:0] delay, + input [WIDTH-1:0] din, + output [WIDTH-1:0] dout); + + genvar i; + generate + for (i=0;i<WIDTH;i=i+1) + begin : gen_delay + SRL16E + srl16e(.Q(dout[i]), + .A0(delay[0]),.A1(delay[1]),.A2(delay[2]),.A3(delay[3]), + .CE(1),.CLK(clk),.D(din[i])); + end + endgenerate + +endmodule // delay_line diff --git a/fpga/usrp3/lib/simple_gemac/eth_tasks.v b/fpga/usrp3/lib/simple_gemac/eth_tasks.v new file mode 100644 index 000000000..096ff3e34 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/eth_tasks.v @@ -0,0 +1,164 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + + +task SendFlowCtrl; + input [15:0] fc_len; + begin + $display("Sending Flow Control, quanta = %d, time = %d", fc_len,$time); + pause_time <= fc_len; + @(posedge clk); + pause_req <= 1; + @(posedge clk); + pause_req <= 0; + $display("Sent Flow Control"); + end +endtask // SendFlowCtrl + +task SendPacket2MAC; + input tx_clk; + input [7:0] data_start; + input [31:0] data_len; + output [7:0] tx_data; + output tx_valid; + output tx_error; + input tx_ack; + reg [15:0] count; + begin + $display("Sending Packet Len=%d, %d", data_len, $time); + count <= 1; + tx_data <= data_start; + tx_error <= 0; + tx_valid <= 1; + while(~tx_ack) + @(posedge tx_clk); + $display("Packet Accepted, %d", $time); + while(count < data_len) + begin + tx_data <= tx_data + 1; + count <= count + 1; + @(posedge clk); + end + tx_valid <= 0; + @(posedge tx_clk); + end +endtask // SendPacket2MAC + +task SendPacket_to_ll8; + input [7:0] data_start; + input [15:0] data_len; +// output [7:0] tx_data; +// output tx_sof; +// output tx_eof; +// output tx_src_rdy; +// input tx_dst_rdy; + reg [15:0] count; + begin + $display("Sending Packet Len=%d, %d", data_len, $time); + count <= 2; + tx_ll_data2 <= data_start; + tx_ll_src_rdy2 <= 1; + tx_ll_sof2 <= 1; + tx_ll_eof2 <= 0; + #1; + while(count < data_len) + begin + while(~tx_ll_dst_rdy2) + @(posedge clk); + @(posedge clk); + tx_ll_data2 = tx_ll_data2 + 1; + count = count + 1; + tx_ll_sof2 <= 0; + end + tx_ll_eof2 <= 1; + while(~tx_ll_dst_rdy2) + @(posedge clk); + @(posedge clk); + tx_ll_src_rdy2 <= 0; + end +endtask // SendPacket_to_ll8 + + +task SendPacketFromFile; + input clk; + input [31:0] data_len; + output [7:0] tx_data; + output tx_valid; + output tx_error; + input tx_ack; + reg [15:0] count; + begin + $display("Sending Packet From File Len=%d, %d",data_len,$time); + $readmemh("test_packet.mem",pkt_rom ); + count = 0; + tx_data = pkt_rom[count]; + tx_error = 0; + tx_valid = 1; + while(~tx_ack) + @(posedge clk); + $display("Packet Accepted, %d",$time); + count = 1; + while(count < data_len) + begin + tx_data = pkt_rom[count]; + count = count + 1; + @(posedge clk); + end + tx_valid <= 0; + @(posedge clk); + end +endtask // SendPacketFromFile + +task Waiter; + input [31:0] wait_length; + begin + tx_ll_src_rdy2 <= 0; + repeat(wait_length) + @(posedge clk); + tx_ll_src_rdy2 <= 1; + end +endtask // Waiter + +task SendPacketFromFile_ll8; + input [31:0] data_len; + input [31:0] wait_length; + input [31:0] wait_time; + + integer count; + begin + $display("Sending Packet From File to LL8 Len=%d, %d",data_len,$time); + $readmemh("test_packet.mem",pkt_rom ); + + while(~tx_ll_dst_rdy2) + @(posedge clk); + tx_ll_data2 <= pkt_rom[0]; + tx_ll_src_rdy2 <= 1; + tx_ll_sof2 <= 1; + tx_ll_eof2 <= 0; + @(posedge clk); + + for(i=1;i<data_len-1;i=i+1) + begin + while(~tx_ll_dst_rdy2) + @(posedge clk); + tx_ll_data2 <= pkt_rom[i]; + tx_ll_sof2 <= 0; + @(posedge clk); + if(i==wait_time) + Waiter(wait_length); + end + + while(~tx_ll_dst_rdy2) + @(posedge clk); + tx_ll_eof2 <= 1; + tx_ll_data2 <= pkt_rom[data_len-1]; + @(posedge clk); + tx_ll_src_rdy2 <= 0; + end +endtask // SendPacketFromFile_ll8 diff --git a/fpga/usrp3/lib/simple_gemac/flow_ctrl_rx.v b/fpga/usrp3/lib/simple_gemac/flow_ctrl_rx.v new file mode 100644 index 000000000..8ea44133a --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/flow_ctrl_rx.v @@ -0,0 +1,69 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +// RX side of flow control -- when we are running out of RX space, send a PAUSE + +module flow_ctrl_rx + (input pause_request_en, input [15:0] pause_time, input [15:0] pause_thresh, + input rx_clk, input rx_reset, input [15:0] rx_fifo_space, + input tx_clk, input tx_reset, output reg pause_req, output reg [15:0] pause_time_req + ); + + // ****************************************************************************** + // Force our TX to send a PAUSE frame because our RX is nearly full + // ****************************************************************************** + + // RX Clock Domain + reg xon, xoff; + reg [21:0] countdown; + + wire [15:0] pause_low_thresh = pause_thresh; + wire [15:0] pause_hi_thresh = 16'hFFFF; + wire [21:0] pq_reduced = {pause_time,6'd0} - 1700; + + always @(posedge rx_clk) + if(rx_reset) + xoff <= 0; + else + xoff <= (pause_request_en & (countdown==0) & (rx_fifo_space < pause_low_thresh)); + + always @(posedge rx_clk) + if(rx_reset) + xon <= 0; + else + xon <= ((countdown!=0) & (rx_fifo_space > pause_hi_thresh)); + + always @(posedge rx_clk) + if(rx_reset) + countdown <= 0; + else if(xoff) + countdown <= pq_reduced; + else if(xon) + countdown <= 0; + else if(countdown != 0) + countdown <= countdown - 1; + + // Cross clock domains + wire xon_tx, xoff_tx; + oneshot_2clk send_xon (.clk_in(rx_clk), .in(xon), .clk_out(tx_clk), .out(xon_tx)); + oneshot_2clk send_xoff (.clk_in(rx_clk), .in(xoff), .clk_out(tx_clk), .out(xoff_tx)); + + always @(posedge tx_clk) + if(xoff_tx) + pause_time_req <= pause_time; + else if(xon_tx) + pause_time_req <= 0; + + always @(posedge tx_clk) + if(tx_reset) + pause_req <= 0; + else + pause_req <= xon_tx | xoff_tx; + +endmodule // flow_ctrl_rx diff --git a/fpga/usrp3/lib/simple_gemac/flow_ctrl_tx.v b/fpga/usrp3/lib/simple_gemac/flow_ctrl_tx.v new file mode 100644 index 000000000..34c90dc88 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/flow_ctrl_tx.v @@ -0,0 +1,47 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +// TX side of flow control -- when other side sends PAUSE, we wait + +module flow_ctrl_tx + (input rst, + input tx_clk, + //host processor + input tx_pause_en, + // From MAC_rx_ctrl + input [15:0] pause_quanta, + input pause_quanta_val, + // MAC_tx_ctrl + output pause_apply, + input paused); + + // ****************************************************************************** + // Inhibit our TX from transmitting because they sent us a PAUSE frame + // ****************************************************************************** + + // Pauses are in units of 512 bit times, or 64 bytes/clock cycles, and can be + // as big as 16 bits, so 22 bits are needed for the counter + + reg [15+6:0] pause_quanta_counter; + reg pqval_d1, pqval_d2; + + always @(posedge tx_clk) pqval_d1 <= pause_quanta_val; + always @(posedge tx_clk) pqval_d2 <= pqval_d1; + + always @ (posedge tx_clk or posedge rst) + if (rst) + pause_quanta_counter <= 0; + else if (pqval_d1 & ~pqval_d2) + pause_quanta_counter <= {pause_quanta, 6'b0}; + else if((pause_quanta_counter!=0) & paused) + pause_quanta_counter <= pause_quanta_counter - 1; + + assign pause_apply = tx_pause_en & (pause_quanta_counter != 0); + +endmodule // flow_ctrl diff --git a/fpga/usrp3/lib/simple_gemac/gmii_to_axis.v b/fpga/usrp3/lib/simple_gemac/gmii_to_axis.v new file mode 100644 index 000000000..956f1cbcd --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/gmii_to_axis.v @@ -0,0 +1,239 @@ +// +// Copyright 2017 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +module gmii_to_axis +#( + parameter RX_FLOW_CTRL=0, + parameter PORTNUM=8'd0 +) +( + input clk125, + input reset, + + // GMII + output gmii_gtx_clk, + + output gmii_tx_en, + output gmii_tx_er, + output [7:0] gmii_txd, + + input gmii_rx_clk, + input gmii_rx_dv, + input gmii_rx_er, + input [7:0] gmii_rxd, + + // Client FIFO Interfaces + input sys_clk, + output [63:0] rx_tdata, + output [3:0] rx_tuser, + output rx_tlast, + output rx_tvalid, + input rx_tready, + + input [63:0] tx_tdata, + input [3:0] tx_tuser, + input tx_tlast, + input tx_tvalid, + output tx_tready, + + output [31:0] debug_rx, + output [31:0] debug_tx +); + + wire clear = 1'b0; + wire [7:0] rx_data, tx_data; + wire tx_clk, tx_valid, tx_error, tx_ack; + wire rx_clk, rx_valid, rx_error, rx_ack; + + wire pause_req; + wire pause_request_en, pause_respect_en; + wire [15:0] pause_time, pause_thresh, pause_time_req, rx_fifo_space; + + wire [31:0] debug_state; + + wire tx_reset, rx_reset; + reset_sync reset_sync_tx + ( + .clk(tx_clk), + .reset_in(reset), + .reset_out(tx_reset) + ); + + reset_sync reset_sync_rx ( + .clk(rx_clk), + .reset_in(reset), + .reset_out(rx_reset) + ); + + simple_gemac simple_gemac + ( + .clk125(clk125), .reset(reset), + .GMII_GTX_CLK(gmii_gtx_clk), + .GMII_TX_EN(gmii_tx_en), + .GMII_TX_ER(gmii_tx_er), + .GMII_TXD(gmii_txd), + .GMII_RX_CLK(gmii_rx_clk), + .GMII_RX_DV(gmii_rx_dv), + .GMII_RX_ER(gmii_rx_er), + .GMII_RXD(gmii_rxd), + .pause_req(RX_FLOW_CTRL ? pause_req : 1'b0), + .pause_time_req(RX_FLOW_CTRL ? pause_time_req : 16'd0), + .pause_respect_en(pause_respect_en), + .ucast_addr(48'h0), + .mcast_addr(48'h0), + .pass_ucast(1'b0), + .pass_mcast(1'b0), + .pass_bcast(1'b0), + .pass_pause(1'b0), + .pass_all(1'b1), + .rx_clk(rx_clk), + .rx_data(rx_data), + .rx_valid(rx_valid), + .rx_error(rx_error), + .rx_ack(rx_ack), + .tx_clk(tx_clk), + .tx_data(tx_data), + .tx_valid(tx_valid), + .tx_error(tx_error), + .tx_ack(tx_ack), + .debug(debug_state) + ); + + assign pause_respect_en = 1'b0; + assign pause_request_en = 1'b0; + + /////////////////////////////////////////////////////////////////////////////////////// + // RX FIFO Chain + wire rx_ll_eof; + wire rx_ll_error; + wire rx_ll_src_rdy; + wire rx_ll_dst_rdy; + wire [7:0] rx_ll_data; + + wire [63:0] rx_tdata_int; + wire [3:0] rx_tuser_int; + wire rx_tlast_int; + wire rx_tvalid_int; + wire rx_tready_int; + + rxmac_to_ll8 rxmac_to_ll8 + ( + .clk(rx_clk), + .reset(rx_reset), + .clear(clear), + .rx_data(rx_data), + .rx_valid(rx_valid), + .rx_error(rx_error), + .rx_ack(rx_ack), + + .ll_data(rx_ll_data), + .ll_sof(), + .ll_eof(rx_ll_eof), + .ll_error(rx_ll_error), // ignore sof + .ll_src_rdy(rx_ll_src_rdy), + .ll_dst_rdy(rx_ll_dst_rdy) + ); + + ll8_to_axi64 #(.START_BYTE(6), .LABEL(PORTNUM)) ll8_to_axi64 + ( + .clk(rx_clk), + .reset(rx_reset), + .clear(clear), + .ll_data(rx_ll_data), + .ll_eof(rx_ll_eof), + .ll_error(rx_ll_error), + .ll_src_rdy(rx_ll_src_rdy), + .ll_dst_rdy(rx_ll_dst_rdy), + .axi64_tdata(rx_tdata_int), + .axi64_tlast(rx_tlast_int), + .axi64_tuser(rx_tuser_int), + .axi64_tvalid(rx_tvalid_int), + .axi64_tready(rx_tready_int) + ); + + axi64_8k_2clk_fifo rxfifo_2clk + ( + .s_aresetn(~rx_reset), + .s_aclk(rx_clk), + .s_axis_tvalid(rx_tvalid_int), + .s_axis_tready(rx_tready_int), + .s_axis_tdata(rx_tdata_int), + .s_axis_tlast(rx_tlast_int), + .s_axis_tuser(rx_tuser_int), + .axis_wr_data_count(), + + .m_aclk(sys_clk), + .m_axis_tvalid(rx_tvalid), + .m_axis_tready(rx_tready), + .m_axis_tdata(rx_tdata), + .m_axis_tlast(rx_tlast), + .m_axis_tuser(rx_tuser), + .axis_rd_data_count() + ); + + /////////////////////////////////////////////////////////////////////////////////////// + // TX FIFO Chain + wire tx_ll_eof, tx_ll_src_rdy, tx_ll_dst_rdy; + wire [7:0] tx_ll_data; + + wire [63:0] tx_tdata_int; + wire [3:0] tx_tuser_int; + wire tx_tlast_int; + wire tx_tvalid_int; + wire tx_tready_int; + + axi64_8k_2clk_fifo txfifo_2clk + ( + .s_aresetn(~tx_reset), + .s_aclk(sys_clk), + .s_axis_tvalid(tx_tvalid), + .s_axis_tready(tx_tready), + .s_axis_tdata(tx_tdata), + .s_axis_tlast(tx_tlast), + .s_axis_tuser(tx_tuser), + .axis_wr_data_count(), + .m_aclk(tx_clk), + .m_axis_tvalid(tx_tvalid_int), + .m_axis_tready(tx_tready_int), + .m_axis_tdata(tx_tdata_int), + .m_axis_tlast(tx_tlast_int), + .m_axis_tuser(tx_tuser_int), + .axis_rd_data_count() + ); + + axi64_to_ll8 #(.START_BYTE(6)) axi64_to_ll8 + ( + .clk(tx_clk), + .reset(tx_reset), + .clear(clear), + .axi64_tdata(tx_tdata_int), + .axi64_tlast(tx_tlast_int), + .axi64_tuser(tx_tuser_int), + .axi64_tvalid(tx_tvalid_int), + .axi64_tready(tx_tready_int), + .ll_data(tx_ll_data), + .ll_eof(tx_ll_eof), + .ll_src_rdy(tx_ll_src_rdy), + .ll_dst_rdy(tx_ll_dst_rdy) + ); + + ll8_to_txmac ll8_to_txmac + ( + .clk(tx_clk), + .reset(tx_reset), + .clear(clear), + .ll_data(tx_ll_data), + .ll_eof(tx_ll_eof), + .ll_src_rdy(tx_ll_src_rdy), + .ll_dst_rdy(tx_ll_dst_rdy), + .tx_data(tx_data), + .tx_valid(tx_valid), + .tx_error(tx_error), + .tx_ack(tx_ack) + ); + +endmodule diff --git a/fpga/usrp3/lib/simple_gemac/ll8_to_axi64.v b/fpga/usrp3/lib/simple_gemac/ll8_to_axi64.v new file mode 100644 index 000000000..22192cfd0 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/ll8_to_axi64.v @@ -0,0 +1,94 @@ +// +// Copyright 2014 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +// Takes 8-bit wide data on a Local-link fifo interface and converts to 64-bit wide axi +// Parameter START_BYTE controls which byte of the 8 the first incoming byte goes into +// Use START_BYTE=6 with ethernet to nicely align the words for processing of IP packets +// Parameter LABEL specifies a value to put in the high word of the very first packet. +// This is useful for labeling packets with the port it came from so downstream knows +// how to process it. LABEL gets overwritten if START_BYTE = 0 + +module ll8_to_axi64 + #(parameter START_BYTE=6, + parameter LABEL=8'h00) + (input clk, input reset, input clear, + input [7:0] ll_data, input ll_eof, input ll_error, input ll_src_rdy, output ll_dst_rdy, + output [63:0] axi64_tdata, output axi64_tlast, output [3:0] axi64_tuser, output axi64_tvalid, input axi64_tready); + + wire error_int, eof_int; + wire [7:0] data_int; + wire valid_int, ready_int; + + axi_fifo_short #(.WIDTH(10)) ll8_fifo + (.clk(clk), .reset(reset), .clear(0), + .i_tdata({ll_error, ll_eof, ll_data}), .i_tvalid(ll_src_rdy), .i_tready(ll_dst_rdy), + .o_tdata({error_int, eof_int, data_int}), .o_tvalid(valid_int), .o_tready(ready_int), + .space(), .occupied()); + + wire [7:0] label_wire = LABEL; // Enforces parameter width + + reg [3:0] state = START_BYTE; + reg [63:0] holding; // = {label_wire, 56'h0}; + reg err, eof, done; + reg [3:0] occ; + + localparam WAIT = 4'd8; + + always @(posedge clk) + if(reset | clear) + begin + state <= START_BYTE; + holding <= {56'h0, label_wire}; + err <= 1'b0; + eof <= 1'b0; + occ <= 3'd0; + done <= 1'b0; + end + else + if(state == WAIT) + begin + state <= START_BYTE; + done <= 1'b0; + holding <= {56'h0, label_wire}; + end + else if(valid_int & ready_int) + begin + holding[state*8 +: 8] <= data_int; + + err <= error_int; + eof <= eof_int; + if(error_int | eof_int) + begin + occ <= state+1; + done <= 1'b1; + state <= WAIT; + end + else if (state == 4'd7) + begin + occ <= 3'd0; + done <= 1'b1; + state <= 4'd0; + end + else + begin + occ <= 3'd0; + done <= 1'b0; + state <= state + 4'd1; + end // else: !if(state == 4'd7) + + end // if (valid_int & ready_int) + else + done <= 1'b0; + + assign axi64_tdata = holding; + assign axi64_tlast = eof; + assign axi64_tuser[3] = err; + assign axi64_tuser[2:0] = occ; + + assign ready_int = axi64_tready & (state != WAIT); + assign axi64_tvalid = done; + +endmodule // ll8_to_axi64 diff --git a/fpga/usrp3/lib/simple_gemac/ll8_to_axi64_tb.v b/fpga/usrp3/lib/simple_gemac/ll8_to_axi64_tb.v new file mode 100644 index 000000000..3f86b41dd --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/ll8_to_axi64_tb.v @@ -0,0 +1,100 @@ +// +// Copyright 2014 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +`timescale 1ns/1ps + +module ll8_to_axi64_tb(); + + reg clk = 0; + reg reset = 1; + + always #10 clk = ~clk; + + initial $dumpfile("ll8_to_axi64_tb.vcd"); + initial $dumpvars(0,ll8_to_axi64_tb); + + initial + begin + #1000 reset = 0; + #2000000; + $finish; + end + + wire [63:0] tdata, tdata_int; + wire [3:0] tuser, tuser_int; + wire tlast, tlast_int; + wire tvalid, tvalid_int, tready, tready_int; + + reg [7:0] ll_data; + reg ll_eof, ll_error, ll_src_rdy; + wire ll_dst_rdy; + + wire [7:0] ll_data2; + wire ll_eof2, ll_src_rdy2, ll_dst_rdy2; + + localparam RPT_COUNT = 12; + + initial + begin + ll_src_rdy <= 0; + + while(reset) + @(posedge clk); + + @(posedge clk); + + {ll_error, ll_eof, ll_data} <= { 1'b0, 1'b0, 8'hA0 }; + repeat(RPT_COUNT-1) + begin + ll_src_rdy <= 1; + @(posedge clk); + ll_data <= ll_data + 1; + end + ll_eof <= 1; + ll_data <= ll_data + 1; + @(posedge clk); + + {ll_error, ll_eof, ll_data} <= { 1'b0, 1'b0, 8'hC0 }; + repeat(RPT_COUNT-1) + begin + ll_src_rdy <= 1; + @(posedge clk); + ll_data <= ll_data + 1; + end + ll_eof <= 1; ll_error <= 1; + ll_data <= ll_data + 1; + @(posedge clk); + ll_src_rdy <= 1'b0; + + end + + ll8_to_axi64 #(.START_BYTE(6), .LABEL(8'h89)) ll8_to_axi64 + (.clk(clk), .reset(reset), .clear(1'b0), + .ll_data(ll_data), .ll_eof(ll_eof), .ll_error(ll_error), .ll_src_rdy(ll_src_rdy), .ll_dst_rdy(ll_dst_rdy), + .axi64_tdata(tdata), .axi64_tlast(tlast), .axi64_tuser(tuser), .axi64_tvalid(tvalid), .axi64_tready(tready) ); + + axi_fifo_short #(.WIDTH(69)) axi_fifo_short + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({tlast,tuser,tdata}), .i_tvalid(tvalid), .i_tready(tready), + .o_tdata({tlast_int,tuser_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int)); + + axi64_to_ll8 #(.START_BYTE(6)) axi64_to_ll8 + (.clk(clk), .reset(reset), .clear(1'b0), + .axi64_tdata(tdata_int), .axi64_tlast(tlast_int), .axi64_tuser(tuser_int), .axi64_tvalid(tvalid_int), .axi64_tready(tready_int), + .ll_data(ll_data2), .ll_eof(ll_eof2), .ll_src_rdy(ll_src_rdy2), .ll_dst_rdy(ll_dst_rdy2) ); + + /* + always @(posedge clk) + if(ll_src_rdy2 & ll_dst_rdy2) + $display("EOF %x\tDATA %x",ll_eof2, ll_data2); + + */ + assign ll_dst_rdy2 = 1; + + always @(posedge clk) + if(tvalid_int & tready_int) + $display("TERR %x\tTUSER %x\tTLAST %x\tTDATA %x",tuser_int[3],tuser_int[2:0], tlast_int, tdata_int); + +endmodule // ll8_to_axi64_tb diff --git a/fpga/usrp3/lib/simple_gemac/ll8_to_txmac.v b/fpga/usrp3/lib/simple_gemac/ll8_to_txmac.v new file mode 100644 index 000000000..6251ce73d --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/ll8_to_txmac.v @@ -0,0 +1,51 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +module ll8_to_txmac + (input clk, input reset, input clear, + input [7:0] ll_data, input ll_eof, input ll_src_rdy, output ll_dst_rdy, + output [7:0] tx_data, output tx_valid, output tx_error, input tx_ack ); + + reg [2:0] xfer_state; + + localparam XFER_IDLE = 0; + localparam XFER_ACTIVE = 1; + localparam XFER_WAIT1 = 2; + localparam XFER_UNDERRUN = 3; + localparam XFER_DROP = 4; + + always @(posedge clk) + if(reset | clear) + xfer_state <= XFER_IDLE; + else + case(xfer_state) + XFER_IDLE : + if(tx_ack) + xfer_state <= XFER_ACTIVE; + XFER_ACTIVE : + if(~ll_src_rdy) + xfer_state <= XFER_UNDERRUN; + else if(ll_eof) + xfer_state <= XFER_WAIT1; + XFER_WAIT1 : + xfer_state <= XFER_IDLE; + XFER_UNDERRUN : + xfer_state <= XFER_DROP; + XFER_DROP : + if(ll_eof) + xfer_state <= XFER_IDLE; + endcase // case (xfer_state) + + assign ll_dst_rdy = (xfer_state == XFER_ACTIVE) | tx_ack | (xfer_state == XFER_DROP); + assign tx_valid = (ll_src_rdy & (xfer_state == XFER_IDLE))|(xfer_state == XFER_ACTIVE); + assign tx_data = ll_data; + assign tx_error = (xfer_state == XFER_UNDERRUN); + +endmodule // ll8_to_txmac + diff --git a/fpga/usrp3/lib/simple_gemac/mdio.v b/fpga/usrp3/lib/simple_gemac/mdio.v new file mode 100644 index 000000000..54b5e183c --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/mdio.v @@ -0,0 +1,852 @@ +// +// Copyright 2014 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Define MDIO to add support for clause 22 and clause 45 MDIO interface +`define MDIO +// If WB clock is 62.5MHz and max MDC spec is 2.5MHz, then divide by 25 +//`define MDC_HALF_PERIOD 13 // Closest int to 12.5 +`define MDC_HALF_PERIOD 100 + +// Registers +`define CPUREG_MDIO_DATA 8'h10 +`define CPUREG_MDIO_ADDR 8'h14 +`define CPUREG_MDIO_OP 8'h18 +`define CPUREG_MDIO_CONTROL 8'h1c +`define CPUREG_MDIO_STATUS 8'h1c +`define CPUREG_GPIO 8'h20 + + +module mdio + ( + // Wishbone Bus + input wb_clk_i, + input wb_rst_i, + input [7:0] wb_adr_i, + input [31:0] wb_dat_i, + input wb_we_i, + input wb_stb_i, + input wb_cyc_i, + output reg [31:0] wb_dat_o, + output wb_ack_o, + output reg wb_int_o, + // MDIO + output reg mdc, + output reg mdio_out, + output reg mdio_tri, + input mdio_in + ); + + // + // State Declarations + // + parameter + IDLE = 0, + PREAMBLE1 = 1, + PREAMBLE2 = 2, + PREAMBLE3 = 3, + PREAMBLE4 = 4, + PREAMBLE5 = 5, + PREAMBLE6 = 6, + PREAMBLE7 = 7, + PREAMBLE8 = 8, + PREAMBLE9 = 9, + PREAMBLE10 = 10, + PREAMBLE11 = 11, + PREAMBLE12 = 12, + PREAMBLE13 = 13, + PREAMBLE14 = 14, + PREAMBLE15 = 15, + PREAMBLE16 = 16, + PREAMBLE17 = 17, + PREAMBLE18 = 18, + PREAMBLE19 = 19, + PREAMBLE20 = 20, + PREAMBLE21 = 21, + PREAMBLE22 = 22, + PREAMBLE23 = 23, + PREAMBLE24 = 24, + PREAMBLE25 = 25, + PREAMBLE26 = 26, + PREAMBLE27 = 27, + PREAMBLE28 = 28, + PREAMBLE29 = 29, + PREAMBLE30 = 30, + PREAMBLE31 = 31, + PREAMBLE32 = 32, + START1 = 33, + C22_START2 = 34, + C45_START2 = 35, + OP1 = 36, + OP2 = 37, + PRTAD1 = 38, + PRTAD2 = 39, + PRTAD3 = 40, + PRTAD4 = 41, + PRTAD5 = 42, + DEVAD1 = 43, + DEVAD2 = 44, + DEVAD3 = 45, + DEVAD4 = 46, + DEVAD5 = 47, + TA1 = 48, + TA2 = 49, + TA3 = 50, + READ1 = 51, + READ2 = 52, + READ3 = 53, + READ4 = 54, + READ5 = 55, + READ6 = 56, + READ7 = 57, + READ8 = 58, + READ9 = 59, + READ10 = 60, + READ11 = 61, + READ12 = 62, + READ13 = 63, + READ14 = 64, + READ15 = 65, + READ16 = 66, + WRITE1 = 67, + WRITE2 = 68, + WRITE3 = 69, + WRITE4 = 70, + WRITE5 = 71, + WRITE6 = 72, + WRITE7 = 73, + WRITE8 = 74, + WRITE9 = 75, + WRITE10 = 76, + WRITE11 = 77, + WRITE12 = 78, + WRITE13 = 79, + WRITE14 = 80, + WRITE15 = 81, + WRITE16 = 82, + C45_ADDR1 = 83, + C45_ADDR2 = 84, + C45_ADDR3 = 85, + C45_ADDR4 = 86, + C45_ADDR5 = 87, + C45_ADDR6 = 88, + C45_ADDR7 = 89, + C45_ADDR8 = 90, + C45_ADDR9 = 91, + C45_ADDR10 = 92, + C45_ADDR11 = 93, + C45_ADDR12 = 94, + C45_ADDR13 = 95, + C45_ADDR14 = 96, + C45_ADDR15 = 97, + C45_ADDR16 = 98, + PREIDLE = 99; + + reg cpuack; + reg [15:0] mdio_read_data; + reg [15:0] mdio_write_data; + reg [15:0] mdio_address; + reg [12:0] mdio_operation; + reg mdio_control; + reg [7:0] mdc_clk_count; + reg mdc_falling_edge; + reg mdio_running; + reg mdio_done; + reg [7:0] state; + + + assign wb_ack_o = cpuack && wb_stb_i; + + always @(posedge wb_clk_i or posedge wb_rst_i) begin + + if (wb_rst_i == 1'b1) begin + wb_dat_o <= 32'b0; + wb_int_o <= 1'b0; + cpuack <= 1'b0; + + mdio_address <= 0; + mdio_operation <= 0; + mdio_write_data <= 0; + mdio_running <= 0; + end + else begin + + wb_int_o <= 1'b0; + cpuack <= wb_cyc_i && wb_stb_i; + // Handshake to MDIO state machine to reset running flag in status. + // Wait for falling MDC edge to prevent S/W race condition occuring + // where done flag still asserted but running flag now cleared (repeatedly). + if (mdio_done && mdc_falling_edge) + mdio_running <= 0; + + // + // Read access + // + if (wb_cyc_i && wb_stb_i && !wb_we_i) begin + + case ({wb_adr_i[7:2], 2'b0}) + + `CPUREG_MDIO_DATA: begin + wb_dat_o <= {16'b0, mdio_read_data}; + end + + `CPUREG_MDIO_STATUS: begin + wb_dat_o <= {31'b0, mdio_running}; + end + + default: begin + end + + endcase + end + + // + // Write access + // + if (wb_cyc_i && wb_stb_i && wb_we_i) begin + $display("reg write @ addr %x",({wb_adr_i[7:2], 2'b0})); + + case ({wb_adr_i[7:2], 2'b0}) + + `CPUREG_MDIO_DATA: begin + mdio_write_data <= wb_dat_i[15:0]; + end + + `CPUREG_MDIO_ADDR: begin + mdio_address <= wb_dat_i[15:0]; + end + + `CPUREG_MDIO_OP: begin + mdio_operation <= wb_dat_i[12:0]; + end + + `CPUREG_MDIO_CONTROL: begin + // Trigger mdio operation here. Cleared by state machine at end of bus transaction. + if (wb_dat_i[0]) + mdio_running <= 1; + end + + default: begin + end + + endcase + + end + + end + + end // always @ (posedge wb_clk_i or posedge wb_rst_i) + + + // + // Produce mdc clock as a signal synchronously from Wishbone clock. + // + always @(posedge wb_clk_i or posedge wb_rst_i) + if (wb_rst_i) + begin + mdc_clk_count <= 1; + mdc <= 0; + mdc_falling_edge <= 0; + end + else if (mdc_clk_count == `MDC_HALF_PERIOD) + begin + mdc_clk_count <= 1; + mdc <= ~mdc; + mdc_falling_edge <= mdc; + end + else + begin + mdc_clk_count <= mdc_clk_count + 1; + mdc_falling_edge <= 0; + end + + // + // MDIO state machine + // + always @(posedge wb_clk_i or posedge wb_rst_i) + if (wb_rst_i) + begin + mdio_tri <= 1; + mdio_out <= 0; + mdio_done <= 0; + mdio_read_data <= 0; + state <= IDLE; + end + else if (mdc_falling_edge) + // + // This is the MDIO bus controller. Use falling edge of MDC. + // + begin + // Defaults + mdio_tri <= 1; + mdio_out <= 0; + mdio_done <= 0; + + + case(state) + // IDLE. + // In Clause 22 & 45 the master of the MDIO bus is tristate during idle. + // + IDLE: begin + mdio_tri <= 1; + mdio_out <= 0; + if (mdio_running) + state <= PREAMBLE1; + end + // Preamble. All MDIO transactions begin witrh 32bits of 1 bits as a preamble. + PREAMBLE1: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE2; + end + PREAMBLE2: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE3; + end + PREAMBLE3: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE4; + end + PREAMBLE4: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE5; + end + PREAMBLE5: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE6; + end + PREAMBLE6: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE7; + end + PREAMBLE7: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE8; + end + PREAMBLE8: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE9; + end + PREAMBLE9: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE10; + end + PREAMBLE10: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE11; + end + PREAMBLE11: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE12; + end + PREAMBLE12: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE13; + end + PREAMBLE13: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE14; + end + PREAMBLE14: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE15; + end + PREAMBLE15: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE16; + end + PREAMBLE16: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE17; + end + PREAMBLE17: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE18; + end + PREAMBLE18: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE19; + end + PREAMBLE19: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE20; + end + PREAMBLE20: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE21; + end + PREAMBLE21: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE22; + end + PREAMBLE22: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE23; + end + PREAMBLE23: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE24; + end + PREAMBLE24: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE25; + end + PREAMBLE25: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE26; + end + PREAMBLE26: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE27; + end + PREAMBLE27: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE28; + end + PREAMBLE28: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE29; + end + PREAMBLE29: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE30; + end + PREAMBLE30: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE31; + end + PREAMBLE31: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= PREAMBLE32; + end + PREAMBLE32: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= START1; + end + // + // Start code for Clause 22 is 01 and Clause 45 is 00 + // + START1: begin + mdio_tri <= 0; + mdio_out <= 0; + if (mdio_operation[12]) + // Clause 45 bit set. + state <= C45_START2; + else + state <= C22_START2; + end + // + // 2nd Clause 22 start bit is a 1 + // + C22_START2: begin + mdio_tri <= 0; + mdio_out <= 1; + state <= OP1; + end + // + // 2nd Clause 45 start bit is a 0 + // + C45_START2: begin + mdio_tri <= 0; + mdio_out <= 0; + state <= OP1; + end + // + // Both Clause 22 & 45 use 2 bits for operation and are compatable. + // Note we don't screen here for illegal Clause 22 ops. + // + OP1: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[11]; + state <= OP2; + end + OP2: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[10]; + state <= PRTAD1; + end + // + // Both Clause 22 & 45 use 2 sucsessive 5 bit fields to form a hierarchical address + // though it's used slightly different between the 2 standards. + // + PRTAD1: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[9]; + state <= PRTAD2; + end + PRTAD2: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[8]; + state <= PRTAD3; + end + PRTAD3: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[7]; + state <= PRTAD4; + end + PRTAD4: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[6]; + state <= PRTAD5; + end + PRTAD5: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[5]; + state <= DEVAD1; + end + DEVAD1: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[4]; + state <= DEVAD2; + end + DEVAD2: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[3]; + state <= DEVAD3; + end + DEVAD3: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[2]; + state <= DEVAD4; + end + DEVAD4: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[1]; + state <= DEVAD5; + end + DEVAD5: begin + mdio_tri <= 0; + mdio_out <= mdio_operation[0]; + state <= TA1; + end + // + // Both Clause 22 & Clause 45 use the same turn around on the bus. + // Reads have Z as the first bit and 0 driven by the slave for the 2nd bit. + // Note that slaves drive the bus on the rising edge of MDC. + // Writes and Address cycles have 10 driven by the master. + // + TA1: begin + // Clause22 write or clause45 write or address go to state TA2 + if ((mdio_operation[12:11] == 2'b10) || (mdio_operation[12:11] == 2'b01)) + begin + mdio_tri <= 0; + mdio_out <= 1; + state <= TA2; + end + else // Read + begin + mdio_tri <= 1; + state <= TA3; + end + end + TA2: begin + mdio_tri <= 0; + mdio_out <= 0; + if (!mdio_operation[12]) // Clause 22 Write + state <= WRITE1; + else if (mdio_operation[10]) // Clause 45 Write + state <= WRITE1; + else // Clause 45 ADDRESS + state <= C45_ADDR1; + end + TA3: begin + mdio_tri <= 1; + state <= READ1; + end + // + // Clause 22 Reads and both forms of clause 45 Reads have the same bus transaction from here out. + // + READ1: begin + mdio_tri <= 1; + mdio_read_data[15] <= mdio_in; + state <= READ2; + end + READ2: begin + mdio_tri <= 1; + mdio_read_data[14] <= mdio_in; + state <= READ3; + end + READ3: begin + mdio_tri <= 1; + mdio_read_data[13] <= mdio_in; + state <= READ4; + end + READ4: begin + mdio_tri <= 1; + mdio_read_data[12] <= mdio_in; + state <= READ5; + end + READ5: begin + mdio_tri <= 1; + mdio_read_data[11] <= mdio_in; + state <= READ6; + end + READ6: begin + mdio_tri <= 1; + mdio_read_data[10] <= mdio_in; + state <= READ7; + end + READ7: begin + mdio_tri <= 1; + mdio_read_data[9] <= mdio_in; + state <= READ8; + end + READ8: begin + mdio_tri <= 1; + mdio_read_data[8] <= mdio_in; + state <= READ9; + end + READ9: begin + mdio_tri <= 1; + mdio_read_data[7] <= mdio_in; + state <= READ10; + end + READ10: begin + mdio_tri <= 1; + mdio_read_data[6] <= mdio_in; + state <= READ11; + end + READ11: begin + mdio_tri <= 1; + mdio_read_data[5] <= mdio_in; + state <= READ12; + end + READ12: begin + mdio_tri <= 1; + mdio_read_data[4] <= mdio_in; + state <= READ13; + end + READ13: begin + mdio_tri <= 1; + mdio_read_data[3] <= mdio_in; + state <= READ14; + end + READ14: begin + mdio_tri <= 1; + mdio_read_data[2] <= mdio_in; + state <= READ15; + end + READ15: begin + mdio_tri <= 1; + mdio_read_data[1] <= mdio_in; + state <= READ16; + end + READ16: begin + mdio_tri <= 1; + mdio_read_data[0] <= mdio_in; + state <= PREIDLE; + mdio_done <= 1; + end + // + // Write 16bits of data for all types of Write. + // + WRITE1:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[15]; + state <= WRITE2; + end + WRITE2:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[14]; + state <= WRITE3; + end + WRITE3:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[13]; + state <= WRITE4; + end + WRITE4:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[12]; + state <= WRITE5; + end + WRITE5:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[11]; + state <= WRITE6; + end + WRITE6:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[10]; + state <= WRITE7; + end + WRITE7:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[9]; + state <= WRITE8; + end + WRITE8:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[8]; + state <= WRITE9; + end + WRITE9:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[7]; + state <= WRITE10; + end + WRITE10:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[6]; + state <= WRITE11; + end + WRITE11:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[5]; + state <= WRITE12; + end + WRITE12:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[4]; + state <= WRITE13; + end + WRITE13:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[3]; + state <= WRITE14; + end + WRITE14:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[2]; + state <= WRITE15; + end + WRITE15:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[1]; + state <= WRITE16; + end + WRITE16:begin + mdio_tri <= 0; + mdio_out <= mdio_write_data[0]; + state <= PREIDLE; + mdio_done <= 1; + end + // + // Write 16bits of address for a Clause 45 Address transaction + // + C45_ADDR1:begin + mdio_tri <= 0; + mdio_out <= mdio_address[15]; + state <= C45_ADDR2; + end + C45_ADDR2:begin + mdio_tri <= 0; + mdio_out <= mdio_address[14]; + state <= C45_ADDR3; + end + C45_ADDR3:begin + mdio_tri <= 0; + mdio_out <= mdio_address[13]; + state <= C45_ADDR4; + end + C45_ADDR4:begin + mdio_tri <= 0; + mdio_out <= mdio_address[12]; + state <= C45_ADDR5; + end + C45_ADDR5:begin + mdio_tri <= 0; + mdio_out <= mdio_address[11]; + state <= C45_ADDR6; + end + C45_ADDR6:begin + mdio_tri <= 0; + mdio_out <= mdio_address[10]; + state <= C45_ADDR7; + end + C45_ADDR7:begin + mdio_tri <= 0; + mdio_out <= mdio_address[9]; + state <= C45_ADDR8; + end + C45_ADDR8:begin + mdio_tri <= 0; + mdio_out <= mdio_address[8]; + state <= C45_ADDR9; + end + C45_ADDR9:begin + mdio_tri <= 0; + mdio_out <= mdio_address[7]; + state <= C45_ADDR10; + end + C45_ADDR10:begin + mdio_tri <= 0; + mdio_out <= mdio_address[6]; + state <= C45_ADDR11; + end + C45_ADDR11:begin + mdio_tri <= 0; + mdio_out <= mdio_address[5]; + state <= C45_ADDR12; + end + C45_ADDR12:begin + mdio_tri <= 0; + mdio_out <= mdio_address[4]; + state <= C45_ADDR13; + end + C45_ADDR13:begin + mdio_tri <= 0; + mdio_out <= mdio_address[3]; + state <= C45_ADDR14; + end + C45_ADDR14:begin + mdio_tri <= 0; + mdio_out <= mdio_address[2]; + state <= C45_ADDR15; + end + C45_ADDR15:begin + mdio_tri <= 0; + mdio_out <= mdio_address[1]; + state <= C45_ADDR16; + end + C45_ADDR16:begin + mdio_tri <= 0; + mdio_out <= mdio_address[0]; + state <= PREIDLE; + mdio_done <= 1; + end + // + // PREIDLE allows the mdio_running bit to reset. + // + PREIDLE: begin + state <= IDLE; + end + endcase // case(state) + + end // if (mdc_falling_edge) + + +endmodule + diff --git a/fpga/usrp3/lib/simple_gemac/rxmac_to_ll8.v b/fpga/usrp3/lib/simple_gemac/rxmac_to_ll8.v new file mode 100644 index 000000000..6c452a9eb --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/rxmac_to_ll8.v @@ -0,0 +1,62 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +module rxmac_to_ll8 + (input clk, input reset, input clear, + input [7:0] rx_data, input rx_valid, input rx_error, input rx_ack, + output [7:0] ll_data, output ll_sof, output ll_eof, output ll_error, output ll_src_rdy, input ll_dst_rdy ); + + reg [2:0] xfer_state; + + localparam XFER_IDLE = 0; + localparam XFER_ACTIVE = 1; + localparam XFER_ERROR = 2; + localparam XFER_ERROR2 = 3; + localparam XFER_OVERRUN = 4; + localparam XFER_OVERRUN2 = 5; + + assign ll_data = rx_data; + assign ll_src_rdy = ((rx_valid & (xfer_state != XFER_OVERRUN2) ) + | (xfer_state == XFER_ERROR) + | (xfer_state == XFER_OVERRUN)); + assign ll_sof = ((xfer_state==XFER_IDLE)|(xfer_state==XFER_ERROR)|(xfer_state==XFER_OVERRUN)); + assign ll_eof = (rx_ack | (xfer_state==XFER_ERROR) | (xfer_state==XFER_OVERRUN)); + assign ll_error = (xfer_state == XFER_ERROR)|(xfer_state==XFER_OVERRUN); + + always @(posedge clk) + if(reset | clear) + xfer_state <= XFER_IDLE; + else + case(xfer_state) + XFER_IDLE : + if(rx_valid) + xfer_state <= XFER_ACTIVE; + XFER_ACTIVE : + if(rx_error) + xfer_state <= XFER_ERROR; + else if(~rx_valid) + xfer_state <= XFER_IDLE; + else if(~ll_dst_rdy) + xfer_state <= XFER_OVERRUN; + XFER_ERROR : + if(ll_dst_rdy) + xfer_state <= XFER_ERROR2; + XFER_ERROR2 : + if(~rx_error) + xfer_state <= XFER_IDLE; + XFER_OVERRUN : + if(ll_dst_rdy) + xfer_state <= XFER_OVERRUN2; + XFER_OVERRUN2 : + if(~rx_valid) + xfer_state <= XFER_IDLE; + endcase // case (xfer_state) + + +endmodule // rxmac_to_ll8 diff --git a/fpga/usrp3/lib/simple_gemac/simple_gemac.v b/fpga/usrp3/lib/simple_gemac/simple_gemac.v new file mode 100644 index 000000000..46bc518fc --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac.v @@ -0,0 +1,72 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +module simple_gemac + (input clk125, input reset, + // GMII + output GMII_GTX_CLK, output GMII_TX_EN, output GMII_TX_ER, output [7:0] GMII_TXD, + input GMII_RX_CLK, input GMII_RX_DV, input GMII_RX_ER, input [7:0] GMII_RXD, + + // Flow Control Interface + input pause_req, input [15:0] pause_time_req, input pause_respect_en, + + // Settings + input [47:0] ucast_addr, input [47:0] mcast_addr, + input pass_ucast, input pass_mcast, input pass_bcast, input pass_pause, input pass_all, + + // RX Client Interface + output rx_clk, output [7:0] rx_data, output rx_valid, output rx_error, output rx_ack, + + // TX Client Interface + output tx_clk, input [7:0] tx_data, input tx_valid, input tx_error, output tx_ack, + + output [31:0] debug + ); + + localparam SGE_IFG = 8'd12; // 12 should be the absolute minimum + + wire rst_rxclk, rst_txclk; + reset_sync reset_sync_tx (.clk(tx_clk),.reset_in(reset),.reset_out(rst_txclk)); + reset_sync reset_sync_rx (.clk(rx_clk),.reset_in(reset),.reset_out(rst_rxclk)); + + wire [15:0] pause_quanta_rcvd; + wire pause_rcvd, pause_apply, paused; + + simple_gemac_tx simple_gemac_tx + (.clk125(clk125),.reset(rst_txclk), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .tx_clk(tx_clk), .tx_data(tx_data), .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack), + .ifg(SGE_IFG), .mac_addr(ucast_addr), + .pause_req(pause_req), .pause_time(pause_time_req), // We request flow control + .pause_apply(pause_apply), .paused(paused) // We respect flow control + ); + + simple_gemac_rx simple_gemac_rx + (.reset(rst_rxclk), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + .rx_clk(rx_clk), .rx_data(rx_data), .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .ucast_addr(ucast_addr), .mcast_addr(mcast_addr), + .pass_ucast(pass_ucast), .pass_mcast(pass_mcast), .pass_bcast(pass_bcast), + .pass_pause(pass_pause), .pass_all(pass_all), + .pause_quanta_rcvd(pause_quanta_rcvd), .pause_rcvd(pause_rcvd), + .debug(debug) + ); + + flow_ctrl_tx flow_ctrl_tx + (.rst(rst_txclk), .tx_clk(tx_clk), + .tx_pause_en(pause_respect_en), + .pause_quanta(pause_quanta_rcvd), // 16 bit value + .pause_quanta_val(pause_rcvd), + .pause_apply(pause_apply), + .paused(paused) + ); + +endmodule // simple_gemac diff --git a/fpga/usrp3/lib/simple_gemac/simple_gemac_rx.v b/fpga/usrp3/lib/simple_gemac/simple_gemac_rx.v new file mode 100644 index 000000000..d63233b1b --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_rx.v @@ -0,0 +1,185 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + + +module simple_gemac_rx + (input reset, + input GMII_RX_CLK, input GMII_RX_DV, input GMII_RX_ER, input [7:0] GMII_RXD, + output rx_clk, output [7:0] rx_data, output reg rx_valid, output rx_error, output reg rx_ack, + input [47:0] ucast_addr, input [47:0] mcast_addr, + input pass_ucast, input pass_mcast, input pass_bcast, input pass_pause, input pass_all, + output reg [15:0] pause_quanta_rcvd, output pause_rcvd, + output [31:0] debug ); + + localparam RX_IDLE = 0; + localparam RX_PREAMBLE = 1; + localparam RX_FRAME = 2; + localparam RX_GOODFRAME = 3; + localparam RX_DO_PAUSE = 4; + localparam RX_ERROR = 5; + localparam RX_DROP = 6; + + localparam RX_PAUSE = 16; + localparam RX_PAUSE_CHK88 = RX_PAUSE + 5; + localparam RX_PAUSE_CHK08 = RX_PAUSE_CHK88 + 1; + localparam RX_PAUSE_CHK00 = RX_PAUSE_CHK08 + 1; + localparam RX_PAUSE_CHK01 = RX_PAUSE_CHK00 + 1; + localparam RX_PAUSE_STORE_MSB = RX_PAUSE_CHK01 + 1; + localparam RX_PAUSE_STORE_LSB = RX_PAUSE_STORE_MSB + 1; + localparam RX_PAUSE_WAIT_CRC = RX_PAUSE_STORE_LSB + 1; + + reg [7:0] rxd_d1; + reg rx_dv_d1, rx_er_d1; + assign rx_clk = GMII_RX_CLK; + + always @(posedge rx_clk) + begin + rx_dv_d1 <= GMII_RX_DV; + rx_er_d1 <= GMII_RX_ER; + rxd_d1 <= GMII_RXD; + end + + reg [7:0] rx_state; + wire [7:0] rxd_del; + wire rx_dv_del, rx_er_del; + reg go_filt; + + wire match_crc; + wire clear_crc = rx_state == RX_IDLE; + wire calc_crc = (rx_state == RX_FRAME) | rx_state[7:4]==4'h1; + + localparam DELAY = 6; + delay_line #(.WIDTH(10)) rx_delay + (.clk(rx_clk), .delay(DELAY), .din({rx_dv_d1,rx_er_d1,rxd_d1}),.dout({rx_dv_del,rx_er_del,rxd_del})); + + always @(posedge rx_clk) + if(reset) + rx_ack <= 0; + else + rx_ack <= (rx_state == RX_GOODFRAME); + + wire is_ucast, is_bcast, is_mcast, is_pause, is_any_ucast; + wire keep_packet = (pass_all & is_any_ucast) | (pass_ucast & is_ucast) | (pass_mcast & is_mcast) | + (pass_bcast & is_bcast) | (pass_pause & is_pause); + + assign rx_data = rxd_del; + assign rx_error = (rx_state == RX_ERROR); + + always @(posedge rx_clk) + if(reset) + rx_valid <= 0; + else if(keep_packet) + rx_valid <= 1; + else if((rx_state == RX_IDLE)|(rx_state == RX_ERROR)) + rx_valid <= 0; + + address_filter af_ucast (.clk(rx_clk), .reset(reset), .go(go_filt), .data(rxd_d1), + .address(ucast_addr), .match(is_ucast), .done()); + address_filter af_mcast (.clk(rx_clk), .reset(reset), .go(go_filt), .data(rxd_d1), + .address(mcast_addr), .match(is_mcast), .done()); + address_filter af_bcast (.clk(rx_clk), .reset(reset), .go(go_filt), .data(rxd_d1), + .address(48'hFFFF_FFFF_FFFF), .match(is_bcast), .done()); + address_filter af_pause (.clk(rx_clk), .reset(reset), .go(go_filt), .data(rxd_d1), + .address(48'h0180_c200_0001), .match(is_pause), .done()); + address_filter_promisc af_promisc (.clk(rx_clk), .reset(reset), .go(go_filt), .data(rxd_d1), + .match(is_any_ucast), .done()); + + always @(posedge rx_clk) + go_filt <= (rx_state==RX_PREAMBLE) & (rxd_d1 == 8'hD5); + + reg [15:0] pkt_len_ctr; + always @(posedge rx_clk) + if(reset |(rx_state == RX_IDLE)) + pkt_len_ctr <= 0; + else + pkt_len_ctr <= pkt_len_ctr + 1; + + localparam MIN_PAUSE_LEN = 71; // 6 + wire pkt_long_enough = (pkt_len_ctr >= MIN_PAUSE_LEN); + always @(posedge rx_clk) + if(reset) + rx_state <= RX_IDLE; + else + if(rx_er_d1 & ~((rxd_d1==8'h0F)&(~rx_dv_d1))) //Handle odd-length pkts from Xilinx IP. + rx_state <= RX_ERROR; + else + case(rx_state) + RX_IDLE : + if(rx_dv_d1) + if(rxd_d1 == 8'h55) + rx_state <= RX_PREAMBLE; + else + rx_state <= RX_ERROR; + RX_PREAMBLE : + if(~rx_dv_d1) + rx_state <= RX_ERROR; + else if(rxd_d1 == 8'hD5) + rx_state <= RX_FRAME; + else if(rxd_d1 != 8'h55) + rx_state <= RX_ERROR; + RX_FRAME : + if(is_pause) + rx_state <= RX_PAUSE; + else if(~rx_dv_d1) + if(match_crc) + rx_state <= RX_GOODFRAME; + else + rx_state <= RX_ERROR; + RX_PAUSE_CHK88 : + if(rxd_d1 != 8'h88) + rx_state <= RX_DROP; + else + rx_state <= RX_PAUSE_CHK08; + RX_PAUSE_CHK08 : + if(rxd_d1 != 8'h08) + rx_state <= RX_DROP; + else + rx_state <= RX_PAUSE_CHK00; + RX_PAUSE_CHK00 : + if(rxd_d1 != 8'h00) + rx_state <= RX_DROP; + else + rx_state <= RX_PAUSE_CHK01; + RX_PAUSE_CHK01 : + if(rxd_d1 != 8'h01) + rx_state <= RX_DROP; + else + rx_state <= RX_PAUSE_STORE_MSB; + RX_PAUSE_WAIT_CRC : + if(pkt_long_enough) + if(match_crc) + rx_state <= RX_DO_PAUSE; + else + rx_state <= RX_DROP; + RX_DO_PAUSE : + rx_state <= RX_IDLE; + RX_GOODFRAME : + rx_state <= RX_IDLE; + RX_DROP, RX_ERROR : + if(~rx_dv_d1) + rx_state <= RX_IDLE; + default + rx_state <= rx_state + 1; + endcase // case (rx_state) + + assign pause_rcvd = (rx_state == RX_DO_PAUSE); + crc crc_check(.clk(rx_clk),.reset(reset),.clear(clear_crc), + .data(rxd_d1),.calc(calc_crc),.crc_out(),.match(match_crc)); + + always @(posedge rx_clk) + if(reset) + pause_quanta_rcvd <= 0; + else if(rx_state == RX_PAUSE_STORE_MSB) + pause_quanta_rcvd[15:8] <= rxd_d1; + else if(rx_state == RX_PAUSE_STORE_LSB) + pause_quanta_rcvd[7:0] <= rxd_d1; + + assign debug = rx_state; + +endmodule // simple_gemac_rx diff --git a/fpga/usrp3/lib/simple_gemac/simple_gemac_tb.v b/fpga/usrp3/lib/simple_gemac/simple_gemac_tb.v new file mode 100644 index 000000000..c65db6c98 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_tb.v @@ -0,0 +1,208 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + + +module simple_gemac_tb; +`include "eth_tasks.v" + + reg clk = 0; + reg reset = 1; + + initial #1000 reset = 0; + always #50 clk = ~clk; + + wire GMII_RX_DV, GMII_RX_ER, GMII_TX_EN, GMII_TX_ER, GMII_GTX_CLK; + wire [7:0] GMII_RXD, GMII_TXD; + + wire rx_valid, rx_error, rx_ack; + wire tx_ack, tx_valid, tx_error; + + wire [7:0] rx_data, tx_data; + + reg [15:0] pause_time; + reg pause_req = 0; + + wire GMII_RX_CLK = GMII_GTX_CLK; + + reg [7:0] FORCE_DAT_ERR = 0; + reg FORCE_ERR = 0; + + // Loopback + assign GMII_RX_DV = GMII_TX_EN; + assign GMII_RX_ER = GMII_TX_ER | FORCE_ERR; + assign GMII_RXD = GMII_TXD ^ FORCE_DAT_ERR; + + wire [47:0] ucast_addr = 48'hF1F2_F3F4_F5F6; + wire [47:0] mcast_addr = 0; + wire pass_ucast =1, pass_mcast=0, pass_bcast=1, pass_pause=0, pass_all=0; + + simple_gemac simple_gemac + (.clk125(clk), .reset(reset), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + .pause_req(pause_req), .pause_time(pause_time), .pause_en(1), + .ucast_addr(ucast_addr), .mcast_addr(mcast_addr), + .pass_ucast(pass_ucast), .pass_mcast(pass_mcast), .pass_bcast(pass_bcast), + .pass_pause(pass_pause), .pass_all(pass_all), + .rx_clk(rx_clk), .rx_data(rx_data), + .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .tx_clk(tx_clk), .tx_data(tx_data), + .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack) + ); + + wire rx_ll_sof, rx_ll_eof, rx_ll_src_rdy, rx_ll_dst_rdy; + wire rx_ll_sof2, rx_ll_eof2, rx_ll_src_rdy2; + reg rx_ll_dst_rdy2 = 1; + wire [7:0] rx_ll_data, rx_ll_data2; + wire rx_ll_error, rx_ll_error2; + + rxmac_to_ll8 rx_adapt + (.clk(clk), .reset(reset), .clear(0), + .rx_data(rx_data), .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .ll_data(rx_ll_data), .ll_sof(rx_ll_sof), .ll_eof(rx_ll_eof), .ll_error(rx_ll_error), + .ll_src_rdy(rx_ll_src_rdy), .ll_dst_rdy(rx_ll_dst_rdy)); + + ll8_shortfifo rx_sfifo + (.clk(clk), .reset(reset), .clear(0), + .datain(rx_ll_data), .sof_i(rx_ll_sof), .eof_i(rx_ll_eof), + .error_i(rx_ll_error), .src_rdy_i(rx_ll_src_rdy), .dst_rdy_o(rx_ll_dst_rdy), + .dataout(rx_ll_data2), .sof_o(rx_ll_sof2), .eof_o(rx_ll_eof2), + .error_o(rx_ll_error2), .src_rdy_o(rx_ll_src_rdy2), .dst_rdy_i(rx_ll_dst_rdy2)); + + wire tx_ll_sof, tx_ll_eof, tx_ll_src_rdy, tx_ll_dst_rdy; + reg tx_ll_sof2=0, tx_ll_eof2=0; + reg tx_ll_src_rdy2 = 0; + wire tx_ll_dst_rdy2; + wire [7:0] tx_ll_data; + reg [7:0] tx_ll_data2 = 0; + wire tx_ll_error; + wire tx_ll_error2 = 0; + + ll8_shortfifo tx_sfifo + (.clk(clk), .reset(reset), .clear(clear), + .datain(tx_ll_data2), .sof_i(tx_ll_sof2), .eof_i(tx_ll_eof2), + .error_i(tx_ll_error2), .src_rdy_i(tx_ll_src_rdy2), .dst_rdy_o(tx_ll_dst_rdy2), + .dataout(tx_ll_data), .sof_o(tx_ll_sof), .eof_o(tx_ll_eof), + .error_o(tx_ll_error), .src_rdy_o(tx_ll_src_rdy), .dst_rdy_i(tx_ll_dst_rdy)); + + ll8_to_txmac ll8_to_txmac + (.clk(clk), .reset(reset), .clear(clear), + .ll_data(tx_ll_data), .ll_sof(tx_ll_sof), .ll_eof(tx_ll_eof), + .ll_src_rdy(tx_ll_src_rdy), .ll_dst_rdy(tx_ll_dst_rdy), + .tx_data(tx_data), .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack)); + + initial $dumpfile("simple_gemac_tb.vcd"); + initial $dumpvars(0,simple_gemac_tb); + + integer i; + reg [7:0] pkt_rom[0:65535]; + reg [1023:0] ROMFile; + + initial + for (i=0;i<65536;i=i+1) + pkt_rom[i] <= 8'h0; + + initial + begin + @(negedge reset); + repeat (10) + @(posedge clk); + SendFlowCtrl(16'h0007); // Send flow control + @(posedge clk); + #30000; + @(posedge clk); + SendFlowCtrl(16'h0009); // Increas flow control before it expires + #10000; + @(posedge clk); + SendFlowCtrl(16'h0000); // Cancel flow control before it expires + @(posedge clk); + + SendPacket_to_ll8(8'hAA,10); // This packet gets dropped by the filters + repeat (10) + @(posedge clk); + + SendPacketFromFile_ll8(60,0,0); // The rest are valid packets + repeat (10) + @(posedge clk); + + SendPacketFromFile_ll8(61,0,0); + repeat (10) + @(posedge clk); + SendPacketFromFile_ll8(62,0,0); + repeat (10) + @(posedge clk); + SendPacketFromFile_ll8(63,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_ll8(64,0,0); + repeat (10) + @(posedge clk); + SendPacketFromFile_ll8(59,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_ll8(58,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_ll8(100,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_ll8(200,150,30); // waiting 14 empties the fifo, 15 underruns + repeat (1) + @(posedge clk); + SendPacketFromFile_ll8(100,0,30); + #10000 $finish; + end + + // Force a CRC error + initial + begin + #90000; + @(posedge clk); + FORCE_DAT_ERR <= 8'h10; + @(posedge clk); + FORCE_DAT_ERR <= 8'h00; + end + + // Force an RX_ER error (i.e. link loss) + initial + begin + #116000; + @(posedge clk); + FORCE_ERR <= 1; + @(posedge clk); + FORCE_ERR <= 0; + end + + // Cause receive fifo to fill, causing an RX overrun + initial + begin + #126000; + @(posedge clk); + rx_ll_dst_rdy2 <= 0; + repeat (30) // Repeat of 14 fills the shortfifo, but works. 15 overflows + @(posedge clk); + rx_ll_dst_rdy2 <= 1; + end + + // Tests: Send and recv flow control, send and receive good packets, RX CRC err, RX_ER, RX overrun, TX underrun + // Still need to test: CRC errors on Pause Frames + + always @(posedge clk) + if(rx_ll_src_rdy2 & rx_ll_dst_rdy2) + begin + if(rx_ll_sof2 & ~rx_ll_eof2) + $display("RX-PKT-START %d",$time); + $display("RX-PKT SOF %d EOF %d ERR%d DAT %x",rx_ll_sof2,rx_ll_eof2,rx_ll_error2,rx_ll_data2); + if(rx_ll_eof2 & ~rx_ll_sof2) + $display("RX-PKT-END %d",$time); + end + +endmodule // simple_gemac_tb diff --git a/fpga/usrp3/lib/simple_gemac/simple_gemac_tx.v b/fpga/usrp3/lib/simple_gemac/simple_gemac_tx.v new file mode 100644 index 000000000..555872fdb --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_tx.v @@ -0,0 +1,262 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +module simple_gemac_tx + (input clk125, input reset, + output GMII_GTX_CLK, output reg GMII_TX_EN, output reg GMII_TX_ER, output reg [7:0] GMII_TXD, + output tx_clk, input [7:0] tx_data, input tx_valid, input tx_error, output tx_ack, + input [7:0] ifg, input [47:0] mac_addr, + input pause_req, input [15:0] pause_time, + input pause_apply, output reg paused + ); + + reg tx_en_pre, tx_er_pre; + reg [7:0] txd_pre; + + assign GMII_GTX_CLK = clk125; + assign tx_clk = clk125; + + reg [7:0] tx_state; + reg [7:0] ifg_ctr; + reg [15:0] frame_len_ctr; + reg [7:0] pause_ctr, pause_dat; + + wire in_ifg = (ifg_ctr != 0); + + wire [31:0] crc_out; + + localparam TX_IDLE = 0; + localparam TX_PREAMBLE = 1; + localparam TX_SOF_DEL = TX_PREAMBLE + 7; + localparam TX_FIRSTBYTE = TX_SOF_DEL + 1; + localparam TX_IN_FRAME = TX_FIRSTBYTE + 1; + localparam TX_IN_FRAME_2 = TX_IN_FRAME + 1; + localparam TX_PAD = TX_IN_FRAME_2 + 1; + localparam TX_CRC_0 = 16; + localparam TX_CRC_1 = TX_CRC_0 + 1; + localparam TX_CRC_2 = TX_CRC_0 + 2; + localparam TX_CRC_3 = TX_CRC_0 + 3; + localparam TX_ERROR = 32; + localparam TX_PAUSE = 55; + localparam TX_PAUSE_SOF = TX_PAUSE + 7; + localparam TX_PAUSE_FIRST = TX_PAUSE_SOF + 1; + localparam TX_PAUSE_END = TX_PAUSE_SOF + 18; + + localparam MIN_FRAME_LEN = 64 + 8 - 4; // Min frame length includes preamble but not CRC + localparam MAX_FRAME_LEN = 8192; // How big are the jumbo frames we want to handle? + always @(posedge tx_clk) + if(reset |(tx_state == TX_IDLE)) + frame_len_ctr <= 0; + else + frame_len_ctr <= frame_len_ctr + 1; + + reg send_pause; + reg [15:0] pause_time_held; + + always @(posedge tx_clk) + if(reset) + send_pause <= 0; + else if(pause_req) + send_pause <= 1; + else if(tx_state == TX_PAUSE) + send_pause <= 0; + + always @(posedge tx_clk) + if(pause_req) + pause_time_held <= pause_time; + + always @(posedge tx_clk) + if(reset) + tx_state <= TX_IDLE; + else + case(tx_state) + TX_IDLE : + if(~in_ifg) + if(send_pause) + tx_state <= TX_PAUSE; + else if(tx_valid & ~pause_apply) + tx_state <= TX_PREAMBLE; + TX_FIRSTBYTE : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_PAD; + else + tx_state <= TX_IN_FRAME; + TX_IN_FRAME : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_PAD; + else if(frame_len_ctr == MIN_FRAME_LEN - 1) + tx_state <= TX_IN_FRAME_2; + TX_IN_FRAME_2 : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_CRC_0; + TX_PAD : + if(frame_len_ctr == MIN_FRAME_LEN) + tx_state <= TX_CRC_0; + TX_CRC_3 : + tx_state <= TX_IDLE; + TX_ERROR : + tx_state <= TX_IDLE; + TX_PAUSE_END : + tx_state <= TX_PAD; + default : + tx_state <= tx_state + 1; + endcase // case (tx_state) + + always @(posedge tx_clk) + if(reset) + begin + tx_en_pre <= 0; + tx_er_pre <= 0; + txd_pre <= 0; + end + else + casex(tx_state) + TX_IDLE : + begin + tx_en_pre <= 0; + tx_er_pre <= 0; + txd_pre <= 0; + end + TX_PREAMBLE, TX_PAUSE : + begin + txd_pre <= 8'h55; + tx_en_pre <= 1; + end + TX_SOF_DEL, TX_PAUSE_SOF : + txd_pre <= 8'hD5; + TX_FIRSTBYTE, TX_IN_FRAME, TX_IN_FRAME_2 : + txd_pre <= tx_valid ? tx_data : 0; + TX_ERROR : + begin + tx_er_pre <= 1; + txd_pre <= 0; + end + TX_CRC_3 : + tx_en_pre <= 0; + TX_PAD : + txd_pre <= 0; + TX_PAUSE_FIRST, 8'b01xx_xxxx : // In Pause Frame + txd_pre <= pause_dat; + endcase // case (tx_state) + + localparam SGE_FLOW_CTRL_ADDR = 48'h01_80_C2_00_00_01; + always @(posedge tx_clk) + case(tx_state) + TX_PAUSE_SOF : + pause_dat <= SGE_FLOW_CTRL_ADDR[47:40]; // Note everything must be 1 cycle early + TX_PAUSE_SOF + 1: + pause_dat <= SGE_FLOW_CTRL_ADDR[39:32]; + TX_PAUSE_SOF + 2: + pause_dat <= SGE_FLOW_CTRL_ADDR[31:24]; + TX_PAUSE_SOF + 3: + pause_dat <= SGE_FLOW_CTRL_ADDR[23:16]; + TX_PAUSE_SOF + 4: + pause_dat <= SGE_FLOW_CTRL_ADDR[15:8]; + TX_PAUSE_SOF + 5: + pause_dat <= SGE_FLOW_CTRL_ADDR[7:0]; + TX_PAUSE_SOF + 6: + pause_dat <= mac_addr[47:40]; + TX_PAUSE_SOF + 7: + pause_dat <= mac_addr[39:32]; + TX_PAUSE_SOF + 8: + pause_dat <= mac_addr[31:24]; + TX_PAUSE_SOF + 9: + pause_dat <= mac_addr[23:16]; + TX_PAUSE_SOF + 10: + pause_dat <= mac_addr[15:8]; + TX_PAUSE_SOF + 11: + pause_dat <= mac_addr[7:0]; + TX_PAUSE_SOF + 12: + pause_dat <= 8'h88; // Type = 8808 = MAC ctrl frame + TX_PAUSE_SOF + 13: + pause_dat <= 8'h08; + TX_PAUSE_SOF + 14: + pause_dat <= 8'h00; // Opcode = 0001 = PAUSE + TX_PAUSE_SOF + 15: + pause_dat <= 8'h01; + TX_PAUSE_SOF + 16: + pause_dat <= pause_time_held[15:8]; + TX_PAUSE_SOF + 17: + pause_dat <= pause_time_held[7:0]; + endcase // case (tx_state) + + wire start_ifg = (tx_state == TX_CRC_3); + always @(posedge tx_clk) + if(reset) + ifg_ctr <= 100; + else if(start_ifg) + ifg_ctr <= ifg; + else if(ifg_ctr != 0) + ifg_ctr <= ifg_ctr - 1; + + wire clear_crc = (tx_state == TX_IDLE); + + wire calc_crc = + (tx_state==TX_IN_FRAME) | + (tx_state==TX_IN_FRAME_2) | + (tx_state==TX_PAD) | + (tx_state[6]); + + crc crc(.clk(tx_clk), .reset(reset), .clear(clear_crc), + .data(txd_pre), .calc(calc_crc), .crc_out(crc_out)); + + assign tx_ack = (tx_state == TX_FIRSTBYTE); + + always @(posedge tx_clk) + begin + GMII_TX_EN <= tx_en_pre; + GMII_TX_ER <= tx_er_pre; + case(tx_state) + TX_CRC_0 : + GMII_TXD <= crc_out[31:24]; + TX_CRC_1 : + GMII_TXD <= crc_out[23:16]; + TX_CRC_2 : + GMII_TXD <= crc_out[15:8]; + TX_CRC_3 : + GMII_TXD <= crc_out[7:0]; + default : + GMII_TXD <= txd_pre; + endcase // case (tx_state) + end + + // report that we are paused only when we get back to IDLE + always @(posedge tx_clk) + if(reset) + paused <= 0; + else if(~pause_apply) + paused <= 0; + else if(tx_state == TX_IDLE) + paused <= 1; + +endmodule // simple_gemac_tx + +// Testing code +/* + reg [7:0] crc_ctr; + reg calc_crc_d1; + always @(posedge tx_clk) + calc_crc_d1 <= calc_crc; + + always @(posedge tx_clk) + if(reset) + crc_ctr <= 0; + else if(calc_crc) + crc_ctr <= crc_ctr+1; + else if(calc_crc_d1) + $display("CRC COUNT = %d",crc_ctr); + else + crc_ctr <= 0; +*/ diff --git a/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper.build b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper.build new file mode 100755 index 000000000..14e8500b3 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper.build @@ -0,0 +1 @@ +iverilog -Wimplict -Wportbind -y ../fifo/ -y ../models/ -y . -y miim -y ../control -y ../coregen/ -y ../control_lib/ -o simple_gemac_wrapper_tb simple_gemac_wrapper_tb.v diff --git a/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper.v b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper.v new file mode 100644 index 000000000..aee2fde4d --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper.v @@ -0,0 +1,185 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + +module simple_gemac_wrapper + #(parameter RX_FLOW_CTRL=0, + parameter PORTNUM=8'd0) + (input clk125, input reset, + // GMII + output GMII_GTX_CLK, output GMII_TX_EN, output GMII_TX_ER, output [7:0] GMII_TXD, + input GMII_RX_CLK, input GMII_RX_DV, input GMII_RX_ER, input [7:0] GMII_RXD, + + // Client FIFO Interfaces + input sys_clk, + output [63:0] rx_tdata, output [3:0] rx_tuser, output rx_tlast, output rx_tvalid, input rx_tready, + input [63:0] tx_tdata, input [3:0] tx_tuser, input tx_tlast, input tx_tvalid, output tx_tready, + + + // Wishbone Bus + input wb_clk_i, + input wb_rst_i, + input [7:0] wb_adr_i, + input [31:0] wb_dat_i, + input wb_we_i, + input wb_stb_i, + input wb_cyc_i, + output [31:0] wb_dat_o, + output wb_ack_o, + output wb_int_o, + + // MDIO + output mdc, + input mdio_out, + output mdio_tri, + output mdio_in, + + // Debug + output [31:0] debug_rx, + output [31:0] debug_tx +); + + wire clear = 0; + wire [7:0] rx_data, tx_data; + wire tx_clk, tx_valid, tx_error, tx_ack; + wire rx_clk, rx_valid, rx_error, rx_ack; + + wire pause_req; + wire pause_request_en, pause_respect_en; + wire [15:0] pause_time, pause_thresh, pause_time_req, rx_fifo_space; + + wire [31:0] debug_state; + + wire tx_reset, rx_reset; + reset_sync reset_sync_tx (.clk(tx_clk),.reset_in(reset),.reset_out(tx_reset)); + reset_sync reset_sync_rx (.clk(rx_clk),.reset_in(reset),.reset_out(rx_reset)); + + simple_gemac simple_gemac + (.clk125(clk125), .reset(reset), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + .pause_req(RX_FLOW_CTRL ? pause_req : 1'b0), .pause_time_req(RX_FLOW_CTRL ? pause_time_req : 16'd0), + .pause_respect_en(pause_respect_en), + .ucast_addr(48'h0), .mcast_addr(48'h0), + .pass_ucast(1'b0), .pass_mcast(1'b0), .pass_bcast(1'b0), + .pass_pause(1'b0), .pass_all(1'b1), + .rx_clk(rx_clk), .rx_data(rx_data), + .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .tx_clk(tx_clk), .tx_data(tx_data), + .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack), + .debug(debug_state) + ); + + assign pause_respect_en = 1'b0; + assign pause_request_en = 1'b0; + + // ///////////////////////////////////////////////////////////////////////////////////// + // RX FIFO Chain + wire rx_ll_eof, rx_ll_error, rx_ll_src_rdy, rx_ll_dst_rdy; + wire [7:0] rx_ll_data; + + wire [63:0] rx_tdata_int; + wire [3:0] rx_tuser_int; + wire rx_tlast_int, rx_tvalid_int, rx_tready_int; + + rxmac_to_ll8 rxmac_to_ll8 + (.clk(rx_clk), .reset(rx_reset), .clear(0), + .rx_data(rx_data), .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .ll_data(rx_ll_data), .ll_sof(), .ll_eof(rx_ll_eof), .ll_error(rx_ll_error), // ignore sof + .ll_src_rdy(rx_ll_src_rdy), .ll_dst_rdy(rx_ll_dst_rdy)); + + ll8_to_axi64 #(.START_BYTE(6), .LABEL(PORTNUM)) ll8_to_axi64 + (.clk(rx_clk), .reset(rx_reset), .clear(0), + .ll_data(rx_ll_data), .ll_eof(rx_ll_eof), .ll_error(rx_ll_error), .ll_src_rdy(rx_ll_src_rdy), .ll_dst_rdy(rx_ll_dst_rdy), + .axi64_tdata(rx_tdata_int), .axi64_tlast(rx_tlast_int), .axi64_tuser(rx_tuser_int), + .axi64_tvalid(rx_tvalid_int), .axi64_tready(rx_tready_int)); + + axi64_8k_2clk_fifo rxfifo_2clk + (.s_aresetn(~rx_reset), + .s_aclk(rx_clk), .s_axis_tvalid(rx_tvalid_int), .s_axis_tready(rx_tready_int), + .s_axis_tdata(rx_tdata_int), .s_axis_tlast(rx_tlast_int), .s_axis_tuser(rx_tuser_int), + .axis_wr_data_count(), + + .m_aclk(sys_clk), .m_axis_tvalid(rx_tvalid), .m_axis_tready(rx_tready), + .m_axis_tdata(rx_tdata), .m_axis_tlast(rx_tlast), .m_axis_tuser(rx_tuser), + .axis_rd_data_count() ); + + // ///////////////////////////////////////////////////////////////////////////////////// + // TX FIFO Chain + wire tx_ll_eof, tx_ll_src_rdy, tx_ll_dst_rdy; + wire [7:0] tx_ll_data; + + wire [63:0] tx_tdata_int; + wire [3:0] tx_tuser_int; + wire tx_tlast_int, tx_tvalid_int, tx_tready_int; + + axi64_8k_2clk_fifo txfifo_2clk + (.s_aresetn(~tx_reset), + .s_aclk(sys_clk), .s_axis_tvalid(tx_tvalid), .s_axis_tready(tx_tready), + .s_axis_tdata(tx_tdata), .s_axis_tlast(tx_tlast), .s_axis_tuser(tx_tuser), + .axis_wr_data_count(), + + .m_aclk(tx_clk), .m_axis_tvalid(tx_tvalid_int), .m_axis_tready(tx_tready_int), + .m_axis_tdata(tx_tdata_int), .m_axis_tlast(tx_tlast_int), .m_axis_tuser(tx_tuser_int), + .axis_rd_data_count() ); + + axi64_to_ll8 #(.START_BYTE(6)) axi64_to_ll8 + (.clk(tx_clk), .reset(tx_reset), .clear(0), + .axi64_tdata(tx_tdata_int), .axi64_tlast(tx_tlast_int), .axi64_tuser(tx_tuser_int), + .axi64_tvalid(tx_tvalid_int), .axi64_tready(tx_tready_int), + .ll_data(tx_ll_data), .ll_eof(tx_ll_eof), .ll_src_rdy(tx_ll_src_rdy), .ll_dst_rdy(tx_ll_dst_rdy)); + + ll8_to_txmac ll8_to_txmac + (.clk(tx_clk), .reset(tx_reset), .clear(clear), + .ll_data(tx_ll_data), .ll_eof(tx_ll_eof), .ll_src_rdy(tx_ll_src_rdy), .ll_dst_rdy(tx_ll_dst_rdy), + .tx_data(tx_data), .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack)); + + // ///////////////////////////////////////////////////////////////////////////////////// + // Flow Control + generate + if(RX_FLOW_CTRL==1) + flow_ctrl_rx flow_ctrl_rx + (.pause_request_en(pause_request_en), .pause_time(pause_time), .pause_thresh(pause_thresh), + .rx_clk(rx_clk), .rx_reset(rx_reset), .rx_fifo_space(rx_fifo_space), + .tx_clk(tx_clk), .tx_reset(tx_reset), .pause_req(pause_req), .pause_time_req(pause_time_req)); + endgenerate + + assign debug_tx = { { tx_ll_data }, + { 1'b0, tx_ll_eof, tx_ll_src_rdy, tx_ll_dst_rdy, 4'b0 }, + { tx_valid, tx_error, tx_ack, 5'b0}, + { tx_data} }; + assign debug_rx = { { rx_ll_data }, + { rx_ll_error, rx_ll_eof, rx_ll_src_rdy, rx_ll_dst_rdy, 4'b0 }, + { rx_valid, rx_error, rx_ack, 5'b0}, + { rx_data} }; + + // + // Wishbone MDIO controller + // + mdio mdio_gige_inst + ( + .wb_clk_i(wb_clk_i), + .wb_rst_i(wb_rst_i), + .wb_adr_i(wb_adr_i), + .wb_dat_i(wb_dat_i), + .wb_we_i(wb_we_i), + .wb_stb_i(wb_stb_i), + .wb_cyc_i(wb_cyc_i), + .wb_dat_o(wb_dat_o), + .wb_ack_o(wb_ack_o), + .wb_int_o(wb_int_o), + .mdc(mdc), + .mdio_out(mdio_in), // Switch sense of in and out here for master and slave. + .mdio_tri(mdio_tri), + .mdio_in(mdio_out) // Switch sense of in and out here for master and slave. + ); + +endmodule // simple_gemac_wrapper + diff --git a/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper_tb.v b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper_tb.v new file mode 100644 index 000000000..b89589234 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper_tb.v @@ -0,0 +1,213 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + + + + +module simple_gemac_wrapper_tb; +`include "eth_tasks_f36.v" + + reg reset = 1; + initial #1000 reset = 0; + wire wb_rst = reset; + + reg eth_clk = 0; + always #50 eth_clk = ~eth_clk; + + reg wb_clk = 0; + always #173 wb_clk = ~wb_clk; + + reg sys_clk = 0; + always #77 sys_clk = ~ sys_clk; + + wire GMII_RX_DV, GMII_RX_ER, GMII_TX_EN, GMII_TX_ER, GMII_GTX_CLK; + wire [7:0] GMII_RXD, GMII_TXD; + + wire rx_valid, rx_error, rx_ack; + wire tx_ack, tx_valid, tx_error; + + wire [7:0] rx_data, tx_data; + + wire GMII_RX_CLK = GMII_GTX_CLK; + + reg [7:0] FORCE_DAT_ERR = 0; + reg FORCE_ERR = 0; + + // Loopback + assign GMII_RX_DV = GMII_TX_EN; + assign GMII_RX_ER = GMII_TX_ER | FORCE_ERR; + assign GMII_RXD = GMII_TXD ^ FORCE_DAT_ERR; + + + wire [31:0] wb_dat_o; + reg [31:0] wb_dat_i; + reg [7:0] wb_adr; + reg wb_stb=0, wb_cyc=0, wb_we=0; + wire wb_ack; + + reg [35:0] tx_f36_data=0; + reg tx_f36_src_rdy = 0; + wire tx_f36_dst_rdy; + wire [35:0] rx_f36_data; + wire rx_f36_src_rdy; + wire rx_f36_dst_rdy = 1; + + simple_gemac_wrapper simple_gemac_wrapper + (.clk125(eth_clk), .reset(reset), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + + .sys_clk(sys_clk), .rx_f36_data(rx_f36_data), .rx_f36_src_rdy(rx_f36_src_rdy), .rx_f36_dst_rdy(rx_f36_dst_rdy), + .tx_f36_data(tx_f36_data), .tx_f36_src_rdy(tx_f36_src_rdy), .tx_f36_dst_rdy(tx_f36_dst_rdy), + + .wb_clk(wb_clk), .wb_rst(wb_rst), .wb_stb(wb_stb), .wb_cyc(wb_cyc), .wb_ack(wb_ack), .wb_we(wb_we), + .wb_adr(wb_adr), .wb_dat_i(wb_dat_i), .wb_dat_o(wb_dat_o), + + .mdio(), .mdc(), + .debug() ); + + initial $dumpfile("simple_gemac_wrapper_tb.vcd"); + initial $dumpvars(0,simple_gemac_wrapper_tb); + + integer i; + reg [7:0] pkt_rom[0:65535]; + reg [1023:0] ROMFile; + + initial + for (i=0;i<65536;i=i+1) + pkt_rom[i] <= 8'h0; + + initial + begin + @(negedge reset); + repeat (10) + @(posedge wb_clk); + WishboneWR(0,6'b111101); + WishboneWR(4,16'hA0B0); + WishboneWR(8,32'hC0D0_A1B1); + WishboneWR(12,16'h0000); + WishboneWR(16,32'h0000_0000); + + @(posedge eth_clk); + SendFlowCtrl(16'h0007); // Send flow control + @(posedge eth_clk); + #30000; + @(posedge eth_clk); + SendFlowCtrl(16'h0009); // Increase flow control before it expires + #10000; + @(posedge eth_clk); + SendFlowCtrl(16'h0000); // Cancel flow control before it expires + @(posedge eth_clk); + + repeat (1000) + @(posedge sys_clk); + SendPacket_to_fifo36(32'hA0B0C0D0,10); // This packet gets dropped by the filters + repeat (1000) + @(posedge sys_clk); + + SendPacket_to_fifo36(32'hAABBCCDD,100); // This packet gets dropped by the filters + repeat (10) + @(posedge sys_clk); +/* + SendPacketFromFile_f36(60,0,0); // The rest are valid packets + repeat (10) + @(posedge clk); + + SendPacketFromFile_f36(61,0,0); + repeat (10) + @(posedge clk); + SendPacketFromFile_f36(62,0,0); + repeat (10) + @(posedge clk); + SendPacketFromFile_f36(63,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_f36(64,0,0); + repeat (10) + @(posedge clk); + SendPacketFromFile_f36(59,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_f36(58,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_f36(100,0,0); + repeat (1) + @(posedge clk); + SendPacketFromFile_f36(200,150,30); // waiting 14 empties the fifo, 15 underruns + repeat (1) + @(posedge clk); + SendPacketFromFile_f36(100,0,30); + */ + #100000 $finish; + end + + // Force a CRC error + initial + begin + #90000; + @(posedge eth_clk); + FORCE_DAT_ERR <= 8'h10; + @(posedge eth_clk); + FORCE_DAT_ERR <= 8'h00; + end + + // Force an RX_ER error (i.e. link loss) + initial + begin + #116000; + @(posedge eth_clk); + FORCE_ERR <= 1; + @(posedge eth_clk); + FORCE_ERR <= 0; + end +/* + // Cause receive fifo to fill, causing an RX overrun + initial + begin + #126000; + @(posedge clk); + rx_ll_dst_rdy2 <= 0; + repeat (30) // Repeat of 14 fills the shortfifo, but works. 15 overflows + @(posedge clk); + rx_ll_dst_rdy2 <= 1; + end + */ + // Tests: Send and recv flow control, send and receive good packets, RX CRC err, RX_ER, RX overrun, TX underrun + // Still need to test: CRC errors on Pause Frames, MDIO, wishbone + + task WishboneWR; + input [7:0] adr; + input [31:0] value; + begin + wb_adr <= adr; + wb_dat_i <= value; + wb_stb <= 1; + wb_cyc <= 1; + wb_we <= 1; + while (~wb_ack) + @(posedge wb_clk); + @(posedge wb_clk); + wb_stb <= 0; + wb_cyc <= 0; + wb_we <= 0; + end + endtask // WishboneWR + /* + always @(posedge clk) + if(rx_ll_src_rdy2 & rx_ll_dst_rdy2) + begin + if(rx_ll_sof2 & ~rx_ll_eof2) + $display("RX-PKT-START %d",$time); + $display("RX-PKT SOF %d EOF %d ERR%d DAT %x",rx_ll_sof2,rx_ll_eof2,rx_ll_error2,rx_ll_data2); + if(rx_ll_eof2 & ~rx_ll_sof2) + $display("RX-PKT-END %d",$time); + end + */ +endmodule // simple_gemac_wrapper_tb diff --git a/fpga/usrp3/lib/simple_gemac/test_packet.mem b/fpga/usrp3/lib/simple_gemac/test_packet.mem new file mode 100644 index 000000000..7f41d3e42 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/test_packet.mem @@ -0,0 +1,66 @@ +ff +ff +ff +ff +ff +ff +08 +00 +07 +5c +2e +e4 +08 +06 +00 +01 +08 +04 +06 +02 +00 +01 +08 +00 +07 +5c +2e +e4 +03 +64 +00 +00 +00 +00 +00 +00 +02 +64 +00 +3a +f3 +5c +4f +12 +01 +10 +00 +01 +00 +00 +00 +00 +00 +00 +20 +41 +42 +41 +08 +00 +AA +BB +CC +DD +EE +FF |