Ich quäle mich mit den genannten Komponenten und versuche zu ergründen
warum meine DHCP Discover Pakete nicht gesendet werden.
Es gibt einige Einstellungen für die MPU um die Cache Bereiche richtig
zu konfigurieren. Sehr hilfreich ist ein Dokument in der ST Community,
das hatte ich schonmal im Forum verlinkt, aber die ST Community ist
wegen Umbau gerade down. Da habe ich auch schon einiges von Piranha
gelesen, der den ST Code auch mächtig anprangert, sowohl Cube
generierten als auch andere Beispiele.
Ich habe nun selber einiges probiert und ein eigenes low_level_output
mit Cache Pflege implementiert:
tut es aber nicht weil q->payload nicht 32 Byte aligned ist, es wird
etwas gesendet, aber kein gültiges dhcp.
Aber hurra, es gibt in lwipopts.h die Einstellung für das Alignment in
den pbuf und das wirkt auch wenn ich die auf 32 setze.
Jetzt wird die payload für dhcp zusammengebaut, der p->payload ist 32
Byte aligned. Dann wird UDP header hinzugefügt und der p->payload um die
headersize von 8 Byte zurückgesetzt. Und puff ist mein 32 Byte alignment
kaputt. Das ist doch Mist, warum ist das so? Den lwip Stack möchte ich
nicht verändern.
Als Workaround kann ich den lwip heap non-cacheable machen, aber das ist
ja eher Holzhammermethode.
Wobei ich auch noch nicht weiß ob mein Cache cleaning so gut ist wenn
die Daten aus einem non-cacheable RAM oder ROM kommen.
Hartes Thema für so einen sonnigen Freitag Nachmittag, aber vielleicht
kennt sich ja jemand mit lwIP besser aus.
Du kannst einfach einen größeren Cache-Bereich (also aufgerundet auf
32Bytes = Cache-LineSize) "cleanen", das schadet nicht, ist nur
(minimal) ineffizient.
J. S. schrieb:> Als Workaround kann ich den lwip heap non-cacheable machen, aber das ist> ja eher Holzhammermethode.
Ja und super langsam.
J. S. schrieb:> Wobei ich auch noch nicht weiß ob mein Cache cleaning so gut ist wenn> die Daten aus einem non-cacheable RAM oder ROM kommen.
Das macht nichts, ist dann ein NOP. Aber wie gesagt, langsam.
Beim Empfang das Cache "Invalidate" nicht vergessen :) Da ist das
Alignment aber schon eher ein Problem: Wenn man damit einen benachbarten
Speicherbereich invalidated der zuvor lokal beschrieben wurde, hat man
diese Änderungen gelöscht...
danke für den Input, versuche ich zu verarbeiten.
Sitze aber schon wieder zulange daran und habe mir Phantom Fehler
gebaut...
Irgendwann das ethernetif von einer anderen HW rüberkopiert und die
hatte eine geringfügig aber wichtige andere Belegung des Phy, arghh, und
das obwohl ich schon Cube und VSC für git parallel auf zwei großen
Monitoren habe.
Damit waren viele Versuche durch diesen Fehler fehlgeschlagen. Der
Low_level_output reicht den Fehler von HAL_ETH_Transmit nicht weiter und
wenn, würde er nicht angezeigt.
Schreibe ich mit dem cache clean nicht auch etwas kaputt? Da muss doch
der cache Inhalt ins RAM bevor gesendet wird?
Jetzt bin ich quasi zurück auf Los.
J. S. schrieb:> Schreibe ich mit dem cache clean nicht auch etwas kaputt? Da muss doch> der cache Inhalt ins RAM bevor gesendet wird?
Nur wenn eine andere CPU oder ein DMA zuvor etwas in benachbarte
Bereiche geschrieben hat, was man noch lesen möchte. Das ist wohl etwas
weniger wahrscheinlich. Aber ja, besser wäre es wohl den Bereich "exakt"
auf 32 Bytes aligned zu bekommen.
hmm, warum nicht die gleiche CPU? Wenn der Bereich cachable ist, dann
wird doch solange der cache auch benutzt?
In einer Richtung musste doch sogar vor und nach Senden oder Empfangen
invalidate/clear gemacht werden, so hatte ich es in einem Beitrag von
Piranha gelsesen.
Niklas G. schrieb:> Aber ja, besser wäre es wohl den Bereich "exakt"> auf 32 Bytes aligned zu bekommen.
das MEM_ALIGNMENT in den lwipopts wird auch berücksichtigt, nur wie
geschrieben durch das Header vor dem payload anfügen ist es an der
Stelle wieder krumm. Aber lwip müsste für diesen Bereich ja auch 32 Byte
Platz lassen. Gucke ich mir an, dann könnte man um diesen Bereich auch
den Cachepointer rückwärts korrigieren, so wie du es sowieso
vorgeschlagen hast.
ok, das funktioniert, habe die Codezeilen von Niklas übernommen. Der
grobe Fehler war allerdings die falsche übernommene Hardwarekonfig.
Jetzt wundern mich noch die Ping Antwortzeiten: im Bild ist oben die .70
die immer über 1 ms braucht. Das ist ein Nucleo mit Testsoftware.
MX_LWIP_Process() wird in while(1) aufgerufen, ohne Wartezeit oder
andere Aktionen. Der Unterschied zum anderen Projekt mit der .65 ist das
dort noch der ETH_IRQ aktiviert ist. Aber auch da wird MX_LWIP_Process()
zyklisch aufgerufen wo doch die Bearbeitung stattfindet? Den ETH IRQ
Handler habe ich noch nicht gefunden, macht der die Ping Antwort evtl.
schneller?
Edit:
ok, der Release Build macht es auch schneller, Debug Build ist hier
deutlich langsamer. Trotzdem interessant ob der ETH_IRQ ausgewertet
wird, vermutlich nicht.
Noch ein Edit:
der generierte ethernetif.c Code ruft HAL_ETH_Start() auf, für IRQ
Betrieb müsste es HAL_ETH_Start_IT() sein. Aber dann laufen die
Callbacks im Interruptkontext was schwieriger zu behandeln ist.
Und im Debug Build gebe ich lwip debug messages über einen UART aus, das
kostet natürlich auch Zeit. Das Puzzle wird vollständiger.
jetzt bin ich wieder am ursprünglichen Problem: wenn zwei Controller
gleichzeitig gestartet werden, dann wird in etwa 1 von 20 Fällen kein
dhcp Discover gesendet.
Ich habe den Fehlerfall und dann den Debugger angehangen. Ich sehe das
alles normal läuft, dhcp_start wird zyklisch wiederholt solange keine IP
Addresse empfangen wurde. Es läuft bis zum HAL_ETH_Transmit. Das meldet
HAL_OK nach Ausführung, es wird aber kein Paket gesendet. Im DMACSR
steht 0x404, da ist das TBU Bit gesetzt:
1
Bit 2 TBU: Transmit Buffer Unavailable
2
This bit indicates that the application owns the next descriptor in the Transmit list, and the
3
DMA cannot acquire it. Transmission is suspended. The TPS0 field of the
4
DMA_Debug_Status0 register explains the Transmit Process state transitions.
5
To resume processing the Transmit descriptors, the application should do the following:
6
1. Change the ownership of the descriptor by setting Bit 31 of TDES3.
7
2. Issue a Transmit Poll Demand command.
8
For ring mode, the application should advance the Transmit Descriptor Tail Pointer register of
9
a channel.
Da ist also die TxDescriptor Liste voll und ETH DMA scheint nicht
gestartet zu werden. Was kann da faul sein?
J. S. schrieb:> jetzt bin ich wieder am ursprünglichen Problem: wenn zwei Controller> gleichzeitig gestartet werden, dann wird in etwa 1 von 20 Fällen kein> dhcp Discover gesendet.
Klingt als würden die Pakete kollidieren, CSMA greifen und dann
eventuell ein Timeout auftreten?
Kollisionen sind es sicher nicht. DHCP hat ein retry mit dynamischem
timeout, es wird erneut ein Discover gesendet nach 2, 4, 8, 16, 32 s
und danach alle 60 s. Da müsste irgendwann ein Versuch erfolgreich sein.
Das TBU Bit war auch nicht das Problem. Ich hatte noch einen Zähler
eingebaut, und im erfolgreichen Fall ist das nach dem Senden auch
gesetzt. D.h. das Ethernet Paket wurde schon per DMA gesendet und der
DMA hat das schon wieder freigegeben.
Meine Vermutung ist jetzt das es noch an der cache maintenance liegt:
beim Senden muss nur vorher ein clean ausgeführt werden, beim Empfangen
nach dem Empfang ein invalidate, aber auch da ist vorher ein clean
nötig. Das habe ich noch nicht drin und werde es nachher mal testen. Die
Sendedaten könnten so falsch sein das der Switch die verwirft oder ich
die nicht hinter dem Switch sehe (im Fehlerfall, im ok Fall gehen die
Daten an den Rechner auf dem der DHCP Server und Wireshark laufen).
ich bin etwas weiter, der Cache war nicht mehr das Problem.
Wenn ich das Nucleo-H743ZI2 mit externen 5 V versorge, dann bekomme ich
beim Einschalten auch oft den Fehler das Ethernet nicht reagiert. In dem
Fehlerfall meldet LAN8742_init() einen Fehler -2, Reset Timeout. Dieser
wird aber in der Cube generierten SW nicht abgefragt:
https://github.com/JojoS62/Test-H743ZI2-lwip/blob/79bd3eb6f7d82179384d7a70a9411745cac3ca20/LWIP/Target/ethernetif.c#L252
Das führt dazu das ETH nicht richtig konfiguriert wird, das Auslesen des
Linkstatus liefert -1 und dann läuft Ethernet mit 10 Mbit / half duplex.
Der lwip Stack läuft wie er soll, aber Switch und Phy verstehen sich
nicht.
Im Phy Init wird erst die Phy Adresse ermittelt, das scheint ok zu sein,
ein Fehler beim Registerzugriff würde gemeldet. Wodurch kann der der
Softreset dann fehlerhaft sein? Das Timeout ist 500 ms, ist das lang
genug? Ich hangele mich gerade durch das datasheet vom LAN8742.
Da steht auch das beim Softreset die strapping bits nicht berücksichtigt
werden, d.h. der Link Mode muss vorher selber konfiguriert werden?
Autoneg oder ein fixer Mode werden im init code auch nicht gesetzt.
Ich denke ich habe es gefunden. Kann es sein das der Code von ST nicht
immer perfekt ist oder sehe ich nur den Wald vor lauten Bäumen nicht
mehr?
Das PHY (LAN8742) init geht schief, es wird eine falsche PHY Adresse
ermittelt. Der Code dazu sieht mir sehr strange aus: im SMR steht in
Bits 0:4 die Adresse drin, per Hardware kann nur 0 oder 1 eingestellt
werden. Für andere Adressen muss man mehr Aufwand treiben, macht man
afaik aber nur in Routerandwendungen. Und dann muss der Address Pin von
einem gpio gesteuert werden.
Hier
https://github.com/JojoS62/Test-H743ZI2-lwip/blob/79bd3eb6f7d82179384d7a70a9411745cac3ca20/Drivers/BSP/Components/lan8742/lan8742.c#L101-L126
wird in einer for Schleife eine variable addr gegen die aus dem SMR
verglichen und wenn es nicht passt wird addr inkrementiert.
Kann sowas Sinn machen?
Dann habe ich für den Address strapping pin einen R von 10 k nach GND.
Scheint mir zu hochohmig zu sein und ich vermute daher die Zufälligkeit
meines Fehlers.
Edit:
verstehe den Code jetzt doch. Wenn ein Device eine andere Adresse hat,
dann reagiert es ja nicht auf das Lesen der Register, und so muss
durchprobiert werden.
Im Fehlerfall hat er bei mir Adresse 31 gemeldet, trotzdem komisch.
J. S. schrieb:> Dann habe ich für den Address strapping pin einen R von 10 k nach GND.> Scheint mir zu hochohmig zu sein und ich vermute daher die Zufälligkeit> meines Fehlers.
Redest du von deiner eigenen Schaltungs-Kreation oder von einem
eval-Board? Bei deiner eigenen Kreation würde ich ggf. mal
kritisch auf die Verdrahtung schauen. Mit 25 oder 50 MHz einen
Chip zu steuern kann schon Fallstricke beinhalten. Na gut, kann
bei einem "schlechten" eval-Board auch passieren.
Wastl schrieb:> Redest du von deiner eigenen Schaltungs-Kreation oder von einem> eval-Board?
beides, bzw habe ich die HW bei beiden nicht gemacht: einmal das
Nucleo-H743ZI2 und einmal eine HW von einem ext. Ing. Büro. Den Fehler
konnte ich beim Nucleo auch provozieren bei ext. Versorgung oder Jumper
STLink raus/rein, was auch die MCU neu startet ohne die Resetleitung zu
ziehen.
Die HW sieht auf jeden Fall deutlich besser als die SW. Wenn es läuft
dann läuft das Ding, es sind jetzt 'nur' die Startprobleme.
Mit dem Workaround den Treiber mit Phy Addresse 0 zu betreiben scheint
es jetzt behoben, aber das ist mir noch zu unsicher.
Eine falsch eingelesenes Addressbit müsste dann ja mit Adresse 1 laufen,
und nicht bis 31 hochzählen. Registerlesen sollte auch ok sein, der
return code wird ja geprüft.
J. S. schrieb:> beides, bzw habe ich die HW bei beiden nicht gemacht: einmal das> Nucleo-H743ZI2 und einmal eine HW von einem ext. Ing. Büro.
Ok. Beim geposteten Schaltplan vermisse ich Abblock-Kondensatoren
an VDDIO. Bei solchen Chips mit hochfrequenten externen Taktungen
würde ich eher zwei Kondensatoren spendieren. Da bin ich ein
wenig pingelig ..... immerhin kommen ja aus den Chips dann
100 MHz (?) oder mehr auf das Kabel.
Danke, ja das dürften die Kollegen richtig gemacht haben.
Aber ich denke das faule Ei liegt wieder auch hier bei ST im Nest. Ich
bin noch weiter in die Tiefe und sehe
1
int32_tETH_PHY_IO_Init(void)
2
{
3
/* We assume that MDIO GPIO configuration is already done
4
in the ETH_MspInit() else it should be done here
5
*/
6
7
/* Configure the MDIO Clock */
8
HAL_ETH_SetMDIOClockRange(&heth);
9
10
return0;
11
}
das HAL_ETH_SetMDIOClockRange() ist nicht nötig weil es im ETH_init
schon aufgerufen wird.
Aber ST weiß gar nicht wie schnell seine Controller sind:
Da ist beim MDIO clock bei 200 MHz Schluss, der H743 rennt aber mit 400
MHz bzw. könnte noch 480 MHz.
Die Kommentare im Header zu den Konstanten sehen anders aus
J. S. schrieb:> Danke, ja das dürften die Kollegen richtig gemacht haben.
Das war im original Schaltplan nicht ersichtlich. Hoffentlich
stimmt auch der Ort wo die Kondensatoren platziert wurden.
Man hat ja schon Pferde vor der Apotheke kotzen sehen.
nah am Phy Chip.
Der Teiler für den Takt ist doch richtig, der Takt der da ankommt ist
Sysclock/2. Aber für den Fall mit 480 MHz fehlt dann trotzdem die
nächste Teilerstufe. Bzw. einen Fehler dürfte das mit LAN8742 auch nicht
machen, der kann bis 2,5 MHz MDC und 240 / 102= 2,35 MHz.
Nächster Test ist nochmal das unnötige HAL_ETH_SetMDIOClockRange()
wegzulassen weil es vorher schon gemacht wird. Es steht zwar nicht im
Manual das danach gewartet werden muss, aber in dem Register sind viele
Steuerbits die vielleicht eine Pause brauchen.
Also der H7 ist einfach zu schnell. Die Phy Initialisierung kommt 3 ms
nachdem die 3,3V vorhanden sind. Der Phy braucht aber wohl 23 ms bis er
seine Strapping Bits verarbeitet hat und ist in dieser Zeit nicht
ansprechbar. Das Problem liess sich jetzt einfach durch ein delay vor
dem MX_LWIP_init beheben.
Aber das sauber zu lösen mit CubeMX generierten Code ist schon übel. Das
MX_LWIP_init kann man aus Codegenerierung herausnehmen und selber später
aufrufen, soweit ok.
Der generierte Code ist aber nicht ok bezüglich Cachepflege. Hier muss
Code ausserhalb der User Code Tags geändert werden. Code ändern und lwip
in Cube abschalten ist nicht gut, dann entfernt der Codegenerator lwip
komplett.
Dann gibt es die Möglichkeit eigene Code Templates einzugügen. Das ist
aber spärlich dokumentiert, ein erster Versuch das ethernet_h7.ftl in
die zusätzlichen Templates zu kopieren hat nicht funktioniert. Es wird
eine C Datei erzeugt, aber mit einem Java Callstack einer exception.
Kann man damit die vorhandene ethernet_if.c ersetzen? Und der Murks ist
das die Codegenerierung dann ja von der IDE Installation abhängig ist,
nicht im Projekt gesichert.
Wie macht ihr das (ausser über Cube/HAL zu schimpfen)?
J. S. schrieb:> Wie macht ihr das (ausser über Cube/HAL zu schimpfen)?
Ich ändere das im Library und füge ein #define dort ein, das ich im
User-Code auswerte.
Das funktioniert dann zwar nur mit der aktuellen Library-Version, aber
vermurkst mir nicht gleich den gesamten Code.
Sobald eine neue Library-Version kommt, muß ich diesen Vorgang natürlich
wiederholen, so lange ST das eigentliche Problem nicht behoben hat, aber
das erkenne ich dank des #define dann zumindest sofort.
Das wird dann aber nach Codegenerierung überschrieben. Das wieder
manuell zu ändern ist sehr unprofessionell. Ziel soll auch sein das auf
einem Buildserver zu bauen.
Und neue Versionen oder Änderungen erkennt man besser durch git…
J. S. schrieb:> Das wird dann aber nach Codegenerierung überschrieben.
Nein, eben nicht!
Es gibt bei dir ein Verzeichnis in dem sich die heruntergeladenen
Librarys befinden.
Diese Files werden von CubeMX in dein Projekt kopiert, und wenn du diese
Änderungen dort vornimmst, sind die auch nach erneuter Codegenerierung
weiter vorhanden.
So mach ich das z.B. bei meinem UART-Treiber, wo ich die Handle-Struct
um einen User-Pointer erweitert habe.