]>
Commit | Line | Data |
---|---|---|
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 |