Hallo Leute, ich experimentiere immer noch mit dem Ethernet MAC des 2468 herum. Ich habe ein eigenes, einfaches Protokoll erfunden, über das ich mit meinem LPC kommunizieren will. Die passende Windoof-Software dazu habe ich schon erfunden, nun gehts an den LPC. Hier setze ich uCOS ein. Die ankommenden Ethernet-Pakete möchte ich in einer Queue ablegen, sodass ich mittels OSQPend auf das Eintreffen von Paketen warten kann. Dazu habe ich mir folgendes überlegt: Wenn ein Paket eintrifft, soll ein Interrupt aufgerufen werden, der mittels OSMemGet einen Buffer alloziiert, und das gerade eingetroffene Paket in diesen Buffer kopiert. Dann wird mittels OSQPost die Basisadresse des gerade aloziierten Buffers auf die Queue gelegt und der RxConsumeIndex um eins erhöht. Der Task, der die eintreffenden Pakete verarbeiten soll, wartet mittels OSQPend, bis etwas in der Queue ist, und verarbeitet es. Anschliessend wird der aloziierte Buffer mit OSMemPut wieder freigegeben. In der Theorie funktioniert das bestens, nur in der Praxis leider nicht so ganz, denn: Beim Aufruf von OSMemGet stürzt der LPC ab - genauer gesagt geht er in den Prefetch Abort. Warum? Das einzige, was in diesem dummen Interrupt nicht funktioniert, ist OSMemGet. Alles andere würde klappen und zeitlich sogar reichen bis zum Eintreffen des nächsten Frames, aber OSMemGet klappt nicht. Habt ihr vielleicht noch eine andere Idee, wie man das lösen könnte? Ich muss allerdings noch erwähnen, dass in dem Interrupt nicht nur das eingetroffene Paket in die Queue gelegt wird. Vielmehr wird eine spezielle Struktur angelegt, welche noch weitere Infos zu dem Paket enthält - z.B. die Grösse. Wäre froh um den einen oder anderen Tip :-)
Mach dir doch von vornherein einen Puffer mit [soundsoviel] Bytes. Dann ein Zeigerarray, was auf die Anfänge der einzelnen Pakete(Rohdaten) innerhalb des erstgenannten Puffer-Arrays zeigt. Und zwei Zeiger aufs Zeigerarray, der eine auf das erste zu verarbeitende Paket und der zweite auf das zuletzt empfangene Paket. So entsteht ein Ringpuffer mit variabler Paket-Größe. Das mal als dreckiger Workaround. Wie schnell trudeln denn die einzelnen Pakete ein? Schneller als sie verarbeitet werden können? Kannst du das anständig debuggen, notfalls mit einem Logicanalyzer an ein paar Portpins? mfg mf
Hallo, die Pakete trudeln natürlich im ungünstigsten Fall schneller ein, als sie verarbeitet werden können. Deshalb muss ja dieser Handstand mit der Queue gemacht werden :( Das wär so ne elegante Lösung gewesen mit diesem dynamischen Memory! lwip macht das ja auch so ähnlich. Debuggen kann ich zwar recht ordentlich, da ich einen J-Link von Segger besitze, aber einen LA habe ich nicht. Mich würde aber noch interessieren, warum OSMemGet gerade beim Rx-Interrupt NICHT funktioniert. Im uCOS-Buch heisst es explizit, dass man diese Funktion in ISRs verwenden darf. Testweise habe ich sie mal aus einem Timer-Interrupt heraus aufgerufen, und dort funktioniert es :O
Bin ich der Einzige, der mit solchen Problemen zu kämpfen hat? :o benutzt sonst keiner die Interrupts sondern polling?
Wenn du TCP verwendest kannst du wenigstens die Geschwindigkeit der Datenströme regulieren. Alle anderen Pakete kannst du einfach droppen ;-)
Schon; aber das nützt alles nichts, wenn der LPC sporadisch abstürzt wenn ein Paket eintrifft. Ich habe mittlerweile festgestellt: greift man auf den Speicherbereich, wo die ankommenden Pakete gespeichert werden, zu, während ein neues Paket eintrifft, dann tritt wohl irgend ein Zugriffsfehler auf und der LPC verabschiedet sich in den data abort. Ich kann dazu im Datenblatt aber nichts finden! Ich habe auch schon mit den AHBCFG1 und AHBCFG2 Registern herumgespielt, ohne Ergebnis. Meine Descriptoren liegen im EMAC-RAM (0x7FE00000), sowie die Paket Buffer auch. Jeder Buffer umfasst 1536 Bytes, sodass garantiert ist, dass immer ein ganzer Frame in einen Descriptor rein passt. Was mache ich falsch, dass dieser dumme Prozessor abstürzt? Es ist zum Haare ausraufen. Egal was ich versuche - nach ein, zwei Paketen gibt das Ding den Geist auf. Und ich habe noch nicht mal irgend ein Protokoll implementiert, nur schon der blosse Empfang der Pakete klappt nicht richtig. Hier ist mein Code:
1 | #include "mac.h" |
2 | |
3 | #include "lpc2468.h" |
4 | |
5 | #include "ucos_ii.h" |
6 | #include "os_cpu.h" |
7 | |
8 | #include "packet.h" |
9 | |
10 | |
11 | static void mac_init_rx_descriptors(void); |
12 | |
13 | static void mac_init_tx_descriptors(void); |
14 | |
15 | static void mac_phy_interrupt(void); |
16 | |
17 | static void mac_interrupt(void); |
18 | |
19 | static void mac_write_phy(INT16U reg, INT16U val); |
20 | |
21 | static INT16U mac_read_phy(INT16U reg); |
22 | |
23 | |
24 | EMAC_DMA_RAM ALIGN static char rx_buffer[MAC_RX_FRAGMENTS][ETH_FRAG_SIZE]; |
25 | |
26 | EMAC_DMA_RAM ALIGN static char tx_buffer[MAC_TX_FRAGMENTS][ETH_FRAG_SIZE]; |
27 | |
28 | OS_EVENT *tx_pkt_cnt; |
29 | |
30 | EMAC_DMA_RAM static rx_tx_descriptor rx_desc[MAC_RX_FRAGMENTS]; |
31 | |
32 | EMAC_DMA_RAM static tstRxStatusDescriptor rx_status_desc[MAC_RX_FRAGMENTS]; |
33 | |
34 | EMAC_DMA_RAM static rx_tx_descriptor tx_desc[MAC_TX_FRAGMENTS]; |
35 | |
36 | EMAC_DMA_RAM static INT32U tx_status_desc[MAC_TX_FRAGMENTS]; |
37 | |
38 | |
39 | |
40 | |
41 | void mac_init(void) |
42 | {
|
43 | INT32U dwID; |
44 | |
45 | tx_pkt_cnt = OSSemCreate(1); |
46 | |
47 | AHBCFG2 |= (BIT_17 | BIT_12); |
48 | |
49 | PCONP |= BIT_30; /* power up the mac */ |
50 | PINSEL2 |= (BIT_00 | BIT_02 | BIT_08 | /* select the rmii pins */ |
51 | BIT_16 | BIT_18 | BIT_20 | BIT_28 | BIT_30); |
52 | PINSEL3 |= (BIT_00 | BIT_02); /* select the mdio and clk pins */ |
53 | MAC1 = (BIT_08 | BIT_09 | BIT_10 | BIT_11 | BIT_14 | BIT_15); /* reset mac */ |
54 | Command = (BIT_03 | BIT_04 | BIT_05); /* reset the mac */ |
55 | MAC1 = 0; /* deassert the soft resets */ |
56 | MAC2 = (BIT_04 | BIT_05); /* enable crc and padding */ |
57 | IPGR = IPGR_DEFAULT; /* set the back-to-back inter packet gap */ |
58 | CLRT = CLRT_DEFAULT; /* set the collision window and retransmission count */ |
59 | MAXF = MAX_FRAME; /* maximum frame length */ |
60 | MCFG = (BIT_02 | BIT_04 | BIT_15); /* divide host clk by 20 and reset mii */ |
61 | MCFG ^= BIT_15; /* deassert the reset from the mii hardware */ |
62 | MCMD = 0; |
63 | Command = (BIT_09 | BIT_06); /* enable the rmii and pass runt frames */ |
64 | SUPP = BIT_08; /* enable 100MBit mode */ |
65 | mac_write_phy(PHY_BCR, BIT_15); /* reset the phy */ |
66 | while(mac_read_phy(PHY_BCR) & (BIT_15 | BIT_11)); /* wait until reset done */ |
67 | dwID = (mac_read_phy(PHY_ID1) << 16); /* get PHY manufacturer and revision */ |
68 | dwID |= (mac_read_phy(PHY_ID2) & 0xFFF0); |
69 | if(dwID != KSZ8001_ID) /* check if this is a KSZ8001L PHY */ |
70 | {
|
71 | return; /* abort if it's not */ |
72 | }
|
73 | mac_write_phy(PHY_ICS, BIT_08 | BIT_10); /* enable link interrupt */ |
74 | mac_write_phy(PHY_CTL, BIT_14); /* enable the link and act LEDs */ |
75 | mac_write_phy(PHY_BCR, BIT_12 | BIT_09); /* enable & start autonegotiation*/ |
76 | PINSEL4 |= BIT_22; /* enable EINT1 interrupt */ |
77 | |
78 | VICVectPriority15 = 0; /* set up the phy interrupt */ |
79 | VICVectAddr15 = (INT32U)mac_phy_interrupt; |
80 | VICIntEnable |= BIT_15; /* enable the link up/down interrupt */ |
81 | |
82 | SA0 = (MAC_ADDRESS1 << 8) | MAC_ADDRESS2; /* set up the station address */ |
83 | SA1 = (MAC_ADDRESS3 << 8) | MAC_ADDRESS4; |
84 | SA2 = (MAC_ADDRESS5 << 8) | MAC_ADDRESS6; |
85 | RxFilterCtrl = (BIT_02 | BIT_05); /* allow broadcast and perfect */ |
86 | |
87 | mac_init_rx_descriptors(); /* initialize rx descriptor table */ |
88 | mac_init_tx_descriptors(); /* initialize tx descriptor table */ |
89 | |
90 | VICVectPriority21 = 0; /* set up the mac interrupt */ |
91 | VICVectAddr21 = (INT32U)mac_interrupt; |
92 | VICIntEnable |= BIT_21; /* enable the mac interrupt */ |
93 | IntClear = 0xFFFFFFFF; |
94 | IntEnable = BIT_03 | BIT_07; |
95 | |
96 | return; |
97 | }
|
98 | |
99 | void mac_tx_enable(void) |
100 | {
|
101 | Command |= BIT_01; |
102 | }
|
103 | |
104 | void mac_tx_disable(void) |
105 | {
|
106 | Command &= ~BIT_01; |
107 | }
|
108 | |
109 | void mac_rx_enable(void) |
110 | {
|
111 | Command |= BIT_00; |
112 | MAC1 |=BIT_00; |
113 | }
|
114 | |
115 | void mac_rx_disable(void) |
116 | {
|
117 | Command &= ~BIT_00; |
118 | MAC1 &= ~BIT_00; |
119 | }
|
120 | |
121 | char* mac_new_packet(void) |
122 | {
|
123 | INT8U err; |
124 | INT16U idx; |
125 | OSSemPend(tx_pkt_cnt, 0, &err); /* wait until memory is available */ |
126 | idx = TxProduceIndex; |
127 | return tx_desc[idx].packet; |
128 | }
|
129 | |
130 | |
131 | void mac_send_packet(INT16U size) |
132 | {
|
133 | INT16U idx = TxProduceIndex; |
134 | tx_desc[idx].control = (BIT_31 | BIT_30 | (size - 1)); |
135 | idx++; |
136 | if(idx == MAC_TX_FRAGMENTS) |
137 | {
|
138 | idx = 0; |
139 | }
|
140 | TxProduceIndex = idx; |
141 | }
|
142 | |
143 | |
144 | static void mac_init_rx_descriptors(void) |
145 | {
|
146 | INT16U idx; |
147 | for(idx = 0; idx < MAC_RX_FRAGMENTS; idx++) /* loop for each element */ |
148 | {
|
149 | rx_desc[idx].packet = &rx_buffer[idx][0]; /* set the buffer pointer */ |
150 | rx_desc[idx].control = (BIT_31 | (ETH_FRAG_SIZE - 1)); /* enable rx int */ |
151 | rx_status_desc[idx].status = 0; /* zero-out the status and hash fields */ |
152 | rx_status_desc[idx].hash = 0; |
153 | }
|
154 | RxDescriptor = (INT32U)rx_desc; /* rx descriptor base */ |
155 | RxStatus = (INT32U)rx_status_desc; /* status descriptor base */ |
156 | RxDescriptorNumber = MAC_RX_FRAGMENTS - 1; /* number of rx descriptors */ |
157 | RxConsumeIndex = 0; /* begin at the first rx descriptor */ |
158 | }
|
159 | |
160 | |
161 | static void mac_init_tx_descriptors(void) |
162 | {
|
163 | INT16U idx; |
164 | for(idx = 0; idx < MAC_TX_FRAGMENTS; idx++) |
165 | {
|
166 | tx_desc[idx].packet = &tx_buffer[idx][0]; |
167 | tx_desc[idx].control = 0; |
168 | }
|
169 | TxDescriptor = (INT32U)tx_desc; |
170 | TxStatus = (INT32U)tx_status_desc; |
171 | TxDescriptorNumber = MAC_TX_FRAGMENTS - 1; /* number of tx descriptors */ |
172 | TxProduceIndex = 0; /* begin at the first tx descriptor */ |
173 | } /* end mac_init_tx_descriptors */ |
174 | |
175 | |
176 | /* the phy calls this via external interrupt 1 every time a cable is plugged in or out. */
|
177 | static void mac_phy_interrupt(void) |
178 | {
|
179 | INT16U interrupt = mac_read_phy(PHY_ICS); /* get the interrupt source */ |
180 | if(interrupt & BIT_00) |
181 | {
|
182 | INT16U mode = (mac_read_phy(PHY_100BTX) & (BIT_04 | BIT_03 | BIT_02)); |
183 | switch(mode) |
184 | {
|
185 | case MODE_10M_HALF_DUPLEX: |
186 | {
|
187 | MAC2 = (BIT_04 | BIT_05 | BIT_07); /* crc, padding, half duplex */ |
188 | SUPP = 0; /* 10MBit mode */ |
189 | Command &= ~BIT_10; /* issue half duplex command */ |
190 | IPGT = 0x12; /* default setting for 10 MBits, half duplex */ |
191 | break; |
192 | }
|
193 | case MODE_10M_FULL_DUPLEX: |
194 | {
|
195 | MAC2 = (BIT_00 | BIT_04 | BIT_05 | BIT_07); /* crc, padding, full duplex */ |
196 | SUPP = 0; /* 10MBit mode */ |
197 | Command |= BIT_10; /* issue full-duplex command */ |
198 | IPGT = 0x15; /* default setting for 10 MBits, full duplex */ |
199 | break; |
200 | }
|
201 | case MODE_100M_HALF_DUPLEX: |
202 | {
|
203 | MAC2 = (BIT_04 | BIT_05 | BIT_07); /* crc, padding, half duplex */ |
204 | SUPP = BIT_08; /* enable 100MBit mode */ |
205 | Command &= ~BIT_10; /* issue half-duplex command */ |
206 | IPGT = 0x12; /* default setting for 100 MBits, half duplex */ |
207 | break; |
208 | }
|
209 | case MODE_100M_FULL_DUPLEX: |
210 | {
|
211 | MAC2 = (BIT_00 | BIT_04 | BIT_05 | BIT_07); /* crc, padding, full duplex */ |
212 | SUPP = BIT_08; /* enable 100MBit mode */ |
213 | Command |= BIT_10; /* issue full-duplex command */ |
214 | IPGT = 0x15; /* default setting for 100 MBits, full duplex */ |
215 | break; |
216 | }
|
217 | }
|
218 | mac_rx_enable(); /* enable the receiver */ |
219 | mac_tx_enable(); /* enable the transmitter */ |
220 | }
|
221 | else if(interrupt & BIT_02) |
222 | {
|
223 | mac_rx_disable(); /* disable the receiver */ |
224 | mac_tx_disable(); /* disable the transmitter */ |
225 | }
|
226 | EXTINT |= BIT_01; |
227 | }
|
228 | |
229 | |
230 | |
231 | |
232 | |
233 | static void mac_interrupt(void) |
234 | {
|
235 | INT32U int_status = IntStatus; /* get the interrupt source */ |
236 | IntClear = int_status; /* acknowledge interrupt */ |
237 | int_status &= IntEnable; /* mask out unwanted irq's */ |
238 | if(int_status) |
239 | {
|
240 | if(int_status & BIT_00) /* rx overrun */ |
241 | {
|
242 | Command |= BIT_05; /* rx reset */ |
243 | }
|
244 | if(int_status & BIT_01) /* rx error */ |
245 | {
|
246 | }
|
247 | if(int_status & BIT_02) /* rx finished */ |
248 | {
|
249 | }
|
250 | if(int_status & BIT_03) /* rx done */ |
251 | {
|
252 | |
253 | |
254 | /* HIER STÜRZT DER KRAM AB */
|
255 | |
256 | /*INT16U idx = RxConsumeIndex;
|
257 | packet_add(rx_desc[idx].packet, (rx_status_desc[idx].status & 0x7FF));
|
258 | idx++;
|
259 | if(idx == MAC_RX_FRAGMENTS)
|
260 | {
|
261 | idx = 0;
|
262 | }
|
263 | RxConsumeIndex = idx;*/
|
264 | }
|
265 | if(int_status & BIT_04) /* tx underrun */ |
266 | {
|
267 | Command |= BIT_04; /* tx reset */ |
268 | }
|
269 | if(int_status & BIT_05) /* tx error */ |
270 | {
|
271 | }
|
272 | if(int_status & BIT_06) /* tx finished */ |
273 | {
|
274 | }
|
275 | if(int_status & BIT_07) /* tx done */ |
276 | {
|
277 | OSSemPost(tx_pkt_cnt); /* increase the number of available tx buffers */ |
278 | }
|
279 | if(int_status & BIT_12) /* software interrupt */ |
280 | {
|
281 | }
|
282 | if(int_status & BIT_13) /* wakeup */ |
283 | {
|
284 | }
|
285 | }
|
286 | }
|
287 | |
288 | static void mac_write_phy(INT16U reg, INT16U val) |
289 | {
|
290 | MADR = (reg & 0x1F); /* the register address. only bits 0..5 are valid. */ |
291 | MWTD = val; /* write data */ |
292 | while(MIND & BIT_00); /* wait until write cycle is complete */ |
293 | }
|
294 | |
295 | static INT16U mac_read_phy(INT16U reg) |
296 | {
|
297 | MADR = (reg & 0x1F); /* the register address. only bits 0..5 are valid. */ |
298 | MCMD = BIT_00; /* issue a read command */ |
299 | while(MIND & BIT_00); /* wait until read operation is complete */ |
300 | MCMD = 0; |
301 | return MRDD; |
302 | }
|
So falsch ist das doch nicht, Keil macht das in den Beispielen jedenfalls nicht anders.
Hallo, nun sind einige Monate vergangen, und ich hab den Kram mit dem MAC für eine Weile ruhen lassen. Nun möchte ich doch endlich wieder mal was damit machen. Und stehe natürlich immernoch vor diesem Problem! Zwar habe ich zwischenzeitlich meinen Code um einiges verbessert. Er läuft jetzt auch stabiler: treffen die Pakete in einigem Abstand voneinander ein, also wenn ich z.B. von einem einzelnen PC aus ein Ping versende, dann klappt alles bestens. Wenn aber zwei oder mehr Pakete in sehr kurzer zeitlicher Abfolge eintreffen, dann stürzt der Rechner ab. Mittlerweile glaube ich auch, herausgefunden zu haben, warum: in der RxDescriptor-Tabelle steht überall nur noch 0xFFFFFFFF drin! Aber warum? wer schreibt mir da rein? Eine Analyse mit Lint zeigte, dass zumindest der Code für meinen MAC-Driver in Ordnung ist. Könnte es evtl. daran liegen, dass der DMA-Bus, der die Daten vom PHY ins RAM schaufeln muss, langsamer ist, als die Pakete eintreffen? Interessant ist: Schalte ich den RxDone-Interrupt aus, dann stürzt der Rechner nie ab. Ich kann 10000 Pakete direkt nacheinander senden oder von beliebig vielen PCs aus, das macht keinen Unterschied. Hat in der Zwischenzeit vielleicht jemand ein ähnliches Verhalten beobachten können oder hat eine Idee?
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.