mikrocontroller.net

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


Autor: Tobias Plüss (hubertus)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Joachim K. (minifloat)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Tobias Plüss (hubertus)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Tobias Plüss (hubertus)
Datum:

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

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du TCP verwendest kannst du wenigstens die Geschwindigkeit der 
Datenströme regulieren.

Alle anderen Pakete kannst du einfach droppen ;-)

Autor: Tobias Plüss (hubertus)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include "mac.h"

#include "lpc2468.h"

#include "ucos_ii.h"
#include "os_cpu.h"

#include "packet.h"


static void mac_init_rx_descriptors(void);

static void mac_init_tx_descriptors(void);

static void mac_phy_interrupt(void);

static void mac_interrupt(void);

static void mac_write_phy(INT16U reg, INT16U val);

static INT16U mac_read_phy(INT16U reg);


EMAC_DMA_RAM ALIGN static char rx_buffer[MAC_RX_FRAGMENTS][ETH_FRAG_SIZE];

EMAC_DMA_RAM ALIGN static char tx_buffer[MAC_TX_FRAGMENTS][ETH_FRAG_SIZE];

OS_EVENT *tx_pkt_cnt;

EMAC_DMA_RAM static rx_tx_descriptor rx_desc[MAC_RX_FRAGMENTS];

EMAC_DMA_RAM static tstRxStatusDescriptor rx_status_desc[MAC_RX_FRAGMENTS];

EMAC_DMA_RAM static rx_tx_descriptor tx_desc[MAC_TX_FRAGMENTS];

EMAC_DMA_RAM static INT32U tx_status_desc[MAC_TX_FRAGMENTS];




void mac_init(void)
{
  INT32U dwID;

  tx_pkt_cnt = OSSemCreate(1);

  AHBCFG2 |= (BIT_17 | BIT_12);

  PCONP |= BIT_30; /* power up the mac */
  PINSEL2 |= (BIT_00 | BIT_02 | BIT_08 | /* select the rmii pins */
    BIT_16 | BIT_18 | BIT_20 | BIT_28 | BIT_30);
  PINSEL3 |= (BIT_00 | BIT_02); /* select the mdio and clk pins */
  MAC1 =  (BIT_08 | BIT_09 | BIT_10 | BIT_11 | BIT_14 | BIT_15); /* reset mac */
  Command = (BIT_03 | BIT_04 | BIT_05); /* reset the mac */
  MAC1 = 0; /* deassert the soft resets */
  MAC2 = (BIT_04 | BIT_05); /* enable crc and padding */
  IPGR = IPGR_DEFAULT; /* set the back-to-back inter packet gap */
  CLRT = CLRT_DEFAULT; /* set the collision window and retransmission count */
  MAXF = MAX_FRAME; /* maximum frame length */
  MCFG = (BIT_02 | BIT_04 | BIT_15); /* divide host clk by 20 and reset mii */
  MCFG ^= BIT_15; /* deassert the reset from the mii hardware */
  MCMD = 0;
  Command = (BIT_09 | BIT_06); /* enable the rmii and pass runt frames */
  SUPP = BIT_08; /* enable 100MBit mode */
  mac_write_phy(PHY_BCR, BIT_15); /* reset the phy */
  while(mac_read_phy(PHY_BCR) & (BIT_15 | BIT_11)); /* wait until reset done */
  dwID = (mac_read_phy(PHY_ID1) << 16); /* get PHY manufacturer and revision */
  dwID |= (mac_read_phy(PHY_ID2) & 0xFFF0);
  if(dwID != KSZ8001_ID) /* check if this is a KSZ8001L PHY */
  {
    return; /* abort if it's not */
  }
  mac_write_phy(PHY_ICS, BIT_08 | BIT_10); /* enable link interrupt */
  mac_write_phy(PHY_CTL, BIT_14); /* enable the link and act LEDs */
  mac_write_phy(PHY_BCR, BIT_12 | BIT_09); /* enable & start autonegotiation*/
  PINSEL4 |= BIT_22; /* enable EINT1 interrupt */

  VICVectPriority15 = 0; /* set up the phy interrupt */
  VICVectAddr15 = (INT32U)mac_phy_interrupt;
  VICIntEnable |= BIT_15; /* enable the link up/down interrupt */

  SA0 = (MAC_ADDRESS1 << 8) | MAC_ADDRESS2; /* set up the station address */
  SA1 = (MAC_ADDRESS3 << 8) | MAC_ADDRESS4;
  SA2 = (MAC_ADDRESS5 << 8) | MAC_ADDRESS6;
  RxFilterCtrl = (BIT_02 | BIT_05); /* allow broadcast and perfect */

  mac_init_rx_descriptors(); /* initialize rx descriptor table */
  mac_init_tx_descriptors(); /* initialize tx descriptor table */

  VICVectPriority21 = 0; /* set up the mac interrupt */
  VICVectAddr21 = (INT32U)mac_interrupt;
  VICIntEnable |= BIT_21; /* enable the mac interrupt */
  IntClear = 0xFFFFFFFF;
  IntEnable = BIT_03 | BIT_07;

  return;
}

