Hallo zusammen,
ich versuche in meinem Programm Daten in ein Array zu kopieren. Das löst
jedoch einen "Hard Fault" aus und die CPU bleibt stehen.
Ich verwende einen STM32F030R8 und programmiere mit Truestudio.
Mein Code sieht wie folgt aus:
Das Alignment stimmt ziemlich sicher nicht. Cortex m können auf 32 Bit
Variablen nur an Adressen zugreifen die durch 4 ohne Rest teilbar sind*.
*afaik gibt es einen Modus des kerns der das toleriert. Wird aber selten
verwendet.
Michael P. schrieb:> void MeineFunktion(uint32_t Data)
Das ist ein 32-bit-Integer.
> uint8_t DataArray[11];
Da sollen 8-bit-Integer rein, und demzufolge ist das nicht aligned.
> *(uint32_t*)&DataArray[2] = Data;
Dieses Pointergecaste wird wohl auf eine unaligned-Adresse kommen (das
bewirkt den Hardfault) und ist außerdem wegen Pointer-Aliasing schon
nach C-Standard undefiniertes Verhalten.
>Nu meine ganz einfache Frage:>Was mache ich falsch?
Ganz einfache Antwort.
Du kannst keine Datentypen und keine Zeiger korrekt verwenden.
Nimm dir noch mal ein C-Buch zur Hand und frische dein Wissen auf.
Was soll das bewirken ?
*(uint32_t*)&DataArray[2]
Daten in ein Array geht so:
arr[123] = datum;
arr und datum müssen vom selben Datentyp sein, z.B. uint32_t, sonst
gehts schief. Mit Pointergecaste kann man nicht eine Typwandlung machen.
Der Hardfault kommt warscheinlich daher, das du auf eine Speicheradresse
schreibst, die kein RAM ist, da das Schreibziel
*(uint32_t*)&DataArray[2] irgendeinen zufälligen Wert enthält.
Karl schrieb:> *afaik gibt es einen Modus des kerns der das toleriert. Wird aber selten> verwendet.
Nicht bei ARMv6-M aka Cortex-M0(+). Der kann das nicht. bei Cortex-M3
und -M4 geht das per default - allerdings nicht bei 64-bittigen
Zugriffen (LDRD/STRD).
Hier müsstest Du memcpy() bemühen, das kann normalerweise auch mit
nicht-aligned Pointern umgehen.
Michael P. schrieb:> Hallo zusammen,>> ich versuche in meinem Programm Daten in ein Array zu kopieren. Das löst> jedoch einen "Hard Fault" aus und die CPU bleibt stehen.> Ich verwende einen STM32F030> Was mache ich falsch?
Du nimmst den falschen Controller.
Fehlermöglichkeiten ohne Ende!
Steig um auf etwas bei dem Mensch noch den Überblick wahren kann, z.B.
AVR
Danke für den Hinweis mit dem Alignment. Daran habe ich nicht gedacht.
Werde die Funktion ein wenig abändern.
@kernighan ritchie
Ich frage mich wer von uns beiden das C-Buch lesen sollte?
@Thomas
Nicht immer kann man sich aussuchen welchen Controller man nimmt, z.B.
wenn die Werkstatt gerade nix anderes hergibt. Außerdem kann man auch
bei einem AVR genug Fehler machen.
>>Was soll das bewirken ?>> *(uint32_t*)&DataArray[2]>Ich frage mich wer von uns beiden das C-Buch lesen sollte?
Mach mich wissend, was macht dieser Code deiner Meinung nach ?
Michael P. schrieb:> Danke für den Hinweis mit dem Alignment. Daran habe ich nicht gedacht.> Werde die Funktion ein wenig abändern.
Du könntest auch ein Struct mit
1
__attribute__((packed))
verwenden. Dann kommt kein Padding dazu.
Dann würde der Compiler (auf umständliche Art und Weise) dafür sorgen,
dass das Aligning nicht verletzt wird.
kernighan ritchie schrieb:> Mach mich wissend, was macht dieser Code deiner Meinung nach ?
Das ist ein normaler Weg, um bei unalligntem Prozessor (z.b. PC) Daten
in einen Bytebuffer zu bringen (oder heraus zu holen). Wenn man nicht
(was richtig wäre) memcpy nutzen will.
Die Adresse des dritten Bytes wird gecastet.
Auf einen pointer auf einen 32bit wert.
Und davon der Inhalt.
Steht es links bei der Zuweisung, werden DataArray[2...5] beschrieben,
sonst gelesen.
vieleicht hilft es auch das bytearray passend zu alignen
__attribute(aligned(4)) oder so ...
Thomas schrieb:> Du nimmst den falschen Controller.> Fehlermöglichkeiten ohne Ende!> Steig um auf etwas bei dem Mensch noch den Überblick wahren kann, z.B.> AVR
aha .. und morgen fangen alle wieder mit trabbi an fahren zu lernen?
ich hatte beim M7 diverse geschwindigkeitsproleme soald das projekt
größer wird.
compilat ist ca 560kb groß ...
per linker file muss ich nun im linker alles auf 8 alignen
>> Mach mich wissend, was macht dieser Code deiner Meinung nach ?>Das ist ein normaler Weg, um bei unalligntem Prozessor (z.b. PC) Daten>in einen Bytebuffer zu bringen (oder heraus zu holen).
Da das ja korrekt ist muss der Hardfault ja aus
/* weitere Verarbeitung */
kommen, sonst ist ja im Codeausschnitt nicht zu sehen.
immer diese Empfehlung, einen 8bitter zu nutzen.
Der TO will ein 32bit Datum in ein 8-bit Array kopieren,
da hilft ein Prozesorwechsel auch nichts. Reines Datenschieben
geht dann auch nicht besser ohne C-Kenntnisse.
DataArray[2] // ein Wert in einem Array, byte aligned
& // davon die Speicheradresse
(uint32_t*) // diese Adresse wieder in einen Zeiger gecasted
* // und davon wieder den Wert
Damit sind die Daten kein einzges mal im RAM neu aligned worden,
sondern nur ihre Deutung umdefiniert.
Wenn schon STM32 und Datenschieben, dann wenigstens mit dem DMA
Controller.
HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t
DstAddress, uint32_t DataLength);
DMA konfigurieren, wenn nicht schon geschehen.
Man kann übrigens DMA Kanäle in 8, 16 und 32 bit konfigurieren, je nach
Bedarf.
{
hdma_memtomem_dma2_stream4.Instance = DMA2_Stream4;
hdma_memtomem_dma2_stream4.Init.Channel = DMA_CHANNEL_0;
hdma_memtomem_dma2_stream4.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma2_stream4.Init.PeriphInc = DMA_PINC_ENABLE;
hdma_memtomem_dma2_stream4.Init.MemInc = DMA_MINC_ENABLE;
hdma_memtomem_dma2_stream4.Init.PeriphDataAlignment =
DMA_PDATAALIGN_BYTE;
!***!
hdma_memtomem_dma2_stream4.Init.MemDataAlignment =
DMA_MDATAALIGN_BYTE;
!***!
hdma_memtomem_dma2_stream4.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma2_stream4.Init.Priority = DMA_PRIORITY_LOW;
hdma_memtomem_dma2_stream4.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_memtomem_dma2_stream4.Init.FIFOThreshold =
DMA_FIFO_THRESHOLD_FULL;
hdma_memtomem_dma2_stream4.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_memtomem_dma2_stream4.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_memtomem_dma2_stream4) != HAL_OK)
{
_Error_Handler(_FILE_, _LINE_);
}
}
void MeineFunktion(uint32_t Data)
{
uint8_t *ptr_data = (uint8_t *)&Data; // Pointer Quelle
uint8_t DataArray[11];
uint8_t *ptr_DataArray = (uint8_t *)&DataArray[2]; // Pointer Ziel
HAL_DMA_Start(&hdma_memtomem_dma2_stream4, ptr_data, ptr_DataArray, 4);
HAL_DMA_PollForTransfer(&hdma_memtomem_dma2_stream4,
HAL_DMA_FULL_TRANSFER,1);
DataArray[6] = (uint8_t)0x09;
/* weitere Verarbeitung */
}
Für 4 Byte ein bisschen Kanonen auf Spatzen mit DMA, aber dafür gehts.
Und den DMA Controller sollte man beim Cortex-M immer nutzen, der kann
Datenschieben besser als die CPU.
wer es mal ausprobieren will, geht ganz einfach mit DMA, nur die Maus
schubsen.
Wer trotzdem den 8bitter nutzen muss, nur als Erinnerung, 32 Bit CPUs
gibt es seit den 80ern, ebenso DMA mit eben den selben CPUs.
https://en.wikipedia.org/wiki/Amiga_Chip_RAM
Zitat "Under the Amiga architecture, the direct memory access (DMA)
controller is integrated into the Agnus (Alice on AGA models) chip."
kernighan ritchie schrieb:> Wer trotzdem den 8bitter nutzen muss, nur als Erinnerung, 32 Bit CPUs> gibt es seit den 80ern
Wer trotzdem die einfachere Lösung nutzen muss, nur als Erinnerung,
weitaus kompliziertere gibt es seit den 80ern :)
kernighan ritchie schrieb:> Für 4 Byte ein bisschen Kanonen auf Spatzen mit DMA, aber dafür gehts.> Und den DMA Controller sollte man beim Cortex-M immer nutzen, der kann> Datenschieben besser als die CPU.
WTF ?
Ja, DMA ist für dieses triviale Problem wirklich mehr als angemessen ...
kernighan ritchie schrieb:> Für 4 Byte ein bisschen Kanonen auf Spatzen mit DMA, aber dafür gehts.> Und den DMA Controller sollte man beim Cortex-M immer nutzen, der kann> Datenschieben besser als die CPU.Mampf F. schrieb:> WTF ?>> Ja, DMA ist für dieses triviale Problem wirklich mehr als angemessen ...
öhm so blöd das klingt
wenn man mehr als 128 Bytes verschiebt und es keine 32bit sind
ist das per DMA wirklich sogar schneller ^^
wenn 32 bit umkopiert werden sollen geht auch memcpy ganz easy ...
aer ei 8/16bit umkopiererei kommt permanent das alignment dazwischen
ich weiß nicht wie oft ich schon deswegen geflucht habe
ich hab im M7 diverse memcpy funktionen per DMA die aber selbst im ITCM
ram liegen... also auch schnell durch sind
32zuterrtzkutehte schrieb:> aer ei 8/16bit umkopiererei kommt permanent das alignment dazwischen> ich weiß nicht wie oft ich schon deswegen geflucht habe
Eigentlich tritt das Problem überhaupt nur auf, wenn man Daten-IO macht.
In den anderen Fällen sollte man sich lieber mal anständige
Datenstrukturen überlegen, das ist sauberer und schneller.
Für IO braucht man aber sowieso entsprechende Routinen zum Konvertieren
beim Senden und Empfangen, schon weil man sich um Endianess kümmern muß.
32zuterrtzkutehte schrieb:> wenn man mehr als 128 Bytes verschiebt und es keine 32bit sind> ist das per DMA wirklich sogar schneller ^^
Ja und in der Zwischenzeit wartet man, bis der DMA fertig ist? ;-)
Kaum zu glauben, was hier die 'Gutglückhobbyprogrammierermöchtegern' vom
Stapel lassen.
Dass der gute uC definiert auf den Hardfault verzweigt, anstatt
'abzuschmieren', erlaubt den SW-Fehler zu finden. Bei einer anderen CPU
hätten wir wieder den Thread: Mein AVR stürzt nach 2h ab, was kann ich
tun?
=> Der Hardfault ist ein tolles feature und keine Last!
Ach ja, und was DMA ist, haben die 'homebrews' auch noch nicht kapiert.
Schei... nein. :-<
DMA läuft in HW, da muss nix gefetcht und (wie meist) über Register
geladen werden.
8x4=32 schrieb:> Dass der gute uC definiert auf den Hardfault verzweigt, anstatt> 'abzuschmieren', erlaubt den SW-Fehler zu finden.
Jein. Ein 8-bitter hat keine Alignment-Probleme, und daher ist es dort
auch kein SW-Fehler, wenn man z.B. aus einem Paketbuffer sich einen
32bit-Int direkt rauszieht, ohne sich um Alignment zu kümmern.
Es ist andererseits, wie man sieht, auch nicht portabel, sondern eben
ein Hack, der (je nach Umsetzung) evtl. bereits in den Bereichen von
undefined behaviour liegt, wegen pointer aliasing, wenn nicht mit packed
structs gemacht, die aber auch nicht unbedingt portabel sind.
> => Der Hardfault ist ein tolles feature und keine Last!
Daß der Hardfault kommt, wenn das Alignment gerissen wurde, ist
natürlich gut, insbesondere wenn man sich mal einen vernünftigen Handler
schreibt, der einem die fault address und Status auswertet. Anhand des
Mapfiles kann man immerhin sehen, in welcher Funktion der Fehler
aufgetreten ist, wenngleich die Ursache natürlich immer noch ganz
woanders liegen kann.
kernighan ritchie schrieb:> wer es mal ausprobieren will, geht ganz einfach mit DMA, nur die> Maus> schubsen.>> Wer trotzdem den 8bitter nutzen muss, nur als Erinnerung, 32 Bit CPUs> gibt es seit den 80ern, ebenso DMA mit eben den selben CPUs
... um hier mal keine Mißverständnisse aufkommen zu lassen, DMA bietet
schon die uralte Z80 Architektur oder bei den AVR- 8Bittern ein Xmega.
Das ist nichts exklusiv 32-bittiges.
8x4=32 schrieb:> Dass der gute uC definiert auf den Hardfault verzweigt, anstatt> 'abzuschmieren', erlaubt den SW-Fehler zu finden. Bei einer anderen CPU> hätten wir wieder den Thread: Mein AVR stürzt nach 2h ab, was kann ich> tun?
Watchdog aktivieren?
SW-Fehler sind nicht deswegen schwerer zu finden weil sie 8-bittiger
Natur sind.
Viele Fehler können bei AVR & Co gar nicht auftreten weil sie viel
einfacher gestrickt sind. So sie dann doch mal auftreten lässt sich ein
aktivierter Watchdog natürlich wunderbar zur Eingrenzung von Fehlern
nutzen. Bei diversen Typen gibts dann ja auch noch die
Hardware-Breakpoints...
kernighan ritchie schrieb:>>Ja und in der Zwischenzeit wartet man, bis der DMA fertig ist?> ;-)>> Der DMA kann auch einen IRQ auslösen, dann muss man nicht warten.
Ja schon klar ... Aber in der Regel hat man keine asynchrone
Datenverarbeitung ... Der Software-Overhead etwas auf asynchron
umzubauen ist viel größer, als einfach stupide per Memcpy Daten zu
kopieren.
Oder im Fall vom TE, wo es vermutlich Daten für die Kommunikation mit
extern ist, definiert man sich ein sauberes (gepacktes) Struct und hat
all die Probleme nicht - der Compiler macht es dann schon richtig und
weiß auch, wie man auf unaligned-Daten zugreift.
Dann hat man die Structs auch noch gleich als Dokumentation quasi
kostenlos inklusive ;-)
Wenn man es per Indizes per Hand per Pointer-Casting auf irgendwas
selbst macht, gibt es halt Probleme.
Die Lösung ist einfach sauberes Programmieren.
Wenn es sein muss, baut man sich noch ein union, in dem das Struct und
ein uint8_t-Array ist, dann braucht man nicht mal mehr memcpy.
Das sind mal wieder Diskussionen hier.......... die keinem helfen.
Für solche leichten Sachen benutze ich gerne einen eigenen Union Type.
Bei dem kannst du auf jedes Byte einzeln zu greifen ohne großen Aufwand.
Pfeifen gibt es hier ......... HAMMER!
Mampf F. schrieb:> Oder im Fall vom TE, wo es vermutlich Daten für die Kommunikation mit> extern ist, definiert man sich ein sauberes (gepacktes) Struct und hat> all die Probleme nicht - der Compiler macht es dann schon richtig und> weiß auch, wie man auf unaligned-Daten zugreift.
Structs über Datenbuffer zu casten ist fast ein so großer Pfusch wie
Bitfelder über Register zu legen (Stichwort Endianess). Und der Compiler
kann unaligned access nur dann richtig machen, wenn die Hardware das
auch unterstützt, was bei den kleinen Cortexen nicht der Fall ist und
direkt in einen Hardfault rennt.
Nop schrieb:> Und der Compiler> kann unaligned access nur dann richtig machen, wenn die Hardware das> auch unterstützt, was bei den kleinen Cortexen nicht der Fall ist und> direkt in einen Hardfault rennt.
Meine Erfahrung mit den früheren ARM7TDMI sagt etwas anderes ... Da
hatte der Compiler unaligned-Zugriffe auf mehrere aligned-Zugriffe
aufgeteilt und zur Not auch umständlich wieder zu einem 32Bit Datum
wieder zusammengebaut - aber halt nicht, wenn man Pointer-Arithmetik
händisch falsch benutzte ... Aber packed Structs war wie gemeint kein
Problem.
Bei den größeren (ab M3?) ist das aber nicht mehr notwendig und das weiß
der Compiler sehr genau :)
Ah, ich hab meinen uralt-Thread wieder gefunden, der genau das Problem
zeigt, aber auch zeigt, dass der Compiler weiß, was er machen muss bei
unaligned-Daten:
Beitrag "[WinARM] Data-Alignment lässt grüßen"
Für die click-faulen:
.
1
Ich bin gerade dabei einen .BMP Importfilter für meinen SAM7S zu basteln
2
und wollte mir mal ankucken, wie der mit dem misalignment des
3
Bitmap-Headers zurecht kommt (Optimiert auf größe).
4
5
Das Ergebnis hat mich so schockiert, dass ich es euch nocht
6
vorenthalten will. Schaut es euch mal an und lasst es auf euch wirken.
Mampf F. schrieb:> Ah, ich hab meinen uralt-Thread wieder gefunden, der genau das Problem> zeigt, aber auch zeigt, dass der Compiler weiß, was er machen muss bei> unaligned-Daten:
Danke - beeindruckend und erschreckend zugleich. :-)