Guten Tag,
ich habe vor einiger Zeit selbst einen SDIO Unterbau für FatFS
programmiert.
Ich würde nun gerne die Schreibgeschwindigkeit etwas erhöhen. Allerdings
läuft mir ab einer SDIO_CLK von >= 8 MHz das Sende-FIFO leer.
(Wenn ich die Funktion mit höchster Optimierung kompiliere geht etwas
mehr; Mit einer Assemblerroutine könnte man auch noch vielleicht etwas
herausholen. Das ist aber alles irgendwie total gepfuscht)
Ansich könnte ich den HW_FLOW_CONTROL des SDIO Moduls nutzen, der die
Clock Stoppt, wenn die FIFOs leer/voll werden. Allerdings funktioniert
dieser Modus nicht korrekt. Die Karte ist danach immer
kaputtgeschrieben. Ein Blick ins Errata-Sheet offenbart:
1
When enabling the HW flow control by setting bit 14 of the SDIO_CLKCR register to ‘1’,
2
glitches can occur on the SDIOCLK output clock resulting in wrong data to be written into
3
the SD/MMC card or into the SDIO device. As a consequence, a CRC error will be reported
4
to the SD/SDIO MMC host interface (DCRCFAIL bit set to ‘1’ in SDIO_STA register).
5
6
Do not use the HW flow control. Overrun errors (Rx mode) and FIFO underrun (Tx mode)
7
should be managed by the application software.
Ich schreibe das SDIO FIFO monentan immer mit 8 vorgepufferten 32-bit
worten voll, sobald dafür genügend Platz verfügbar ist. Das ist nicht
optimal alles über die CPU zu schaufeln. Und genau hier kommt jetzt mein
Problem:
Ich würde sehr sehr gerne den DMA des STMs nutzen um die Daten zu
schreiben, allerdings ist das mit FatFS nicht möglich.
1) Der DMA erfordert, dass die 32 bit Worte auch 32-bit "aligned" sind.
2) wenn im Burst Modus betrieben (wie von ST im Reference manual
beschrieben), darf der Burst keine 1K-Speichergrenze überschreiten, da
ein solcher Zugriff nicht auf dem internen AHB Bus möglich ist.
Beides ist bei der Nutzung von FATFs nicht garantiert.
Die Beispiele, die ich online finde, ignorieren gekonnt dieses Problem
und nutzen einfach den DMA.
Hat sich damit schon jemand beschäftigt? Interpretiere ich das falsch?
Ich würde ungern auf den DMA verzichten, da dieser notwendig ist die
Datenrate zu erreichen.
Hast dir schonmal ChibiOS angesehen? Zumindest nutze ich das mit fatfs,
SDIO und DMA. Allerdings auf nem F103. Wie ich Giovanni aber kenne,
sollte das auf dem F4 auch laufen.
Die HAL von ChibiOS läuft unter Apache 2.0. Kann man also gemütlich in
allen Projekten nutzen.
Nico W. schrieb:> Hast dir schonmal ChibiOS angesehen?
Habe mir gerade die DMA Config angeschaut. Es wird der DMA für
Wort-größe konfiguriert.
Laut ST muss in diesem Fall der Speicher-Puffer Word-aligned sein. Ich
sehe aber auch hier keine Garantie dafür.
Ausschnitt aus dem Reference Manual:
1
When the data width (programmed in the PSIZE or MSIZE bits in the DMA_SxCR register)
2
is a half-word or a word, respectively, the peripheral or memory address written into the
3
DMA_SxPAR or DMA_SxM0AR/M1AR registers has to be aligned on a word or half-word
4
address boundary, respectively.
Dieser Beitrag zeigt ebenfalls eine Config (zum Lesen). Diese schlägt
bei mir jedoch fehl, sobald der Speicherpuffer nicht mehr aligned ist.
Beitrag "Re: STM32 SDIO Blöcke lesen"
Elm chans FatFS kopiert um wenn es nicht Sektoraligned ist was du
schreiben willst.
Ein SD Karten Sektor ist ja meist 512Byte aligned (oder eben mehr).
http://elm-chan.org/fsw/ff/doc/appnote.html
Unter Performance Effective File Access
Ich benutze SDIO auch mit DMA, die Initialisierung stammt aus dem HAL
Code:
https://github.com/JojoS62/COMPONENT_SDIO/blob/master/TARGET_STM/TARGET_STM32F4/sdio_device.c
In dem Repo sind auch Tests, die sind alle positiv durchgelaufen.
Am Anfang hatte ich auch das Polling benutzt, aber ebenfalls Probleme
mit Buffer over/underrun Fehlern. Vor allem wenn der Code mit Debug
Option übersetzt wurde.
In den Tests sind statische Buffer, die werden immer Word aligned sein.
In anderem Code hole ich den Datenbuffer per malloc, da weiss ich nicht
ob der immer word aligned ist, das könnte ich mal mit einer krummen
Adresse testen. Du meinst doch das das ein Problem ist?
Mw E. schrieb:> Elm chans FatFS kopiert um wenn es nicht Sektoraligned ist was du> schreiben willst.> Ein SD Karten Sektor ist ja meist 512Byte aligned (oder eben mehr).
Du verwechselst hier den Sektor der SD karte mit dem Speicher des STMs.
Das Problem ist, dass der Speicherpuffer im Speicher des STMs zumindest
4 byte (word) aligned sein muss. Bei der Nutzung von busts muss er sogar
16 byte aligned sein und darf keine 1k Speichergrenze überschreiten.
In der Doku von FatFs steht (http://elm-chan.org/fsw/ff/doc/dread.html):
1
There are some considerations about the memory addres passed via buff. It is not that always aligned with the word boundary because the argument is defined as BYTE*.
Das ist genau das Problem. Meine Tests zeigen auch, dass das in der
regel gut geht. Ich kann das aber auch so hinbasteln, dass der Puffer
nicht aligned ist und dann knallt es.
M. H. schrieb:> Bei der Nutzung von busts muss er sogar 16 byte aligned sein und darf> keine 1k Speichergrenze überschreiten.
Nur einzelne Bursts dürfen die 1k-Grenzen nicht überschreiten; der ganze
Transfer schon. IIRC ist das automatisch richtig wenn die Adresse 16Byte
aligned ist.
Durch Implementierung eines eigenen FAT32-Treibers mit eigener Puffer
Verwaltung kann man die DMA-Anforderungen erfüllen und hohe
Geschwindigkeiten erreichen, beim Lesen und Schreiben. Das kann dank DMA
sogar komplett parallel zum sonstigen Programmablauf laufen.
Niklas Gürtler schrieb:> Durch Implementierung eines eigenen FAT32-Treibers
Richtig. Nur will ich ungern die Fat Library selber schreiben bzw den
Code von FatFS anfassen. Die einzige Möglichkeit die mir einfällt, wäre
tatsächlich voher zu Prüfen, ob der Puffer aligned ist. Wenn ja, dann
DMA, ansonsten umkopieren oder langsamer über CPU ins SDIO schaufeln.
M. H. schrieb:> Das Problem ist, dass der Speicherpuffer im Speicher des STMs zumindest> 4 byte (word) aligned sein muss. Bei der Nutzung von busts muss er sogar> 16 byte aligned sein und darf keine 1k Speichergrenze überschreiten.
Dann würde ich für die Read/Write Block Funktion einen passenen Block
statisch anlegen und den benutzen wenn der übergebene Datenbereich nicht
passt. Kostet dann halt ein zusätzliches memcpy().
Jim M. schrieb:> Dann würde ich für die Read/Write Block Funktion einen passenen Block> statisch anlegen und den benutzen wenn der übergebene Datenbereich nicht> passt. Kostet dann halt ein zusätzliches memcpy().
Ja. Und den Nachteil, dass Multi-Block operationen nicht gehen bzw. nur
wenn ich den Puffer auf mehrere Blöcke ausweite.
Ich verstehe nur nicht, warum das sämtliche Libraries inklusive von
CubeMX etc mit DMA machen. Entweder funktioniert das dort durch Glück
und ein wenig Magie, oder ich verstehe da etwas nicht richtig und mache
mir umsosnt so nen Kopf. Habe halt gar keine Lust darauf, dass der code
irgendwann versagt, nur weil irgendwo ein Puffer um ein Byte verrutscht.
M. H. schrieb:> Du verwechselst hier den Sektor der SD karte mit dem Speicher des STMs.> Das Problem ist, dass der Speicherpuffer im Speicher des STMs zumindest> 4 byte (word) aligned sein muss. Bei der Nutzung von busts muss er sogar> 16 byte aligned sein und darf keine 1k Speichergrenze überschreiten.
Ne eigentlich verechsle ich da nichts.
Auf die SD Karte kann man nur Blockweise lesen/schreiben.
Wenn man nur einen Teil davon ließt/schreibt muss er eh den ganzen
Sektor lesen und intern zwischenspeichern (dann verändern und
zurückschreiben). DER Speicher muss aligned sein.
Bei der von mir verlinkten Beschreibung las sich das eben so als würden
unalignde Schreibzugriffe kopiert, weils eh in den Sektorbuffer muss.
Daher merkt sich das FS einen Sektor zwischen, das ist ein großes Array
in dem struct.
Das kannste ja mal alignen auf 16 o. 4 byte.
http://elm-chan.org/fsw/ff/doc/sfile.html
"BYTE buf[FF_MAX_SS];" <- da mal ein gcc align attribute drauf werfen
Aber wenn er sich bei disk_read selber widerspricht ist das etwas doof.
M. H. schrieb:> Meine Tests zeigen auch, dass das in der> regel gut geht. Ich kann das aber auch so hinbasteln, dass der Puffer> nicht aligned ist und dann knallt es.
Also wenn man bei f_write mal einen unaligned Pointer reinreicht?
Mw E. schrieb:> Also wenn man bei f_write mal einen unaligned Pointer reinreicht?
Das hängt ein wenig davon ab, wie groß der Puffer ist. Und wie viel
FatFs mit dem Dateisystem sonst noch zu tun hat bei der Operation.
Prinzipiell hast du aber recht. Wenn du aber bspw. genau 512 Byte
schreibst, dann geht der Puffer einfach durch.
Ich werde mich wohl mal durch den Code von FatFs wurschteln und schauen,
ob es mit ein paar align-Attributen getan ist. Die Puffer, die ich in
die Funktionen reingebe, kann ich ja bequem bestimmen.
So siehts wohl aus, wenn der f_write eine Größe von vielfachen der SD
Sektoren bekommt gehts 1zu1 durch. Wenn dann noch der Buffer unaligned
ist krachts.
In der disk_read Beschreibung steht ja auch, dass man dafür sorgen soll,
dass der Buffer dann aligned ist. Das kann durchaus fummlig werden wenn
man in Dateien rumließt und structs füllen will.
Ansonsten noch was, weil ne Frage zum alignment von malloc aufkam:
https://en.cppreference.com/w/c/types/max_align_t
Naja.. ich werde mir das im Laufe der Woche mal anschauen und
herumspielen.
aber ich gehe richtig in der Annahme, dass meine Überlegungen begründet
sind, oder?
Ich war nämlich sehr verwirrt, dass das irgendwie niemanden interessiert
hat bei den Implementierungen, die man so findet.
Mw E. schrieb:> Ansonsten noch was, weil ne Frage zum alignment von malloc aufkam:> https://en.cppreference.com/w/c/types/max_align_t
malloc verwende ich in diesem Projekt nicht. Da ist alles statisch :)
wenn ich ein 'fread(fileBuffer, 1, 8*1024, f);' aufrufe dann sehe ich
das schon beim _read() nur 1 kB Blöcke gelesen und umkopiert werden. Das
muss ja schon die clib machen, ist diese Blockgröße auch konfigurierbar?
Da wird das Alignment Problem eliminiert, beim f_read im Chan Code
werden auch nur 1 k Blöcke in einen fixen Buffer gelesen.