diff options
Diffstat (limited to 'fpga/usrp3/lib/simple_gemac')
23 files changed, 2847 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..f1fe18a4a --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/Makefile.srcs @@ -0,0 +1,24 @@ +# +# 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 \ +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..ccae0ea20 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/address_filter.v @@ -0,0 +1,40 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +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..ec2364c92 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/address_filter_promisc.v @@ -0,0 +1,37 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +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..c82bc4fd0 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/axi64_to_ll8.v @@ -0,0 +1,49 @@ + + +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; +    +   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; +   assign axi64_tready = ready_int & (eof_int | state == 7); +   assign eof_int = axi64_tlast & (axi64_tuser[2:0] == (state + 3'd1)); +       +   always @* +     case(state) +       0 : data_int <= axi64_tdata[63:56]; +       1 : data_int <= axi64_tdata[55:48]; +       2 : data_int <= axi64_tdata[47:40]; +       3 : data_int <= axi64_tdata[39:32]; +       4 : data_int <= axi64_tdata[31:24]; +       5 : data_int <= axi64_tdata[23:16]; +       6 : data_int <= axi64_tdata[15:8]; +       7 : data_int <= axi64_tdata[7:0]; +       default : data_int <= axi64_tdata[7:0]; +     endcase // case (state) +    +   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..3ce289f82 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/crc.v @@ -0,0 +1,71 @@ +// +// Copyright 2011 Ettus Research LLC +// + + +
 +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..325182f38 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/delay_line.v @@ -0,0 +1,26 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +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..e505948de --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/eth_tasks.v @@ -0,0 +1,161 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +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..048c26b62 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/flow_ctrl_rx.v @@ -0,0 +1,66 @@ +// +// Copyright 2011 Ettus Research LLC +// + + +
 +// 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..11c120b1c --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/flow_ctrl_tx.v @@ -0,0 +1,44 @@ +// +// Copyright 2011 Ettus Research LLC +// + + +
 +// 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/ll8_to_axi64.v b/fpga/usrp3/lib/simple_gemac/ll8_to_axi64.v new file mode 100644 index 000000000..778728eea --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/ll8_to_axi64.v @@ -0,0 +1,102 @@ + +// 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 <= {label_wire, 56'h0}; +	  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 <= {label_wire, 56'h0}; +	 end +       else if(valid_int & ready_int) +	 begin +	    case(state) +	      4'd0:  +		begin +		   holding[63:56] <= data_int; +		   holding[55:0] <= 56'h0; +		end +	      4'd1: holding[55:48] <= data_int; +	      4'd2: holding[47:40] <= data_int; +	      4'd3: holding[39:32] <= data_int; +	      4'd4: holding[31:24] <= data_int; +	      4'd5: holding[23:16] <= data_int; +	      4'd6: holding[15:8] <= data_int; +	      4'd7: holding[7:0] <= data_int; +	    endcase // case (state) +	     +	    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..590875338 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/ll8_to_axi64_tb.v @@ -0,0 +1,95 @@ +`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..67729c52d --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/ll8_to_txmac.v @@ -0,0 +1,48 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + +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..81fe5ba8f --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/mdio.v @@ -0,0 +1,847 @@ +// 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..4a6713397 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/rxmac_to_ll8.v @@ -0,0 +1,59 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + +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..87a739ccd --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac.v @@ -0,0 +1,69 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + +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..72b2c73d2 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_rx.v @@ -0,0 +1,182 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +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) // | (~pkt_long_enough & ~rx_dv_d1) & (rx_state != RX_IDLE)) +	 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..9f4f19e6d --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_tb.v @@ -0,0 +1,205 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +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..923587baa --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_tx.v @@ -0,0 +1,259 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + +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..47fe37b8e --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper.v @@ -0,0 +1,182 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + +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..2b9f38772 --- /dev/null +++ b/fpga/usrp3/lib/simple_gemac/simple_gemac_wrapper_tb.v @@ -0,0 +1,210 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + + +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 | 
