]> Joshua Wise's Git repositories - fpgaboy.git/blob - Ethernet.v
d96158534dc52154ceda219ce7d2c66db4934d0d
[fpgaboy.git] / Ethernet.v
1 `define ADDR_ETH_STATUS 16'hFF68
2 `define ADDR_ETH 16'hFF69
3
4 module Ethernet (
5         input clk,
6         input wr,
7         input rd,
8         input [15:0] addr,
9         inout [7:0] data,
10         input ethclk, rxclk,
11         input rxp, rxm,
12         output txp, txm);
13
14         wire [10:0] txhwaddr;
15         wire [7:0] txhwdata;
16         wire txbusy;
17
18         reg [10:0] txlength = 0;
19         reg [10:0] txwraddr = 0;
20         reg [1:0] txstate = 0;
21         reg txstart = 0;
22         
23         reg [15:0] addrlatch;
24         reg rdlatch;
25         
26         wire decode = (addrlatch == `ADDR_ETH_STATUS) || (addrlatch == `ADDR_ETH);
27         reg [7:0] odata;
28         assign data = (decode && rdlatch) ? odata : 8'bzzzzzzzz;
29
30         EnetTX tx(
31                 .clk20(ethclk),
32                 .Ethernet_TDp(txp),
33                 .Ethernet_TDm(txm),
34                 .busy(txbusy),
35                 .length(txlength),
36                 .rdaddress(txhwaddr),
37                 .start(txstart),
38                 .indata(txhwdata));
39         
40         EthModRam txram(
41                 .wdata(data),
42                 .waddr(txwraddr),
43                 .wr(wr && state == 2'b10 && addr == `ADDR_ETH),
44                 .wrclk(clk),
45                 .rdata(txhwdata),
46                 .raddr(txhwaddr),
47                 .rdclk(ethclk));
48         
49         wire [10:0] rxphyaddr;
50         wire [7:0] rxphydata;
51         wire rxphywr;
52         wire rxpktrdy;
53         reg rxclear = 0;
54         reg [1:0] rxstate = 0;
55         wire [10:0] rxlength;
56         reg [10:0] rxcurlength;
57         
58         reg [10:0] rxcoreaddr = 0;
59         wire [7:0] rxcoredata;
60         
61         EnetRX rx(
62                 .rxclk(rxclk),
63                 .manchester_data_in(~rxp),
64                 .wr(rxphywr),
65                 .oaddr(rxphyaddr),
66                 .odata(rxphydata),
67                 .pktrdy(rxpktrdy),
68                 .olength(rxlength),
69                 .pktclear(rxclear));
70         
71         EthModRam rxram(
72                 .wdata(rxphydata),
73                 .waddr(rxphyaddr),
74                 .wr(rxphywr),
75                 .wrclk(rxclk),
76                 .rdata(rxcoredata),
77                 .raddr(rxcoreaddr),
78                 .rdclk(clk));
79
80         always @ (posedge clk) begin
81                 addrlatch <= addr;
82                 rdlatch <= rd;
83                 
84                 if (rd && addr == `ADDR_ETH_STATUS)
85                         odata <= {state,4'b0,rxpktrdy,txbusy};
86                 else if (wr && addr == `ADDR_ETH_STATUS) begin          /* Reset the state machines. */
87                         rxstate <= 2'b00;
88                         txstate <= 2'b00;
89                 end else if (rd && addr == `ADDR_ETH) begin
90                         case (rxstate)
91                         2'b00: begin
92                                 if (!rxpktrdy)
93                                         rxcurlength <= 0;
94                                 else
95                                         rxcurlength <= rxlength;
96                                 odata <= rxpktrdy ? {5'b0,rxlength[10:8]} : 8'b0;
97                                 rxstate <= 2'b01;
98                         end
99                         2'b01: begin
100                                 rxstate <= (rxcurlength == 0) ? 2'b00 : 2'b10;
101                                 odata <= rxcurlength[7:0];
102                                 rxcoreaddr <= 0;
103                         end
104                         2'b10: begin
105                                 odata <= rxcoredata;
106                                 if (rxcoreaddr == (rxcurlength - 1)) begin
107                                         rxstate <= 2'b11;
108                                         rxclear <= 1;
109                                 end else
110                                         rxcoreaddr <= rxcoreaddr + 1;
111                         end
112                         endcase
113                 end else if (rxstate == 2'b11 || rxclear) begin
114                         rxstate <= 2'b00;
115                         rxclear <= 0;
116                 end else if (wr && addr == `ADDR_ETH) begin
117                         case(txstate)
118                         2'b00: begin
119                                 txlength[10:8] <= data[2:0];
120                                 txstate <= 2'b01;
121                         end
122                         2'b01: begin
123                                 txlength[7:0] <= data[7:0];
124                                 txstate <= 2'b10;
125                                 txwraddr <= 0;
126                         end
127                         2'b10:
128                                 if(txwraddr == (txlength - 1)) begin
129                                         txstate <= 2'b11;
130                                         txstart <= 1'b1;
131                                 end else
132                                         txwraddr <= txwraddr + 1;
133                         endcase
134                 end else if (txstate == 2'b11) begin
135                         txstart <= 1'b0;
136                         txstate <= 2'b00;
137                 end
138         end
139
140 endmodule
141
142 module EthModRam (
143         input [7:0] wdata,
144         input [10:0] waddr,
145         input [10:0] raddr,
146         input wr,
147         input wrclk,
148         input rdclk,
149         output reg [7:0] rdata);
150
151         reg [7:0] mem [1600:0];
152
153         always @(posedge wrclk) begin
154                 if(wr)
155                         mem[waddr] <= wdata;
156         end
157
158         always @(posedge rdclk)
159                 rdata <= mem[raddr];
160 endmodule
161
162 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);
163         reg StartSending; always @(posedge clk20) StartSending<=start;
164         reg [11:0] curlen = 0; always @(posedge clk20) if (start) curlen <= length + 8;
165         
166         reg [11:0] internaladdr;
167         assign rdaddress = internaladdr - 8;
168         wire [7:0] pkt_data = (internaladdr < 7) ? 8'h55 :
169                                 (internaladdr == 7) ? 8'hD5 :
170                                 indata;
171
172         //////////////////////////////////////////////////////////////////////
173         // and finally the 10BASE-T's magic
174         reg [3:0] ShiftCount;
175         reg SendingPacket;
176         always @(posedge clk20) if(StartSending) SendingPacket<=1; else if(ShiftCount==14 && internaladdr==(curlen + 4)) SendingPacket<=0;
177         always @(posedge clk20) ShiftCount <= SendingPacket ? ShiftCount+1 : 15;
178         wire readram = (ShiftCount==15);
179         always @(posedge clk20) if(ShiftCount==15) internaladdr <= SendingPacket ? internaladdr+1 : 0;
180         reg [7:0] ShiftData; always @(posedge clk20) if(ShiftCount[0]) ShiftData <= readram ? pkt_data : {1'b0, ShiftData[7:1]};
181
182         // generate the CRC32
183         reg [31:0] CRC;
184         reg CRCflush; always @(posedge clk20) if(CRCflush) CRCflush <= SendingPacket; else if(readram) CRCflush <= (internaladdr==curlen);
185         reg CRCinit; always @(posedge clk20) if(readram) CRCinit <= (internaladdr==7);
186         wire CRCinput = CRCflush ? 0 : (ShiftData[0] ^ CRC[31]);
187         always @(posedge clk20) if(ShiftCount[0]) CRC <= CRCinit ? ~0 : ({CRC[30:0],1'b0} ^ ({32{CRCinput}} & 32'h04C11DB7));
188
189         // generate the NLP
190         reg [17:0] LinkPulseCount; always @(posedge clk20) LinkPulseCount <= SendingPacket ? 0 : LinkPulseCount+1;
191         reg LinkPulse; always @(posedge clk20) LinkPulse <= &LinkPulseCount[17:1];
192
193         // TP_IDL, shift-register and manchester encoder
194         reg SendingPacketData; always @(posedge clk20) SendingPacketData <= SendingPacket;
195         assign busy = SendingPacketData;
196         reg [2:0] idlecount; always @(posedge clk20) if(SendingPacketData) idlecount<=0; else if(~&idlecount) idlecount<=idlecount+1;
197         wire dataout = CRCflush ? ~CRC[31] : ShiftData[0];
198         reg qo; always @(posedge clk20) qo <= SendingPacketData ? ~dataout^ShiftCount[0] : 1;
199         reg qoe; always @(posedge clk20) qoe <= SendingPacketData | LinkPulse | (idlecount<6);
200         always @(posedge clk20) Ethernet_TDp <= (qoe ? qo : 1'b0);
201         always @(posedge clk20) Ethernet_TDm <= (qoe ? ~qo : 1'b0);
202 endmodule
203
204 module EnetRX(
205         input rxclk,
206         input manchester_data_in,
207         output reg wr,
208         output reg [10:0] oaddr,
209         output reg [7:0] odata,
210         output reg pktrdy = 0,
211         output reg [10:0] olength = 0,
212         input pktclear);
213         
214         reg [2:0] in_data;
215         always @(posedge rxclk) in_data <= {in_data[1:0], manchester_data_in};
216         
217         reg [7:0] data;
218         
219         reg [1:0] cnt;
220         always @(posedge rxclk) if(|cnt || (in_data[2] ^ in_data[1])) cnt<=cnt+1;
221         
222         reg new_bit_avail;
223         always @(posedge rxclk) new_bit_avail <= (cnt==3);
224         always @(posedge rxclk) if(cnt==3) data<={in_data[1],data[7:1]};
225
226         /////////////////////////////////////////////////
227         reg end_of_Ethernet_frame;
228         
229         reg [4:0] sync1;
230         always @(posedge rxclk)
231                 if(end_of_Ethernet_frame)
232                         sync1<=0; 
233                 else if(new_bit_avail) begin
234                         if(!(data==8'h55 || data==8'hAA))       // not preamble?
235                                 sync1 <= 0;
236                         else
237                                 if(~&sync1)             // if all bits of this "sync1" counter are one, we decide that enough of the preamble
238                                                         // has been received, so stop counting and wait for "sync2" to detect the SFD
239                                         sync1 <= sync1 + 1; // otherwise keep counting
240                 end
241         
242         reg [9:0] sync2;
243         always @(posedge rxclk)
244                 if(end_of_Ethernet_frame || pktrdy)     // i.e., if we're busy, drop it on the floor
245                         sync2 <= 0;
246                 else 
247                 if(new_bit_avail) begin
248                         if(|sync2) // if the SFD has already been detected (Ethernet data is coming in)
249                                 sync2 <= sync2 + 1; // then count the bits coming in
250                         else if(&sync1 && data==8'hD5) // otherwise, let's wait for the SFD (0xD5)
251                                 sync2 <= sync2 + 1;
252                 end
253
254         wire new_byte_available = new_bit_avail && (sync2[2:0]==3'h0) && (sync2[9:3]!=0);  
255
256         /////////////////////////////////////////////////
257         // if no clock transistion is detected for some time, that's the end of the Ethernet frame
258
259         reg [2:0] transition_timeout;
260         always @(posedge rxclk) if(in_data[2]^in_data[1]) transition_timeout<=0; else if(~&cnt) transition_timeout<=transition_timeout+1;
261         always @(posedge rxclk) end_of_Ethernet_frame <= &transition_timeout;
262         
263         /////////////////////////////////////////////////
264         always @(posedge rxclk)
265                 if (new_byte_available && !pktrdy) begin
266                         odata <= data;
267                         oaddr <= oaddr + 1;
268                         wr <= 1;
269                 end else if (end_of_Ethernet_frame && (oaddr > 1)) begin
270                         olength <= oaddr;
271                         oaddr <= 0;
272                         wr <= 0;
273                         pktrdy <= 1;
274                 end else if (pktclear) begin
275                         pktrdy <= 0;
276                         wr <= 0;
277                 end else
278                         wr <= 0;
279         
280 endmodule
This page took 0.035282 seconds and 2 git commands to generate.