ich quäle mich schon den ganzen Tag mit einem Problem mit lvgl Grafik
und einer SD Karte unter Mbed.
Das Programm ist groß, zuviel Code um alles zu zeigen, ich versuche es
zu beschreiben.
Symptom: das mounten der SD-Karte schlägt fehl wenn die Zeile mit dem
Instantiieren des lvgl Treibers im Code ist. Das Lesen des Bootsektors
schlägt fehl und es wird '-22' (kein gültiges Volume) gemeldet, auch
beim Zeilenweisen Ausführen im Debugger.
Wenn ich aber die Zeile mit dem lvgl Treiber danach auskommentiere, dann
funktioniert das Dateisystem und das rootdir wird korrekt ausgegeben.
Da der Treiber noch nicht instantiiert wurde, kann es eigentlich nur an
statischem Code, der durch den Treiber dazugelinkt wird, liegen. Das ist
schon einiges weil ich das BSP zum Disco-F746 verwende. Ich habe aber
bisher nichts gesehen was stören könnte.
Die SD Karte benutzt DMA, der Grafiktreiber optional. Das war meine
erste Vermutung, deshalb habe ich die Grafik auf Pixelweises Ausgeben
geändert, das hatte aber keinen positiven Effekt. Puffer im lvgl Treiber
habe ich kleiner gemacht, statisch oder dynamisch angelegt, NAK.
Speicher hat da Board reichlich, daran sollte es auch nicht liegen. Was
kann so blöde Fehler verursachen?
ich bin etwas weiter, es hat schon etwas in Verbindung mit DMA zu tun.
Auch das Display zeigt garbage an wenn ich per DMA2D vom Renderbuffer
(in internen SRAM1) in den framebuffer (im ext. SDRAM) kopiere. Es
funktioniert korrekt wenn der Renderbuffer im DTCM RAM Bereich liegt.
Genauso bei der Dir Ausgabe, wenn die Grafik nicht dazugelinkt wird,
dann verschieben sich die Daten ins 64 KB DTCM und das Lesen per DMA2
funktioniert. Wenn die Daten im SRAM1 liegen, dann gibt es Fehler und
die Daten sind komplett oder teilweise falsch.
Etwas am Takt falsch konfiguriert oder gibt es noch was bei DMA
Initialsierung?
Hallo,
nicht alle RAM Bereiche sind vom DMA aus ansprechbar, im Handbuch ist
eine Verbindungsmatrix.
Ich hatte ein Problem dass ich vom RAM der CPU nicht mit dem DMA in den
DAC schreiben konnte, bzw. nur const Werte.
Bin unterwegs, ich schicke später die Anleitung für die Umsetzung,
Anpassung der Linkerscripts,... ST hatte mir dabei geholfen.
Grüße, Seppel.
Die Abbildung 1 im RM0385 ? Danach haben DMA2/DMA2D ja Verbindungen zu
allen RAM. Das es Limits gibt hatte ich beim F407, dann ging es aber gar
nicht WIMRE.
Aber jetzt habe ich wenigstens die richtige Suchrichtung. Auf jeden Fall
wird es übel kompliziert wenn RAM nicht gleich RAM ist, beim H7 sieht
das ja noch heftiger aus...
Auch das die Dir Ausgabe teilweise ging war verwirrend, bei Fehlern hat
das Chan-FAT die Dateinamen wie 8.3 abgekürzt:
Ich kenn jetzt deine CPU nicht, aber ein klassiker, der zu solchen
Fehlern führt: Daten per CPU in das SDRAM schreiben. Teile bleiben
dabei im Cache und sind noch nicht im SDRAM angekommen. Ein jetzt
gestarteter DMA liest aus dem SDRAM und beachtet den Cache nicht. So
kommt es zu solchen Effekten.
Musst mal schauen ob Du einen Daten-Cache hast und ggf. einen
Cache-Writeback auf den Quell Speicherbereich loslassen bevor Du den DMA
startest.
ja, Danke, der F7 hat D- und I-Cache.
Habe gerade den D-Cache disabled und neu kompiliert, damit sieht es auch
gut aus, sowohl beim SDMMC als auch DMA2D, jedenfalls so auf den ersten
Blick.
Ist natürlich die Holzhammermethode, ich habe ST Dokumente gesehen wo es
differenziertere Lösungen gibt. Aber das muss man erstmal verstehen.
Das SDIO hatte mit DMA beim F4 wunderbar funktioniert, auf dem F7 erst
auch, aber da hatte ich die Grafik noch nicht mit drin. Ich erzähle
gerade auch immer die ST sind alle gleich, aber einige sind doch
gleicher...
Hallo,
anbei das PDF, ich verwenden unter anderem den STM32H745, bei diesem
trat das Problem auf.
Vielleicht hilft es Dir weiter, hoffe Du lässt uns an der Lösung
teilhaben. :-)
Grüße, Seppel
Danke, das ist so ähnlich. Da funktioniert der DMA aber gar nicht wenn
man den falschen Speicher erwischt. Der F7 hat nicht soviel Auswahl und
der schnellere DTCM mit 64 KB reicht nicht für alles.
Bei mir geht es dann eher darum wie man Cache und DMA vereint.
Hallo,
mit dem ART(DMA2D) kenne ich mich leider nicht aus, wie man das im
Detail nutzt kann ich auf die schnelle nicht nachvollziehen. Vielleicht
kannst Du was dazu schreiben, wäre super.
Laut RM0385 Rev 8 Seite 67 hat der "DMA2D bus" Zugriff auf "SRAM1, SRAM2
and DTCM (through the AHBS bus of Cortex®-M7), external memories through
FMC or Quad SPI, and internal Flash memory."
Laut ReferenceManual (RM0385 Rev 8, Seite 64) des STM32F4 ist das DTCM
und ITCM nicht durch den DMA1 und DMA2 regulär zugreifbar. Was die
gestrichelte Linie die mit "AHBS" gekennzeichnet ist und eine Verbindung
zum DTCM haben soll bedeutet, weiß ich noch nicht genau, scheint
unidirektion zu sein(Pfeil)? Der Cache scheint mir nicht Adressierbar zu
sein, auf Seiter 69 ist das zumindest nicht explizit benannt.
Was mir noch aufgefallen ist, Programming Manual, PM0253 Rev 5 Seite
242, "Initializing and enabling the L1-cache", da scheint es auch einige
Stoplerfallen zu geben.
Grüße, Seppel
Ich hatte beim STM32F429 ein ähnliches Problem, weil ich den SRAM für
den Framebuffer mit viel zu vielen Waitstates initialisiert hatte. Der
DMA2D (und auch 'händisches' beschreiben) ist damit überhaupt nicht
klargekommen und hat die ganze Kiste blockiert. Ich kenne F7xx
allerdings nicht genug, um zu wissen, obs sowas bei dir sein könnte.
ein entscheidender Tipp kam ja von Nils, und mit dem deaktivieren des
Daten Cache sehen die DMA Transfers gut aus. Nur sollte das eleganter
gehen, indem man vor dem Transfer einen flush auslöst. Wie das geht,
habe ich mir noch nicht angesehen. Nachdem ich gestern den ganzen Tag am
Rechner gesessen hatte, musste das Kind heute mal an die frische Luft :)
Seppel schrieb:> mit dem ART(DMA2D) kenne ich mich leider nicht aus, wie man das im> Detail nutzt kann ich auf die schnelle nicht nachvollziehen. Vielleicht> kannst Du was dazu schreiben, wäre super.
Wenn mein Code vorzeigbar ist, dann packe ich den auf github. Ist eben
ein Rumpf mit Mbed-OS, lvgl Grafik, SD Karte über 4 Bit SDIO und
Ethernet läuft auch. Ich wollte eigentlich nur mal eben die Grafik auf
SDC speichern, die einzelnen Komponenten hatte ich ja schon. Das das
Zusammenspiel dann so kompliziert wird hätte ich nicht gedacht.
DMA2D ist wie der 'normale' DMA, kann aber zusätzlich Pixelformate
konvertieren, alpha blending mixen und Offsets für rechteckige
Teilbereiche im Grafikspeicher berücksichtigen. Ich denke da kann man
sich insgesamt lange mit beschäfftigen, Lvgl kann auch den alpha Kanal
benutzen.
Im Board Support Package von dem Board war die Grafik Initialiserung
sowie Zeichenprimitive und DMA für zeilenweises Füllen schon drin. Lvgl
hat einen Buffer in das die Grafik gerendert wird und diesen Bereich
muss der Grafiktreiber dann in den Framebuffer schieben. Der
Renderbuffer muss nur n*Displayzeilenbreite groß sein, damit kann es
auch gut mit den günstigen ILI und co. zusammenspielen. Wenn man DMA
hat, dann kann man zwei Buffer benutzen. Während einer übertragen wird,
kann lvgl schon den nächsten füllen.
Hallo,
ok, den Datencache komplett deaktiviert,... schade dass es nicht
ausgereicht hat den in einen anderen RAM-Bereich auszulagern.
Super dass Du es veröffentlichst, ich freue mich mal eine Blick drauf zu
werfen, "Ich will ja nicht dumm sterben". :-)
Vielen Dank, Grüße, Seppel
Johannes S. schrieb:> Nur sollte das eleganter> gehen, indem man vor dem Transfer einen flush auslöst.
Ich kenne das Problem vom H7xx.
Du kannst per MPU den RAM-Bereich vom Cache aussparen.
Seppel schrieb:> die MPU ist bei mir aus, denn ich brauche die nicht, keine Security,...
Du weißt nur nicht, daß Du die MPU brauchst. Um Sicherheit geht es
überhaupt nicht.
Per MPU sorgt man dafür, daß ein RAM-Bereich nicht per D-Cache
beschleunigt wird. Andere Bereiche schon, was höchste Geschwindigkeit
ermöglicht.
Johannes S. schrieb:> das ARM_MPU_SetRegion ist in der CMSIS definiert, ist das ein Feature> der ARM cores?
So ist es. Die Regionen kann man sich frei aussuchen.
Bei meiner Anwendung verwende ich internes RAM als Bildspeicher und
schreibe die Bilddaten per DMA über FMC (TFT_ADR = 0x60000000) an das
"nackte" Display. Die MPU wird wie folgt initialisiert:
das Ding will immer noch nicht. Habe schon jede Menge Einstellungen
getestet, es wird immer ein gestörtes Bild angezeigt.
Ich habe probiert die Zieladresse im SDRAM als auch die Quelladresse vom
Cache auszuschliessen. Die Adressen passen, wenn ich die Zugriffsrechte
auf 'none' stelle, dann gibts hardfaults. Ausser beim DMA, der darf
trotzdem.
Es ist aber reproduzierbar, wenn xbuf (also die DMA Quelle) im DTCM
liegt, gibt es keine Fehler, liegt es im SRAM1, dann gibt es diesen
Datenmüll.
Mit SRAM1 funktionierte es interessanterweise wenn Ethernet auch
aktiviert war, da habe ich aber gefunden das im EMAC Treiber auch die
MPU konfiguriert wird, aber für einen anderen RAM Bereich. Das ist eine
Unschärfe im OS weil diese globale Resource nicht zentral verwaltet
wird, der EMAC überschreibt eine schon beim Systemstart konfigurierte
Region.
1
/* Disable the MPU */
2
HAL_MPU_Disable();
3
4
#if 1
5
// Select region 7 and use it for framebuffer
6
ARM_MPU_SetRegion(
7
ARM_MPU_RBAR(
8
7,// Region
9
// (LCD_FB_START_ADDRESS)), // Base (32 Byte granularity) ((uint32_t)0xC0000000)
arrrgh... Ethernet hat nicht die MPU umkonfiguriert, das war nur für H7
aktiv. Für F7 wurde der Holzhammer rausgeholt und der D-Cache wieder
komplett abgeschaltet.
Das erklärt das Verhalten und riecht schwer nach Errata.
Noch ein Update:
es gibt doch einen Zauberspruch wenn der D-Cache aktiviert ist:
1
SCB_CleanInvalidateDCache();
vor die DMA Operation und es klappt. Ebenso beim SD Karte lesen.
Will mich aber nicht selber loben, das habe ich beim Stöbern im lvgl
github Repo gefunden, da gab es für das F769 schon einen Treiber. Der
hat auch noch so ein Anti-Tearing drin was ich noch nicht übernommen
habe.
auf einem Discovery STM32F769NI (da hatte ich das gleiche Problem) sieht
das so aus:
https://youtu.be/yxNnvWAxlx4
Johannes S. schrieb:> es gibt doch einen Zauberspruch wenn der D-Cache aktiviert ist:> SCB_CleanInvalidateDCache();> vor die DMA Operation und es klappt. Ebenso beim SD Karte lesen.
Das ist dann soetwas wie ein Abblockkondensator in Software.
Wenn es nicht hilft, gibt es ja noch andere SBC_xxxx ;-)
Johannes S. schrieb:> das ist ja schon blöd das man den ganzen 32 oder 16 kB Cache vor dem DMA> putzen muss.
Ich hätte ja auch eher gedacht, es abschließend zu machen. Aber dazu
müßte man einen Einblick ins Programm haben.
Egal, wichtig ist, daß man bei eigener Programmierung die "Fallen" im
Hinterkopf hat.
m.n. schrieb:> Ich hätte ja auch eher gedacht, es abschließend zu machen.
der Ablauf bei lvgl ist:
- CPU rendert in Buffer, der muss Vielfache von Displayzeile groß sein.
- Buffer wird Pixelweise per CPU oder per DMA in Framebuffer übertragen
Meine Beobachtung war ja schon vorher das es funktioniert hat wenn der
Buffer im DTCM Bereich liegt. Das könnte man auch im Linker erzwingen,
aber dieser Bereich ist bei den Controllern unterschiedlich groß und der
DMA bei der SD Karte hatte das Problem ja auch.
Es sieht also so aus das der Renderbuffer noch (teilweise) im Cache ist
und erst ins SRAM muss bevor er per DMA in den Framebuffer kopiert
werden kann. So wäre meine Erklärung.
Moin,
was ist denn aus dem Versuch geworden, den DMA buffer via MPU uncached
zu konfigurieren?
Das ist doch bei Caches immer das Gleiche. Und man muss nicht immer das
komplette DCache invalidieren oder flushen, dafür gibt es doch auch
SCB_InvalidateDCache_by_Addr() usw. Natürlich sollten die DMA Buffer auf
Cache Lines ausgerichtet sein.
Bei dem Input Buffer für DMA2D könnte man den Cache auf WriteThrough
konfigurieren. Das müsste da schon reichen, da die CPU schreibt und der
DMA nur liest.
Darth Moan schrieb:> was ist denn aus dem Versuch geworden, den DMA buffer via MPU uncached> zu konfigurieren?
habe ich nicht hinbekommen, Beispiel war
Beitrag "Re: komplexes Problem mit Grafik und Dateisystem auf Disco-F746NG"
ich hatte aber viele viele verschiedene Varianten probiert, wurden alle
ignoriert. Wenn du einen Vorschlag hast wie es aussehen müsste, dann
kann ich es testen.
> dafür gibt es doch auch> SCB_InvalidateDCache_by_Addr()
ja, das wäre auch eine sinnvolle Optimierung, kann ich auch noch
probieren.
Moin,
Johannes S. schrieb:> ignoriert. Wenn du einen Vorschlag hast wie es aussehen müsste, dann> kann ich es testen.
Hmm, auf die Schnelle nicht. Da müsste ich das F7 board mal wieder
rauskramen und ein entsperechendes Beispiel konstruieren.
Ich hatte jetzt mal schnell in die AN4838 reingeschaut, da war ein
Beispiel drin, von dem ich gehofft hätte, das es das tut, was es
verspricht. Ob ich das heute noch ausprobiert bekomme, kann ich nicht
versprechen.
Internal SRAM Normal memory 0x2000 0000 Region0 8 Kbytes Shareable,
write through, no write allocate
C=1, B = 0, TEX = 0, S=1
SRD = 0, XN= 0, AP = full access
Flash memory Normal memory 0x0800 0000 Region1 1 Mbyte Non-shareable
write through, no write allocate
C=1, B = 0, TEX = 0, S=0
SRD = 0, XN= 0, AP = full access
FMC Normal memory 0x6000 0000 Region2 512 Mbytes Shareable, write
through, no write allocate
C=1, B = 0, TEX = 0, S=1
SRD = 0, XN= 0, AP = full access
1
voidMPU_RegionConfig(void)
2
{
3
MPU_Region_InitTypeDefMPU_InitStruct;
4
/* Disable MPU */
5
HAL_MPU_Disable();
6
/* Configure RAM region as Region N°0, 8kB of size and R/W region */
es gibt noch eine AN4839, da geht es auch direkt um den F7/H7 L1 Cache.
das ist ja mal gut erklärt, das setzen auf WT für den Renderbuffer
sollte eigentlich die Lösung sein, ich probiere es noch mal.
edit2:
wobei das WB für den Renderbuffer und das abschliessende Invalidieren
des Caches (für diesen Bereich) ja nicht per se schlecht sein muss. Wenn
das Rendern die gleichen Pixel mehrfach überschreibt, dann kann das ja
wieder ein Vorteil sein. Da ist die Frage wie lange das Cache putzen
dauert.
So oder so, für einen µC sind diese F7/H7 schon rattenschnell.
Darth Moan schrieb:> Internal SRAM Normal memory 0x2000 0000 Region0 8 Kbytes Shareable,> write through, no write allocate> C=1, B = 0, TEX = 0, S=1> SRD = 0, XN= 0, AP = full access
mit diesen Einstellungen hat es jetzt funktioniert. Mein Fehler war
noch, das der Buffer nicht an der Region Size ausgerichtet war. Ich
hatte angenommen das ein 32 Byte Alignment (Cache Line Size) reichen
würde, aber dem ist scheinbar nicht so. Ein 64 kB Buffer muss auch auf
einer geraden 64 k Adresse liegen, was bei so großen Bereichen dann auch
das Zusammenspiel mit dem Linker nötig macht.
1
{
2
/* Disable the MPU */
3
HAL_MPU_Disable();
4
5
// lv_color_t* xbuf2 = (lv_color_t*)0x20060000; // a buffer for 20*800 rows * 4 Byte
Moin,
es freut mich das zu hören/lesen. Dann ist das also kein Fall für das
Erata.
Dass die Regions immer an ihrer natürlichen Grenze liegen müssen, kenne
ich so von der MMU auf einer anderen Plattform. Die MMU HW benutzte die
SIZE Information dazu, die zu vergleichenden Adress Bits zu bestimmen.
Damit fängt eine Region immer an einem Vielfachen ihrer Grösse an.
Das man das auch anders in HW giessen könnte, wäre mir gar nicht in den
Sinn gekommen. Aber wenn man jetzt danach sucht, wird es bestimmt
irgendwo eine HW geben, die abweichende Grenzen erlaubt. Ich kenne ja
nur ein paar Plattformen.
Johannes S. schrieb:> Da ist die Frage wie lange das Cache putzen dauert.
Vielleicht hast Du noch einmal "Luft", um das grob zu ermitteln.
> So oder so, für einen µC sind diese F7/H7 schon rattenschnell.
So ist es, und wenn man sich dafür auch mal mit aktiven Caches und deren
"Risiken und Nebenwirkungen" beschäftigen muß, schadet es bestimmt
nicht.
An anderer Stelle erwähnst Du, bei umfangreicherer Grafik Cortex-A ins
Auge zu fassen. Ich sehe die H7 als Eierlegendewollmilchsau, deren
Preis/Leistungs-Verhältnis auch dann noch stimmt, wenn man nur 5-10% der
Peripherie nutzt. Die Grafik-Geschichten sind nur ein kleiner Teil, der
für Mess- und Steuerungsaufgaben völlig ausreichend ist. Meine Meinung.
m.n. schrieb:>> Da ist die Frage wie lange das Cache putzen dauert.>> Vielleicht hast Du noch einmal "Luft", um das grob zu ermitteln.
auf dem F769 Disco, das Display hat 800 * 480 mit 32 BPP (16 BPP RGB565
ginge auch):
DMA Transfer 64000 Byte : ~350 µs (mit CleanDCache)
4
DMA Transfer 64000 Byte : ~330 µs (ohne Clean, write through SRAM)
Der Renderbuffer ist jetzt 20 Zeilen groß, ein kompletter Screen ist
damit in 480 / 20 = 24 Transfers gefüllt, sollte also < 10 ms dauern.
Das Clean by_Addr braucht bei großen Datenbereichen länger, das sieht
man auch im Code das es mehr Schleifendurchläufe hat. Da das write
through jetzt funktioniert ist das clean aber nicht mehr nötig.
Unschön ist nur das man das Linkerscript anpacken muss um den Buffer
ordentlich zu plazieren. Die 2*64 kB Buffer sind natürlich auch
großzügig spendiert, das will man sicher nicht immer so haben.
> An anderer Stelle erwähnst Du, bei umfangreicherer Grafik Cortex-A ins> Auge zu fassen. Ich sehe die H7 als Eierlegendewollmilchsau,
mit den Cortex-A habe ich bisher nichts gemacht, ausser Raspberrys zu
nutzen. Die CM7 sind schon genial, nur so Boards mit HyperRAM und -Flash
im BGA baue ich auch nicht mal eben. Bei Ali gibt es aber mittlerweile
jede Menge schöner Displays wo man mehr als etwas SPI braucht, ich finde
es gut sowas auch nutzen zu können.
ich habe das nochmal mit dem buffer im DTCM RAM gemessen, ich hätte
erwartet das es mit seinen 0 Waitstates ja noch schneller ist.
Uberraschend war aber das der DMA Transfer jetzt deutlich länger
gebraucht hat, 550 µs gegenüber den 330 µs aus dem SRAM1. Das verstehe
ich nicht ganz, scheint aber an den verschiedenen Bussen zu liegen.
Insgesamt ist ein einfacher lvgl Benchmark, der den Aufbau mit ein paar
Buttons auf einem Screen mißt, aber mit Buffer im DTDM deutlich
schneller als mit Buffer im SRAM1: 15 zu 22 ms.
Ich habe die Tests mehrmals wiederholt und bin mir auch sicher die nicht
vertauscht zu haben, da sind jeweils debug Ausgaben drin.
Den Treibencode mit den Testsänderungen habe ich angehängt, ist durch
die Test #ifdefs etwas unübersichtlich. Das Linkerscript muss ich für
den Wechsel DTCM/SRAM1 auch jeweils umschalten.
Für den lvgl Benchmark habe ich die Ausgaben auskommentiert, da wären
sonst die Zeiten für die Ausgabe auf die serielle mit drin.