void mac_tx_enable(void)
{
  Command |= BIT_01;
}

void mac_tx_disable(void)
{
  Command &= ~BIT_01;
}

void mac_rx_enable(void)
{
  Command |= BIT_00;
  MAC1 |=BIT_00;
}

void mac_rx_disable(void)
{
  Command &= ~BIT_00;
  MAC1 &= ~BIT_00;
}

char* mac_new_packet(void)
{
  INT8U err;
  INT16U idx;
  OSSemPend(tx_pkt_cnt, 0, &err); /* wait until memory is available */
  idx = TxProduceIndex;
  return tx_desc[idx].packet;
}


void mac_send_packet(INT16U size)
{
  INT16U idx = TxProduceIndex;
  tx_desc[idx].control = (BIT_31 | BIT_30 | (size - 1));
  idx++;
  if(idx == MAC_TX_FRAGMENTS)
  {
    idx = 0;
  }
  TxProduceIndex = idx;
}


static void mac_init_rx_descriptors(void)
{
  INT16U idx;
  for(idx = 0; idx < MAC_RX_FRAGMENTS; idx++) /* loop for each element */
  {
    rx_desc[idx].packet = &rx_buffer[idx][0]; /* set the buffer pointer */
    rx_desc[idx].control = (BIT_31 | (ETH_FRAG_SIZE - 1)); /* enable rx int */
    rx_status_desc[idx].status = 0; /* zero-out the status and hash fields */
    rx_status_desc[idx].hash = 0;
  }
  RxDescriptor = (INT32U)rx_desc; /* rx descriptor base */
  RxStatus = (INT32U)rx_status_desc; /* status descriptor base */
  RxDescriptorNumber = MAC_RX_FRAGMENTS - 1; /* number of rx descriptors */
  RxConsumeIndex = 0; /* begin at the first rx descriptor */
}


static void mac_init_tx_descriptors(void)
{
  INT16U idx;
  for(idx = 0; idx < MAC_TX_FRAGMENTS; idx++)
  {
    tx_desc[idx].packet = &tx_buffer[idx][0];
    tx_desc[idx].control = 0;
  }
  TxDescriptor = (INT32U)tx_desc;
  TxStatus = (INT32U)tx_status_desc;
  TxDescriptorNumber = MAC_TX_FRAGMENTS - 1; /* number of tx descriptors */
  TxProduceIndex = 0; /* begin at the first tx descriptor */
} /* end mac_init_tx_descriptors */


