Forum: Mikrocontroller und Digitale Elektronik STM32F7 - DCache & DMA


von Vincent H. (vinci)


Lesenswert?

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_t x[] = {'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_t x[] = {'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

von Carl D. (jcw2)


Lesenswert?

Sicherstellen, daß alle Daten auch geschrieben sind, dafür gibt es 
spezielle Maschinenbefehle.

DMB, DSB sind's beim M3.

von A. B. (Gast)


Lesenswert?


von (prx) A. K. (prx)


Lesenswert?

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.

von Nils P. (torus)


Lesenswert?

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.

von Jim M. (turboj)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Siehe AN4839 von ST.

von Vincent H. (vinci)


Lesenswert?

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:
1
uint8_t x[] = {'a', 'b', 'c', 'd'};
2
SCB_CleanInvalidateDCache_by_Addr(reinterpret_cast<uint32_t*>(&x), sizeof(x));
3
cmd::Serial::transmitDma(&x[0], sizeof(x));

bzw.
1
uint8_t x[] = {'a', 'b', 'c', 'd'};
2
SCB_CleanDCache();
3
cmd::Serial::transmitDma(&x[0], sizeof(x));


Eine reine DSB Instruktion bringt nichts.


/edit
Ja die AN4839 hatte ich auch grad entdeckt :)

von Karl (Gast)


Lesenswert?

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.

von Vincent H. (vinci)


Lesenswert?

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...?

von (prx) A. K. (prx)


Lesenswert?

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.

von Jens D. (jens) Benutzerseite


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

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:
1
SCB_CleanInvalidateDCache_by_Addr(reinterpret_cast<uint32_t*>(x), sizeof(x));
2
cmd::Serial::transmitDma(x, sizeof(x));

von Vincent H. (vinci)


Lesenswert?

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:
>
1
SCB_CleanInvalidateDCache_by_Addr(reinterpret_cast<uint32_t*>(x), 
2
> sizeof(x));
3
> cmd::Serial::transmitDma(x, sizeof(x));


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.