Grüß euch
Ich hab über die Weihnachtsfeiertage ein neues kleines Hobbyprojekt
angefangen bei dem ich zum ersten Mal mit einem von STs (relativ) neuen
F7 Prozessoren herumspielen darf.
Leider hab ich ein Problem damit den zur Verfügung stehenden Cache
(Daten) voll auszunutzen, wenn ich gleichzeitig DMA Übertragungen
tätigen will. Folgendes funktioniert nämlich schlichtweg nicht:
1
uint8_tx[]={'a','b','c','d'};// hier am Stack
2
cmd::Serial::transmitDma(&x[0],sizeof(x));// Konfiguriert und startet DMA
Das Problem ist, dass x zwar eine Ram-Adresse bekommt, die Daten dort
aber schlichtweg nie ankommen. Wie sich später herausstellte landet x
zur Gänze im Cache! Jetzt dachte ich eigentlich, dass es unter anderem
für genau solche Probleme die dmb/dsb-Instruktion gibt nur führen die
leider nicht zu dem gewünschten Ergenis.
Sprich auch
1
uint8_tx[]={'a','b','c','d'};// hier am Stack
2
__DMB();
3
cmd::Serial::transmitDma(&x[0],sizeof(x));// Konfiguriert und startet DMA
sendet alles andere nur nicht "abcd".
Die einzige Methode die ich ad-hoc gefunden hab ist, den Cache gänzlich
via
1
SCB_DisableDCache();
zu deaktiveren. Aber das kann ja nicht sein?
Wie geht man mit jenem Problem um ohne den DCache ständig auf und
abdrehn zu müssen?
tia
Es gibt bei Prozessoren dieser Komplexität oft mehrere Speicherbereiche
mit unterschiedlichen Eigenschaften hinsichtlich Coherency und Caching.
Da das DTCM RAM wohl nicht gecached wird, treten dort auch keine
Probleme mit dem Cache auf. Barriers/Syncs brauchts trotzdem.
Bei DMA und Caches gilt genrell:
* Wenn per DMA aus dem RAM gelesen wird:
Vor dem Transfer einen Cache Writeback auslösen. Ansonsten liegen die
Daten möglicherweise noch im Cache und nicht im RAM.
* Wenn per DMA in das RAM geschrieben wird:
Vor dem Transfer einen Cache Invalidate auslösen. Ansonsten liest Du
möglicherwise nicht den neuen Inhalt aus dem RAM sondern alten Kram aus
dem Cache. Der Invalidate sollte immer vor dem Transfer sein, ansonsten
kann es passieren das Du gleichzeitig DMA machst, den Inhalt aber mit
altem Zeugs aus deinem Cache überschreibst.
Wenn Du diese beiden Regel beherzigst sollte es funktionieren. Achja,
und Du musst nicht immer den gesammten Cache Invalidieren bzw. Writeback
auslösen. Es reicht den Bereich zu bearbeiten, auf dem DMA läuft.
Vincent H. schrieb:> Das Problem ist, dass x zwar eine Ram-Adresse bekommt, die Daten dort> aber schlichtweg nie ankommen.
Huch, dann wäre aber IMO was kaputt.
Oder packt das Linker Skript den Stack woanders hin als das
Datensegment? Dann müsste man x[] global und/oder static deklarieren, so
das es im Daten Segment landet.
Nils P. schrieb:> Bei DMA und Caches gilt genrell:>> * Wenn per DMA aus dem RAM gelesen wird:>> Vor dem Transfer einen Cache Writeback auslösen. Ansonsten liegen die> Daten möglicherweise noch im Cache und nicht im RAM.>> * Wenn per DMA in das RAM geschrieben wird:>> Vor dem Transfer einen Cache Invalidate auslösen. Ansonsten liest Du> möglicherwise nicht den neuen Inhalt aus dem RAM sondern alten Kram aus> dem Cache. Der Invalidate sollte immer vor dem Transfer sein, ansonsten> kann es passieren das Du gleichzeitig DMA machst, den Inhalt aber mit> altem Zeugs aus deinem Cache überschreibst.>> Wenn Du diese beiden Regel beherzigst sollte es funktionieren. Achja,> und Du musst nicht immer den gesammten Cache Invalidieren bzw. Writeback> auslösen. Es reicht den Bereich zu bearbeiten, auf dem DMA läuft.
Richtige Antwort, Danke!
Benutzt man die CMSIS Funktionen (core_cm7.h), dann funktioniert
folgendes:
A. K. schrieb:> Da das DTCM RAM wohl nicht gecached wird, treten dort auch keine> Probleme mit dem Cache auf. Barriers/Syncs brauchts trotzdem.
Vorher aber gut schauen ob der DMA Controller auf das dtcm zugreifen
kann. War bei den f4 nicht so, bei Cortex m7 scheint aber ein Trend
dahin zu existieren.
Weiter kann die MPU des m7 Speicherbereiche mit verschiedenen Attributen
bzgl. Cachebarkeit versehen. Das wäre explizit.
Und zu guter Letzt gäbe es in cmsis Funktionen um Teile des Caches zu
invalidieren oder ins RAM zu schreiben.
Die Frage ist halt immer ob es lohnt. M7 ist nochmals ein anderes beast
als M4.
Karl schrieb:> Die Frage ist halt immer ob es lohnt. M7 ist nochmals ein anderes beast> als M4.
Die Frage ist vermutlich eher, ob es sich lohnt den DCache überhaupt
aufzudrehen, wenn man einen Großteil seiner Daten via DMA
herumschriebt...?
Vincent H. schrieb:> Die Frage ist vermutlich eher, ob es sich lohnt den DCache überhaupt> aufzudrehen, wenn man einen Großteil seiner Daten via DMA> herumschriebt...?
... oder ob man sich vorher Gedanken darüber macht, welche Daten mit DMA
beharkt werden und welche nicht. Und ob man das trennt und in
verschiedene Speicherbereiche legt.
Immerhin gibts bei den Cortext M7 Cores verschiedene Speicherbereiche
und -interfaces, die u.U. verschiedene Eigenschaften haben, was Caching
angeht. Manchmal gibts auch eine MPU, die ebenfalls dabei mitreden kann.
Ich hatte ein ähnliches Problem aber im Bezug auf das Lesen von Daten
die per DMA geschrieben wurden.
Bei mir hatte es geholfen über die MPU den Cache für den DMA Bereich
abzuschalten.
Vincent H. schrieb:> SCB_CleanInvalidateDCache_by_Addr(reinterpret_cast<uint32_t*>(&x),> sizeof(x));> cmd::Serial::transmitDma(&x[0], sizeof(x));
So ist's sauberer:
Dr. Sommer schrieb:> Vincent H. schrieb:>> SCB_CleanInvalidateDCache_by_Addr(reinterpret_cast<uint32_t*>(&x),>> sizeof(x));>> cmd::Serial::transmitDma(&x[0], sizeof(x));>> So ist's sauberer:>
Ehm, jo...
Ich bin ja generell offen für "cleaner Code" Gschichtln, aber das war
sowieso nur Pseudo-Code zum erläutern des Problems. Ich verwend (fast)
nie built-in Arrays.