/* the phy calls this via external interrupt 1 every time a cable is plugged in or out. */
static void mac_phy_interrupt(void)
{
  INT16U interrupt = mac_read_phy(PHY_ICS); /* get the interrupt source */
  if(interrupt & BIT_00)
  {
    INT16U mode = (mac_read_phy(PHY_100BTX) & (BIT_04 | BIT_03 | BIT_02));
    switch(mode)
    {
      case MODE_10M_HALF_DUPLEX:
      {
        MAC2 = (BIT_04 | BIT_05 | BIT_07); /* crc, padding, half duplex */
        SUPP = 0; /* 10MBit mode */
        Command &= ~BIT_10; /* issue half duplex command */
        IPGT = 0x12; /* default setting for 10 MBits, half duplex */
        break;
      }
      case MODE_10M_FULL_DUPLEX:
      {
        MAC2 = (BIT_00 | BIT_04 | BIT_05 | BIT_07); /* crc, padding, full duplex */
        SUPP = 0; /* 10MBit mode */
        Command |= BIT_10; /* issue full-duplex command */
        IPGT = 0x15; /* default setting for 10 MBits, full duplex */
        break;
     }
      case MODE_100M_HALF_DUPLEX:
      {
        MAC2 = (BIT_04 | BIT_05 | BIT_07); /* crc, padding, half duplex */
        SUPP = BIT_08; /* enable 100MBit mode */
        Command &= ~BIT_10; /* issue half-duplex command */
        IPGT = 0x12; /* default setting for 100 MBits, half duplex */
        break;
      }
      case MODE_100M_FULL_DUPLEX:
      {
        MAC2 = (BIT_00 | BIT_04 | BIT_05 | BIT_07); /* crc, padding, full duplex */
        SUPP = BIT_08; /* enable 100MBit mode */
        Command |= BIT_10; /* issue full-duplex command */
        IPGT = 0x15; /* default setting for 100 MBits, full duplex */
        break;
      }
    }
    mac_rx_enable(); /* enable the receiver */
    mac_tx_enable(); /* enable the transmitter */
  }
  else if(interrupt & BIT_02)
  {
    mac_rx_disable(); /* disable the receiver */
    mac_tx_disable(); /* disable the transmitter */
  }
  EXTINT |= BIT_01;
}





static void mac_interrupt(void)
{
  INT32U int_status = IntStatus; /* get the interrupt source */
  IntClear = int_status; /* acknowledge interrupt */
  int_status &= IntEnable; /* mask out unwanted irq's */
  if(int_status)
  {
    if(int_status & BIT_00) /* rx overrun */
    {
      Command |= BIT_05; /* rx reset */
    }
    if(int_status & BIT_01) /* rx error */
    {
    }
    if(int_status & BIT_02) /* rx finished */
    {
    }
    if(int_status & BIT_03) /* rx done */
    {
      
      
      /* HIER STÜRZT DER KRAM AB */
   
      /*INT16U idx = RxConsumeIndex;
      packet_add(rx_desc[idx].packet, (rx_status_desc[idx].status & 0x7FF));
      idx++;
      if(idx == MAC_RX_FRAGMENTS)
      {
        idx = 0;
      }
      RxConsumeIndex = idx;*/
    }
    if(int_status & BIT_04) /* tx underrun */
    {
      Command |= BIT_04; /* tx reset */
    }
    if(int_status & BIT_05) /* tx error */
    {
    }
    if(int_status & BIT_06) /* tx finished */
    {
    }
    if(int_status & BIT_07) /* tx done */
    {
      OSSemPost(tx_pkt_cnt); /* increase the number of available tx buffers */
    }
    if(int_status & BIT_12) /* software interrupt */
    {
    }
    if(int_status & BIT_13) /* wakeup */
    {
    }
  }
}

static void mac_write_phy(INT16U reg, INT16U val)
{
  MADR = (reg & 0x1F); /* the register address. only bits 0..5 are valid. */
  MWTD = val; /* write data */
  while(MIND & BIT_00); /* wait until write cycle is complete */
}

static INT16U mac_read_phy(INT16U reg)
{
  MADR = (reg & 0x1F); /* the register address. only bits 0..5 are valid. */
  MCMD = BIT_00; /* issue a read command */
  while(MIND & BIT_00); /* wait until read operation is complete */
  MCMD = 0;
  return MRDD;
}

So falsch ist das doch nicht, Keil macht das in den Beispielen 
jedenfalls nicht anders.

Autor: Tobias Plüss (hubertus)
Datum:

Bewertung
0 lesenswert
nicht 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?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.