X-Git-Url: http://git.joshuawise.com/fpgaboy.git/blobdiff_plain/4c90390a786b738c0dc0b8d55e7cc2d857f625de..99b9687942367fd256a797bcd1f67fec07f92a37:/Ethernet.v diff --git a/Ethernet.v b/Ethernet.v new file mode 100644 index 0000000..2f0c668 --- /dev/null +++ b/Ethernet.v @@ -0,0 +1,217 @@ +`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] outaddr; + wire [7:0] outdata; + wire busy; + + reg [10:0] len = 0; + reg [10:0] addrcnt = 0; + reg [1:0] state = 0; + reg start = 0; + + reg [15:0] addrlatch; + reg rdlatch; + + assign data = (addrlatch == `ADDR_ETH_STATUS && rdlatch) ? {state,5'b0,busy} : 8'bzzzzzzzz; + + EnetTX tx( + .clk20(ethclk), + .Ethernet_TDp(txp), + .Ethernet_TDm(txm), + .busy(busy), + .length(len), + .rdaddress(outaddr), + .start(start), + .indata(outdata)); + + EthModRam txram( + .wdata(data), + .waddr(addrcnt), + .raddr(outaddr), + .wr(wr && state == 2'b10 && addr == `ADDR_ETH), + .clk(clk), + .ethclk(ethclk), + .rdata(outdata) + ); + + always @ (posedge clk) begin + addrlatch <= addr; + rdlatch <= rd; + if(wr && addr == `ADDR_ETH) begin + case(state) + 2'b00: begin + len[10:8] <= data[2:0]; + state <= 2'b01; + end + 2'b01: begin + len[7:0] <= data[7:0]; + state <= 2'b10; + end + 2'b10: + if(addrcnt == len) begin + state <= 2'b11; + start <= 1'b1; + addrcnt <= 0; + end else + addrcnt <= addrcnt + 1; + endcase + end else if (state == 2'b11) begin + start <= 1'b0; + state <= 2'b00; + end + end + +endmodule + +module EthModRam ( + input [7:0] wdata, + input [10:0] waddr, + input [10:0] raddr, + input wr, + input clk, + input ethclk, + output reg [7:0] rdata +); + + reg [7:0] mem [1600:0]; + + always @ (posedge clk) begin + if(wr) + mem[waddr] <= wdata; + end + + always @(posedge ethclk) + 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 wire wr, + output reg [10:0] oaddr, + output wire [7:0] odata, + output reg pktrdy, + output reg [10:0] olength, + 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) + 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; + + ///////////////////////////////////////////////// + always @(posedge rxclk) + if (new_byte_available && !pktrdy) begin + odata <= data; + oaddr <= oaddr + 1; + wr <= 1; + end else if (end_of_Ethernet_frame) begin + olength <= oaddr; + oaddr <= 0; + wr <= 0; + pktrdy <= 1; + end else if (pktclear) begin + pktrdy <= 0; + wr <= 0; + else + wr <= 0; + +endmodule