diff options
author | Martin Braun <martin.braun@ettus.com> | 2020-01-23 16:10:22 -0800 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2020-01-28 09:35:36 -0800 |
commit | bafa9d95453387814ef25e6b6256ba8db2df612f (patch) | |
tree | 39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/lib/simple_gemac | |
parent | 3075b981503002df3115d5f1d0b97d2619ba30f2 (diff) | |
download | uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2 uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip |
Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.
This commit also updates the license files and paths therein.
We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.
== Original Codebase and Rebasing ==
The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/
It can be used for bisecting, reference, and a more detailed history.
The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.
If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:
- Create a directory to store patches (this should be an empty
directory):
mkdir ~/patches
- Now make sure that your FPGA codebase is based on the same state as
the code that was merged:
cd src/fpga # Or wherever your FPGA code is stored
git rebase v4.0.0.0-pre-uhd-merge
Note: The rebase command may look slightly different depending on what
exactly you're trying to rebase.
- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:
git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches
Note: Make sure that only patches are stored in your output directory.
It should otherwise be empty. Make sure that you picked the correct
range of commits, and only commits you wanted to rebase were exported
as patch files.
- Go to the UHD repository and apply the patches:
cd src/uhd # Or wherever your UHD repository is stored
git am --directory fpga ~/patches/*
rm -rf ~/patches # This is for cleanup
== Contributors ==
The following people have contributed mainly to these files (this list
is not complete):
Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>
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 |