Forum: Mikrocontroller und Digitale Elektronik LPC2468, Ethernet MAC und Interrupts


von Tobias P. (hubertus)


Lesenswert?

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 :-)

von Achim M. (minifloat)


Lesenswert?

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

von Tobias P. (hubertus)


Lesenswert?

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

von Tobias P. (hubertus)


Lesenswert?

Bin ich der Einzige, der mit solchen Problemen zu kämpfen hat? :o
benutzt sonst keiner die Interrupts sondern polling?

von Simon K. (simon) Benutzerseite


Lesenswert?

Wenn du TCP verwendest kannst du wenigstens die Geschwindigkeit der 
Datenströme regulieren.

Alle anderen Pakete kannst du einfach droppen ;-)

von Tobias P. (hubertus)


Lesenswert?

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.

von Tobias P. (hubertus)


Lesenswert?

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
Noch kein Account? Hier anmelden.