`define ADDR_ETH_STATUS 16'hFF68 `define ADDR_ETH 16'hFF69 module Ethernet ( input clk, input wr, input rd, input [15:0] addr, inout [7:0] data, input ethclk, rxclk, input rxp, rxm, output txp, txm); wire [10:0] txhwaddr; wire [7:0] txhwdata; wire txbusy; reg [10:0] txlength = 0; reg [10:0] txwraddr = 0; reg [1:0] txstate = 0; reg txstart = 0; reg [15:0] addrlatch; reg rdlatch; wire decode = (addrlatch == `ADDR_ETH_STATUS) || (addrlatch == `ADDR_ETH); reg [7:0] odata; assign data = (decode && rdlatch) ? odata : 8'bzzzzzzzz; EnetTX tx( .clk20(ethclk), .Ethernet_TDp(txp), .Ethernet_TDm(txm), .busy(txbusy), .length(txlength), .rdaddress(txhwaddr), .start(txstart), .indata(txhwdata)); EthModRam txram( .wdata(data), .waddr(txwraddr), .wr(wr && txstate == 2'b10 && addr == `ADDR_ETH), .wrclk(clk), .rdata(txhwdata), .raddr(txhwaddr), .rdclk(ethclk)); wire [10:0] rxphyaddr; wire [7:0] rxphydata; wire rxphywr; wire rxpktrdy; reg rxclear = 0; reg [1:0] rxstate = 0; wire [10:0] rxlength; reg [10:0] rxcurlength; reg [10:0] rxcoreaddr = 0; wire [7:0] rxcoredata; EnetRX rx( .rxclk(rxclk), .manchester_data_in(~rxp), .wr(rxphywr), .oaddr(rxphyaddr), .odata(rxphydata), .pktrdy(rxpktrdy), .olength(rxlength), .pktclear(rxclear)); EthModRam rxram( .wdata(rxphydata), .waddr(rxphyaddr), .wr(rxphywr), .wrclk(rxclk), .rdata(rxcoredata), .raddr(rxcoreaddr), .rdclk(clk)); always @ (posedge clk) begin addrlatch <= addr; rdlatch <= rd; if (rd && addr == `ADDR_ETH_STATUS) odata <= {txstate,4'b0,rxpktrdy,txbusy}; else if (wr && addr == `ADDR_ETH_STATUS) begin /* Reset the state machines. */ rxstate <= 2'b00; txstate <= 2'b00; end else if (rd && addr == `ADDR_ETH) begin case (rxstate) 2'b00: begin if (!rxpktrdy) rxcurlength <= 0; else rxcurlength <= rxlength; odata <= rxpktrdy ? {5'b0,rxlength[10:8]} : 8'b0; rxstate <= 2'b01; end 2'b01: begin rxstate <= (rxcurlength == 0) ? 2'b00 : 2'b10; odata <= rxcurlength[7:0]; rxcoreaddr <= 0; end 2'b10: begin odata <= rxcoredata; if (rxcoreaddr == (rxcurlength - 1)) begin rxstate <= 2'b11; rxclear <= 1; end else rxcoreaddr <= rxcoreaddr + 1; end endcase end else if (rxstate == 2'b11 || rxclear) begin rxstate <= 2'b00; rxclear <= 0; end else if (wr && addr == `ADDR_ETH) begin case(txstate) 2'b00: begin txlength[10:8] <= data[2:0]; txstate <= 2'b01; end 2'b01: begin txlength[7:0] <= data[7:0]; txstate <= 2'b10; txwraddr <= 0; end 2'b10: if(txwraddr == (txlength - 1)) begin txstate <= 2'b11; txstart <= 1'b1; end else txwraddr <= txwraddr + 1; endcase end else if (txstate == 2'b11) begin txstart <= 1'b0; txstate <= 2'b00; end end endmodule module EthModRam ( input [7:0] wdata, input [10:0] waddr, input [10:0] raddr, input wr, input wrclk, input rdclk, output reg [7:0] rdata); reg [7:0] mem [1600:0]; always @(posedge wrclk) begin if(wr) mem[waddr] <= wdata; end always @(posedge rdclk) rdata <= mem[raddr]; endmodule module EnetTX(input clk20, output reg Ethernet_TDp, output reg Ethernet_TDm, output wire busy, input start, input [11:0] length, output wire [11:0] rdaddress, input [7:0] indata); reg StartSending; always @(posedge clk20) StartSending<=start; reg [11:0] curlen = 0; always @(posedge clk20) if (start) curlen <= length + 8; reg [11:0] internaladdr; assign rdaddress = internaladdr - 8; wire [7:0] pkt_data = (internaladdr < 7) ? 8'h55 : (internaladdr == 7) ? 8'hD5 : indata; ////////////////////////////////////////////////////////////////////// // and finally the 10BASE-T's magic reg [3:0] ShiftCount; reg SendingPacket; always @(posedge clk20) if(StartSending) SendingPacket<=1; else if(ShiftCount==14 && internaladdr==(curlen + 4)) SendingPacket<=0; always @(posedge clk20) ShiftCount <= SendingPacket ? ShiftCount+1 : 15; wire readram = (ShiftCount==15); always @(posedge clk20) if(ShiftCount==15) internaladdr <= SendingPacket ? internaladdr+1 : 0; reg [7:0] ShiftData; always @(posedge clk20) if(ShiftCount[0]) ShiftData <= readram ? pkt_data : {1'b0, ShiftData[7:1]}; // generate the CRC32 reg [31:0] CRC; reg CRCflush; always @(posedge clk20) if(CRCflush) CRCflush <= SendingPacket; else if(readram) CRCflush <= (internaladdr==curlen); reg CRCinit; always @(posedge clk20) if(readram) CRCinit <= (internaladdr==7); wire CRCinput = CRCflush ? 0 : (ShiftData[0] ^ CRC[31]); always @(posedge clk20) if(ShiftCount[0]) CRC <= CRCinit ? ~0 : ({CRC[30:0],1'b0} ^ ({32{CRCinput}} & 32'h04C11DB7)); // generate the NLP reg [17:0] LinkPulseCount; always @(posedge clk20) LinkPulseCount <= SendingPacket ? 0 : LinkPulseCount+1; reg LinkPulse; always @(posedge clk20) LinkPulse <= &LinkPulseCount[17:1]; // TP_IDL, shift-register and manchester encoder reg SendingPacketData; always @(posedge clk20) SendingPacketData <= SendingPacket; assign busy = SendingPacketData; reg [2:0] idlecount; always @(posedge clk20) if(SendingPacketData) idlecount<=0; else if(~&idlecount) idlecount<=idlecount+1; wire dataout = CRCflush ? ~CRC[31] : ShiftData[0]; reg qo; always @(posedge clk20) qo <= SendingPacketData ? ~dataout^ShiftCount[0] : 1; reg qoe; always @(posedge clk20) qoe <= SendingPacketData | LinkPulse | (idlecount<6); always @(posedge clk20) Ethernet_TDp <= (qoe ? qo : 1'b0); always @(posedge clk20) Ethernet_TDm <= (qoe ? ~qo : 1'b0); endmodule module EnetRX( input rxclk, input manchester_data_in, output reg wr, output reg [10:0] oaddr, output reg [7:0] odata, output reg pktrdy = 0, output reg [10:0] olength = 0, input pktclear); reg [2:0] in_data; always @(posedge rxclk) in_data <= {in_data[1:0], manchester_data_in}; reg [7:0] data; reg [1:0] cnt; always @(posedge rxclk) if(|cnt || (in_data[2] ^ in_data[1])) cnt<=cnt+1; reg new_bit_avail; always @(posedge rxclk) new_bit_avail <= (cnt==3); always @(posedge rxclk) if(cnt==3) data<={in_data[1],data[7:1]}; ///////////////////////////////////////////////// reg end_of_Ethernet_frame; reg [4:0] sync1; always @(posedge rxclk) if(end_of_Ethernet_frame) sync1<=0; else if(new_bit_avail) begin if(!(data==8'h55 || data==8'hAA)) // not preamble? sync1 <= 0; else if(~&sync1) // if all bits of this "sync1" counter are one, we decide that enough of the preamble // has been received, so stop counting and wait for "sync2" to detect the SFD sync1 <= sync1 + 1; // otherwise keep counting end reg [9:0] sync2; always @(posedge rxclk) if(end_of_Ethernet_frame || pktrdy) // i.e., if we're busy, drop it on the floor sync2 <= 0; else if(new_bit_avail) begin if(|sync2) // if the SFD has already been detected (Ethernet data is coming in) sync2 <= sync2 + 1; // then count the bits coming in else if(&sync1 && data==8'hD5) // otherwise, let's wait for the SFD (0xD5) sync2 <= sync2 + 1; end wire new_byte_available = new_bit_avail && (sync2[2:0]==3'h0) && (sync2[9:3]!=0); ///////////////////////////////////////////////// // if no clock transistion is detected for some time, that's the end of the Ethernet frame reg [2:0] transition_timeout; always @(posedge rxclk) if(in_data[2]^in_data[1]) transition_timeout<=0; else if(~&cnt) transition_timeout<=transition_timeout+1; always @(posedge rxclk) end_of_Ethernet_frame <= &transition_timeout; reg [11:0] iaddr; ///////////////////////////////////////////////// always @(posedge rxclk) if (new_byte_available && !pktrdy) begin odata <= data; oaddr <= iaddr; iaddr <= iaddr + 1; wr <= 1; end else if (end_of_Ethernet_frame && (iaddr > 1)) begin olength <= iaddr; iaddr <= 0; wr <= 0; pktrdy <= 1; end else if (pktclear) begin pktrdy <= 0; wr <= 0; end else wr <= 0; endmodule