Schönen guten Tag zusammen! Seit einigen Wochen arbeite ich mich in die Arm Cortex Architektur ein, konkret Atmel SAMD10D14 mit M0+ CPU. In C (Atmel Studio) läuft es ganz gut, ich komme voran, diverse Taktfrequenzen laufen "schon". So wollte ich mal auch kleine Assemblerprogramme schreiben, um auch mal das Ding genau von Innen kennenzulernen, back to the roots, ich liebe Assembler trotz seiner Nachteile wie Aufwand und Portabilität. Eine Assembler Entwicklungsumgebung scheint es nicht zu geben, Atmel Studio patzt genauso wie Eclipse. Bei ersterem habe ich nach Anweisung alle C Files rausgeworfen, allerdings bindet der Assembler noch einige Files ein, was sich nicht abändern läßt in der Toolchain, da das Parameterfeld für arm-none-eabi-gcc grau hinterlegt ist, also nichts geändert werden kann. Auch der Assembler mault legale Befehle an wie mov r1,#80 (cannot honor width suffix). So nutze ich nun auf Kommandoebene unter Linux die Gnu-Arm toolchain: arm-none-eabi-as -mcpu=cortex-m0plus -mthumb test_arm.s -o test_arm.o arm-none-eabi-ld -e 0 -Ttext=0x0 -o test_arm.elf test_arm.o arm-none-eabi-objcopy -O ihex test_arm.elf test_arm.hex Und hier stoße ich auf ein weiteres Problem. Obwohl ich mit der Direktive .org genau angebe, wo die Vektortabelle und der Maschinencode platziert werden sollen, wird ab Adresse 0 der Code abgelegt, danach folgt die Vektortabelle (umgekehrt wäre richtig). Auch Versuche mit Direktiven wie .text und .data bleiben unerhört. Im Quellcode habe ich außerdem die Reihenfolge - Vektoren -Assemblerbefehle. Was mache ich falsch? Was ist von der GNU Toolchain zu halten, zumal mein Disassembler behauptet, aus mov r1, #80 wird ein 32 Bit ARM Befehl movw erzeugt, der erst ab dem M3 verstanden wird? Gibt es nicht eine schöne kostenlose Entwicklungsumgebung für reine Assemblerprogramme, denn ich habe langsam keine Lust mehr auf diese Assembler Odyssee? Herzlichst, Uwe
Uwe H. schrieb: > Gibt es nicht eine schöne kostenlose > Entwicklungsumgebung für reine Assemblerprogramme, Ja, nimm einfach SEGGER Embedded Studio. Damit kannst du dir ein Projekt für dein Device generieren lassen. Du kannst dann einfach die main.c usw. raus schmeißen und fügst deinen Assemblercode in dem Startup Code hinzu. Damit startest du mit etwas funktionierendem, was für dich einfacher sein sollte. https://www.segger.com/products/development-tools/embedded-studio/ Das Problem bei dir wird sein, dass du kein Linkerfile hast, welches dem Linker sagt, welche Section er wo hinlegen soll, also z.B. die Vektoren auf 0x00 und das Programm dahinter.
:
Bearbeitet durch User
Uwe H. schrieb: > Und hier stoße ich auf ein weiteres Problem. Obwohl ich mit der > Direktive .org genau angebe, wo die Vektortabelle und der Maschinencode > platziert werden sollen, wird ab Adresse 0 der Code abgelegt, was Du ja hiermit explizit einforderst: > arm-none-eabi-ld -e 0 -Ttext=0x0 -o test_arm.elf test_arm.o
Uwe H. schrieb: > Obwohl ich mit der > Direktive .org genau angebe, wo die Vektortabelle und der Maschinencode > platziert werden sollen, wird ab Adresse 0 der Code abgelegt Das ist IMO dokumentiertes Verhalten von Gnu LD. Der ist zu dumm um ohne ein explizites Linker Skript (und entsprechende Deklarationen im Quelltext) Sachen an definierte Addressen zu legen. Darüber hinaus gibt es AFAIK keine Möglichkeit etwaige Lücken sinnvoll zu füllen, d.h. wenn ich was im Code an Addresse 0x1000 haben will kann er den restlichen Code in .text nur davor oder dahinter packen. Für Arm Cortex-M würde ich eher einen C-Compiler empfehlen. Das hat auch den Hintergrund das es auf den dickeren µCs oft 2,3,4x dieselbe Hardware nur an unterschiedlichen Addressen gibt. Das kann ich in C relativ leicht abhandeln (Pointer), in Assembler wären das irgendwelche Makros. Uwe H. schrieb: > Eine Assembler > Entwicklungsumgebung scheint es nicht zu geben, Atmel Studio patzt > genauso wie Eclipse. Hmm, ich habe hier in Eclipse ein paar Assembler Files in den Cortex-M C Projekten mit drin, die tun alle ohne Probleme. Allerdings war da IIRC auch was manuell anzupassen, was das Parsing und den Indexer anging. Ist leider schon zu lange her für Detais.
Til S. schrieb: > Du kannst dann einfach die main.c usw. raus schmeißen und fügst deinen > Assemblercode in dem Startup Code hinzu. Ich kenne eure IDE nicht aber normalerweise ist der Startup-Code von Atmel in C geschrieben und nicht in Assembler.
Den Start-up Code wollte ich natürlich auch in Assembler schreiben, also Alles, angefangen von der Vektortabelle bis hin zu den Interrupt-/Exceptionroutinen, Initialisierungen, Alles! Ist doch nicht schwer und macht Spaß, denn man hat die totale Kontrolle. In C packt man dir noch paar Libraries dazu, wo du nicht genau weißt, was die so alles machen, was für Nebeneffekte die haben. Klar kann man das mit viel Zeitaufwand erforschen, sich durch Macros und Strukturen mit Bitfeldern durcharbeiten. Bei sicherheitskritischen Sachen würde ich mir ernsthaft überlegen, ob Assembler nicht die transparentere und sichere Alternative wäre, denn was die Compiler so an Assemblercode vom Stapel lassen, ist meistens schwer verständlich im Gegensatz zum selbst geschriebenen Code! Bitte nicht falsch verstehen: Ich will hier keinen Religionskrieg starten Assembler gegen Hochsprache!!!!
Du könntest eine *.S Datei erzeugen, zum Build hinzufügen und dann im Main aufrufen.
In der GCC Toolchain sind üblicherweise Muster für den Startup Code dabei, in .\share\gcc-arm-none-eabi\samples\startup.
Christopher J. schrieb: > Ich kenne eure IDE nicht aber normalerweise ist der Startup-Code von > Atmel in C geschrieben und nicht in Assembler. Autsch. Dann sollte man sich den in Assembler von einem anderen Hersteller klauen, dank Cortex-M stimmen ja wenigstens die Fault Handler bis zum SysTick. Die restlichen Handler müssten angepasst werden, kann man dann aber aus der C-Datei abschreiben.
Hallo Johannes und Jim! Nicht der Start-up Code ist das Problem, sondern den Assemblercode richtig assembliert zu bekommen! Bisher sieht es so aus, daß ich mir wohl ein Linkerfile schreiben muss, damit alles an die richtigen Adressen gepackt wird. Mal schauen, wie man das macht..... Gruß Uwe
Uwe H. schrieb: > In C packt man > dir noch paar Libraries dazu, wo du nicht genau weißt, was die so alles > machen, was für Nebeneffekte die haben. Die mache nur wichtige Sachen (RAM nullen, Stack setzen, Takt, PLL setzen usw.). Wenn sie Nebeneffekte hätten, dann hätte das bestimmt schon jemand bemerkt. Uwe H. schrieb: > Bei sicherheitskritischen Sachen würde ich mir ernsthaft > überlegen, ob Assembler nicht die transparentere und sichere Alternative > wäre Bei sicherheitskritischen Sachen würde ich auf keinen Fall den Programmierer unnütz von der eigentlichen Aufgabe ablenken, indem ich ihm z.B. das Push/Pop-Gedöns aufbürde. Verwaltungs-Sachen kann der Compiler viel besser und vor allem fehlerfrei.
Uwe H. schrieb: > Den Start-up Code wollte ich natürlich auch in Assembler schreiben, > also Alles, angefangen von der Vektortabelle bis hin zu den > Interrupt-/Exceptionroutinen, Initialisierungen, Alles! Ok, ist verstanden. Habe ich selber auch mal gemacht. Uwe H. schrieb: > Ist doch nicht > schwer und macht Spaß, denn man hat die totale Kontrolle. Dito ;) Ich hab dir mal ein absolut minimalistisches Blinky (für STM32) angehängt, was in 84 Byte läuft (ja, es geht kleiner und nein, das Programm ist nicht sinnvoll). Ohne Interrupts, Startup-Code usw. Der Startup-Code ist gewissermaßen das Blinky. Das solltest du relativ problemlos auf den SAMD10 anpassen können. Die "minimale" Vektortabelle ist vorhanden, d.h. initialer SP und PC aber sonst eben nichts. Da kannst du noch deine Vektoren einbauen. Das Makefile ist gewissermaßen auch ein Witz. Kann man innerhalb weniger Sekunden auch in eine .sh oder .bat umbauen, deshalb hier der gesamte Inhalt:
1 | all: |
2 | arm-none-eabi-as -mthumb -mcpu=cortex-m3 -o miniblink.o miniblink.S |
3 | arm-none-eabi-ld -T miniblink.ld -o miniblink.elf miniblink.o |
4 | arm-none-eabi-objcopy -O binary miniblink.elf miniblink.bin |
5 | arm-none-eabi-size miniblink.elf |
Hier sei noch angemerkt, dass man "normalerweise" weder as zum assemblen, noch ld zum linken benutzt. Beides übernimmt in der Regel der gcc und zwar mit
1 | arm-none-eabi-gcc -x assembler-with-cpp # für den Assembler |
2 | # und |
3 | arm-none-eabi-gcc # für den Linker |
Dem Linker-Script fehlt quasi der komplette Teil für den RAM, also .data und .bss, weil die einfach nicht gebraucht werden, weil eben kein RAM genutzt wird. Ist eben minimalistisch aber man kann es ja noch erweitern. Da es ebenfalls sehr klein ist, hier der komplette Inhalt:
1 | MEMORY |
2 | { |
3 | ROM (rx) : ORIGIN = 0x08002000, LENGTH = 64k |
4 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K |
5 | } |
6 | |
7 | _estack = ORIGIN(RAM) + LENGTH(RAM); |
8 | |
9 | ENTRY(gpioconfig) |
10 | |
11 | SECTIONS |
12 | { |
13 | .text : { |
14 | KEEP(*(.vector_table)) |
15 | *(.text*) |
16 | } >ROM |
17 | } |
Das ENTRY(gpioconfig) kann man sich schenken. Normalerweise steht dort ENTRY(reset_handler) oder so ähnlich. Ist jedoch in jedem Fall meines Wissens nach überflüssig. _estack ist das Symbol für den initialen Stackpointer und zeigt auf das Ende des RAM (was ja ohnehin nicht benutzt wird aber sei es drum) und der Rest sagt einfach nur: "pack .vector_table vor .text und beides ins ROM".
Uwe H. schrieb: > Atmel Studio patzt > genauso wie Eclipse. In eclipse ist das kein Problem, einfach alle .c Dateien löschen und den Assembler Code in .S Dateien packen. Die müssen natürlich in der GNU Assembler Syntax vorliegen. Wenn du -nostartfiles -nostdlib an den Linker übergibst, wird da auch sonst nichts mit reingezogen. Bei z.B. den STM32 in den Hersteller-Beispielen der Startup-Code und ISR-Vektor sowieso schon in Assembler, und du kannst auch einfach die normalen Linker-Scripte übernehmen, die interessiert es nicht ob C oder Assembler genutzt wird.
Christopher J. schrieb: > Til S. schrieb: >> Du kannst dann einfach die main.c usw. raus schmeißen und fügst deinen >> Assemblercode in dem Startup Code hinzu. > > Ich kenne eure IDE nicht aber normalerweise ist der Startup-Code von > Atmel in C geschrieben und nicht in Assembler. Ja, kenne ich aber ist in diesem Fall tatsächlich Assembler. @Uwe Ich habe dir schnell mal ein Projekt für den SAMD10D14 und Embedded Studio zusammen gebaut. In dem Projekt sind keine C Dateien drin sondern nur Assemblercode. Du kannst deinen Assemblercode direkt in der thumb_crt0.s ergänzen oder dort einen Assemblerfunktion in einem separaten Assemblermodul aufrufen. Das Projekt macht gerade nur einfach ein while(1) (b .) und ich habe dir einen Kommentar eingefügt, damit du weißt, wo du deinen Code einfügen musst.
PS: Man braucht auch kein eigenes Makefile schreiben, man kann z.B. die Projekt-Templates vom GNU-MCU-Eclipse-Plugin nutzen und eclipse alles automatisch assemblisieren/linken lassen. So hat man mehr das Feeling einer Entwicklungsumgebung und weniger Handfrickelei.
Ich würde Keil µvision verwenden. Die packs für den SAMD10D14 findet man unter: http://www.keil.com/dd2/pack/ Darin enthalten sind auch die startup files (startup_SAMD10.s) in assembler.
Uwe H. schrieb: > Bisher sieht es so aus, daß ich mir > wohl ein Linkerfile schreiben muss, damit alles an die richtigen > Adressen gepackt wird. Das ist zwar sinnvoll, aber (wie oben schon geschrieben) nicht unbedingt notwendig. Du hast GNU ld in deiner Kommandozeile gesagt, dass er das .text-Segment an Adresse 0 tun soll (und genau das hat er gemacht). Mach' das einfach richtig, dann funktioniert's auch ohne Linker-Script: arm-none-eabi-ld -e 0 -o test_arm.elf --section-start=.interrupt_vector_table=0 xxx.o Dann muss man nur noch (mit '.section ".interrupt_vector_table"') dafür gesorgt werden, dass die auch tatsächlich in der richtigen Section landet und alles ist gut.
Danke dir Til für deine Mühe! Werde am WE mir mal das Seggerstudio reinziehen, habe es schon runtergeladen, und mal schauen, was dein Beispielprogramm bringt. Zur Qualität des GNU Codes sagt leider keiner etwas. Auch beim C Compiler hatte ich schon Übles erlebt, bei Optimierungsstufe Q1 hatte mir der Compiler eine Zeile wegoptimiert, die permanent auf das OUTTGL Register schreibt, sprich es toggelt.... Seitdem ist Q0 angesagt, entsprechend sieht der Code aus..... BG Uwe
Uwe H. schrieb: > bei Optimierungsstufe Q1 hatte > mir der Compiler eine Zeile wegoptimiert, die 99,9% des Codes, der "Kaputt"/weg optimiert wird, ist schlicht falsch, und mit falschem Code darf der Compiler machen was er will. Auch wenn man mit -O0 kompiliert kann einen das später einholen. Daher sollte man lieber den Code korrigieren anstatt über den Optimizer zu lästern!
Uwe H. schrieb: > Obwohl ich mit der Direktive .org genau angebe Die Grundregel galt schon zu CP/M-Zeiten: wenn man verschiebliche Objektdateien benutzt, dann legt der Linker fest, was wohin kommt. .org ist dann keine sinnvolle Direktive. Uwe H. schrieb: > Zur Qualität des GNU Codes sagt leider keiner etwas. Sie ist sehr viel besser als die der meisten Leute, die vor der Tastatur sitzen. ;-)
Ok, Jörg, dann verrate mir bitte mal, wie man dem Linker mitteilt, nicht verschiebbaren Code zu erzeugen, sprich Code mit absoluten Adressen, wobei relative Adressen nur Sinn machen, wenn die Programme im RAM laufen, im Flash verschiebt sich nichts....
Uwe H. schrieb: > Ok, Jörg, dann verrate mir bitte mal, wie man dem Linker mitteilt, nicht > verschiebbaren Code zu erzeugen, sprich Code mit absoluten Adressen, > wobei relative Adressen nur Sinn machen, wenn die Programme im RAM > laufen, im Flash verschiebt sich nichts.... Bei jedem Assembler, der Sections unterstützt, versteht sich .org als Offset zur Section-Startadresse, nicht als absolute Adresse. Und wie man bestimmt, wo die Section hinkommt, wurde ja schon angesprochen.
Benutz doch einfach ein Linker-Script, so wie es alle anderen auch machen. Kopier einfach das vom Hersteller aus dem Beispiel-Code. Damit erübrigen sich die ganzen Probleme. Uwe H. schrieb: > Ok, Jörg, dann verrate mir bitte mal, wie man dem Linker mitteilt, nicht > verschiebbaren Code zu erzeugen, Der Code von Linkern für Embedded-Plattformen erzeugt immer absoluten Code. Der Compiler bzw. Assembler, welche die .o Dateien erzeugt, hingegen nicht. Den macht der Linker erst absolut. Anders geht's auch gar nicht, was sollte der Linker tun wenn mehrere .o Dateien mit ".org 0" auf die gleiche Adresse schreiben wollen? Daher werden zentral im Linker-Script den Symbolen Adressen zugewiesen. Bevor du mit Assembler anfängst schau dir doch vielleicht mal genau an, wie C-Projekte funktionieren. Da kannst du dann Stück für Stück allen C-Code durch Assembler ersetzen.
Markus F. schrieb: > Bei jedem Assembler, der Sections unterstützt, versteht sich .org als > Offset zur Section-Startadresse, nicht als absolute Adresse. Wirklichen Sinn hat das bloß nicht, denn man kann ja nie vorhersagen, ob es nicht noch weitere Objekte gibt, die in der gleichen Section etwas platzieren wollen. OK, mit einer Ausnahme: wenn man sich überlagernde Sections haben will. Aber wofür sollte man das brauchen? Der Linker kann auf der Basis von Symbolnamen ja bereits überlagern (Common-Block-Feature aus FORTRAN-Zeiten).
Uwe H. schrieb: > Zur Qualität des GNU Codes sagt leider keiner etwas. Auch beim C > Compiler hatte ich schon Übles erlebt, bei Optimierungsstufe Q1 hatte > mir der Compiler eine Zeile wegoptimiert, die permanent auf das OUTTGL > Register schreibt Das liegt mit an Sicherheit grenzender Wahrscheinlichkeit nicht am GNU Compiler, sondern an der Definition des Registers. Wahrscheinlich fehlt schlicht und einfach ein volatile an passender Stelle. Uwe H. schrieb: > Ok, Jörg, dann verrate mir bitte mal, wie man dem Linker mitteilt, nicht > verschiebbaren Code zu erzeugen, sprich Code mit absoluten Adressen Deine Ahnungslosigkeit scheint keine Grenzen zu kennen. Der Linker legt den Code natürlich auf feste Adressen. Das ist ja gerade sein Job. Allerdings kann er alles, was sich innerhalb der gleichen .section befindet, darin beliebig anordnen. das ist der Vertrag, den du mit dem Linker hast. Und wenn du irgendwas an feste Adressen legen mußt, dan pack es in eine eigene Section und tu die in dein Linkerscript. Die GNU Toolchain ist wie ein Lego-Kasten: sehr mächtig, wenn man weiß was man tut. Wer einfach nur blind Steine aufeinander stapelt, wird damit aber nicht glücklich.
Uwe H. schrieb: > Zur Qualität des GNU Codes sagt leider keiner etwas. für die Qualität von Assemblercode ist ausschliesslich der Programmierer verantwortlich - da gibt's nix zu sagen.
Sorry Markus, das ist eine Pauschalaussage, für die es aber paar wenige Ausnahmen geben kann! Wenn beim M0+ der Befehl mov r1, #0x80 erlaubt ist, siehe Cortex M0+ Programming Manual, und der Assembler macht daraus eine ARM V7 instruction, nämlich movew r1,#0x80, die erst ab M3 verstanden wird, dann kann man dafür nicht den Programmierer verantwortlich machen und ihn mit Stammtischparolen konfrontieren! Natürlich hatte ich dem Assembler die CPU verraten (mcpu=cortex-m0plus), das rein präventiv! Ich stimme dir zu, wenn du sagen würdest, und das hast du bestimmt versucht, ein Assembler hat bei der (Maschinen)Codeerzeugung quasi keine Optionen, übersetzt also 1:1. Natürlich kann ihm auch mal dabei ein Fehler passieren, s.o., dennoch ist die Fehlerquote weitaus geringer als beim wesentlich komplexeren Compiler, der nicht umsonst generell als wartungsintensiv gilt! Als ich zuletzt vor 15 Jahren Code geschrieben hatte, seitdem machte ich nur noch Hardware/EMV, gab es kaum einen Monat, wo ein Compiler keinen Mist produzierte. Star diesbezüglich war damals der Cosmic Compiler.
Scotty60 schrieb: > Sorry Markus, das ist eine Pauschalaussage, für die es aber paar wenige > Ausnahmen geben kann! Wenn beim M0+ der Befehl mov r1, #0x80 erlaubt > ist, siehe Cortex M0+ Programming Manual, und der Assembler macht daraus > eine ARM V7 instruction, nämlich movew r1,#0x80, die erst ab M3 > verstanden wird, dann kann man dafür nicht den Programmierer > verantwortlich machen und ihn mit Stammtischparolen konfrontieren! Ich kenne mich mit dem M0+ nicht aus, aber 1.) Stammtischparolen liegen mir fern 2.) in meiner Kopie des Cortex-M0+ Technical Reference Manual kommt MOV mit einem immediate-Wert nicht als unterstützte Instruktion vor, lediglich MOVS 3.) mein gas übersetzt dein Statement nicht (auch nicht in irgendwas Falsches), sondern liefert die (zugegebenermassen nicht unbedingt erhellende) Fehlermeldung: "Error: cannot honour width suffix" Und auch die Fehlermeldung lässt sich (wenn auch nur "um die Ecke") erklären: die einzigen "fass' die Flags nicht an" MOV-Instruktionen mit immediate-Werten, die es im Thumb-Instruktionset überhaupt gibt, sind die mit 32-Bit immediates (die wiederum aber der M0plus nicht unterstützt). Der Assembler versteht also "32 bit" und sagt: "nein, kann ich nicht". [edit: Tippfehler korrigiert]
:
Bearbeitet durch User
Scotty60 schrieb: > seitdem machte ich nur noch Hardware/EMV, gab es kaum einen Monat, wo > ein Compiler keinen Mist produzierte. Star diesbezüglich war damals der > Cosmic Compiler. Ich hab schon ne Menge mit dem GNU Compiler gearbeitet und habe diverse Compiler Fehler gefunden, aber kein Einziges Mal war es einer der falschen Code generiert hat - höchstens ineffizienten Code. Die Leute, die hier im Forum darüber lästern dass der Compiler falschen Code generiert, sind meistens die, die nicht wissen was in C erlaubt ist und was nicht! PS: Der Cosmic Compiler soll doch so supi sein, warum gibt man für den so viel Geld aus wenn er so fehlerhaft ist?
Scotty60 schrieb: > wo ein Compiler keinen Mist produzierte. Seltsame Erfahrungen. Kommst mir gerade ein bisschen vor wie der Falschfahrer … „Einer? Alle!“ Gerade im ARM-Bereich wird der GCC (und der darunter liegende Assembler) tagtäglich von vielen Programmierern auf der Welt für produktiven Code benutzt (also pay job, kein Hobby). Meinst du nicht, dass derart grundlegende Fehler dabei sehr schnell auffallen würden?
Jörg W. schrieb: > Die Grundregel galt schon zu CP/M-Zeiten: wenn man verschiebliche > Objektdateien benutzt, dann legt der Linker fest, was wohin kommt. > .org ist dann keine sinnvolle Direktive. Das würde ich aber definitiv anders sehen. In nahezu jeder Architektur gibt es Speicherbereiche oder Teile davon, für die es absolut Sinn ergibt, schon im Quelltext entweder auf eine absolute Adresse oder zumindest auf ein bestimmtes Alignment ausgerichtet zu werden. Und es ist natürlich ein sehr nützliches Feature, dies bereits im Quelltext festlegen (und kommentieren) zu können, statt auf Frickeleien mit irgendwelchen proprietären Make-Scripts angewiesen zu sein. Nö, wirklich Sinn ergäbe nur: Der Compiler/Assembler verwaltet das (soweit vom Programmierer explizit geschrieben) und teilt dem Linker mit, was aus seiner Sicht unveränderlich ist. Der Linker hat sich damit abzufinden und den Rest so zu lokalisieren, dass er passt. Er muss aber natürlich einen Fehler werfen, wenn sich dabei unauflösbare Widersprüche ergeben. Den das (und nur das) wäre eine sinnvolle Unterstützung des Programmierers. > Sie ist sehr viel besser als die der meisten Leute, die vor der > Tastatur sitzen. ;-) Das allerdings ist leider nur zu wahr. Spricht allerdings meiner Erfahrung nach nicht besondern für die üblichen toolchains, sondern eher nur gegen die meisten, die selbige benutzen...
c-hater schrieb: > Und es ist natürlich ein sehr nützliches Feature, dies bereits im > Quelltext festlegen (und kommentieren) zu können Kann ja sein dass du das nützlich findest, aber die GNU Toolchain machts halt nicht so ;-) c-hater schrieb: > statt auf Frickeleien > mit irgendwelchen proprietären Make-Scripts angewiesen zu sein. Ich finde es ist eher eine Frickelei, wenn man die Adressen wild im Quellcode verteilt. Da ist es sauberer, wenn alle Speicherbereiche und -Adressen zentral und übersichtlich im Linker-Script für den konkreten Controller stehen (bei der GNU Toolchain ist übrigens nichts proprietär), und der Code abstrakt und somit portabler ist.
Jörg W. schrieb: > Gerade im ARM-Bereich wird der GCC Du haust in die falsche Kerbe. Er hatte nicht vom GCC geschrieben: "war damals der Cosmic Compiler". Uwe H. schrieb: > Und hier stoße ich auf ein weiteres Problem. Obwohl ich mit der > Direktive .org genau angebe, wo die Vektortabelle und der Maschinencode > platziert werden sollen,... In der ARM-Welt hat es sich als Standard etabliert, sowas wie ORG nicht zu bedienen und nicht zur Kenntnis nehmen zu wollen. Stattdessen muß man Moduln machen, die den Platz, wo was hin soll, etwa so formulieren (Keil):
1 | AREA RESET, CODE, READONLY |
2 | |
3 | oder
|
4 | AREA |.ARM.__at_0x2FC|, CODE, READONLY |
Also: ORG gibt's nicht und LTORG ist nur zum Abstapeln von Direktoperanden gedacht. Man kann nen Trick mit ALIGN und entsprechendem Argument benutzen, aber das ist m.E. letztlich Pfusch. W.S.
Dr. Sommer schrieb: > Ich hab schon ne Menge mit dem GNU Compiler gearbeitet und habe diverse > Compiler Fehler gefunden, aber kein Einziges Mal war es einer der > falschen Code generiert hat - Also wenn man dich so hört, kommt man glatt zu Schluss, Compiler machen keine Fehler, benötigen keine Wartung! Frag mal einen Compilerhersteller, warum Compiler generell wartungsintensiv sind! Solltest du aber Compiler kennen, die absolut fehlerfrei sind, dann ab mit der Info ins Guinessbuch der Rekorde!!!! Und bitte erzähle uns auch keinen von fehlerfreier Software!
Markus F. schrieb: > 2.) in meiner Kopie des Cortex-M0+ Technical Reference Manual kommt MOV > mit einem immediate-Wert nicht als unterstützte Instruktion vor, > lediglich MOVS Markus, da hast du Recht, danke für den Tipp! Generell gibt es bei der Regel mit dem "S" am Befehl (=Update des Statusregisters) einige Ausnahmen, etwa auch bei lsl, lsr, asrs, rors (ohne Anspruch auf Vollständigkeit!). Ich habe den fürchterlichen Verdacht, daß die 16 Bit Befehle namens Thumb (ich weiß, es gibt etwa eine Handvoll Ausnahmen) extrem viele Restriktionen ergeben, auch bei R8-R12, daß ich mich langsam nach den ARM 32 Bit Befehlen sehne (kennste da eine M Version?)! Verständlich, denn je weniger Bits der Opcode hat, desto weniger Möglichkeiten hat man, Stichwort 2^16 statt 2^32 (abzüglich Platz für immediate Bits). Vielleicht bin ich auch zu verwöhnt, denn als ich von 31 Jahren anfing zu programmieren, hatte ich den Luxus, auf Motorola 68000 programmieren zu dürfen, zwischen 12.000-20.000 Zeilen! Das hieß vorbildliche Orthogonalität des Befehlssatzes, in Sachen Adressierung konnte man weitgehend seine Phantasien ausleben! Ok, ich weiß, das ist unfair, denn 68000 Familie war CISC total. Natürlich reichten da keine 1-2 Takte, verständlich.
Scotty60 schrieb: > Markus F. schrieb: >> 2.) in meiner Kopie des Cortex-M0+ Technical Reference Manual kommt MOV >> mit einem immediate-Wert nicht als unterstützte Instruktion vor, >> lediglich MOVS > > Markus, da hast du Recht, danke für den Tipp! Generell gibt es bei der > Regel mit dem "S" am Befehl (=Update des Statusregisters) einige > Ausnahmen, etwa auch bei lsl, lsr, asrs, rors (ohne Anspruch auf > Vollständigkeit!). Ich habe den fürchterlichen Verdacht, daß die 16 Bit > Befehle namens Thumb (ich weiß, es gibt etwa eine Handvoll Ausnahmen) > extrem viele Restriktionen ergeben, auch bei R8-R12, daß ich mich > langsam nach den ARM 32 Bit Befehlen sehne (kennste da eine M Version?)! > Verständlich, denn je weniger Bits der Opcode hat, desto weniger > Möglichkeiten hat man, Stichwort 2^16 statt 2^32 (abzüglich Platz für > immediate Bits). Nun, um ehrlich zu sein bin ich nie auf die Idee gekommen, auch nur eine einzige Zeile ARM Thumb mehr als unbedingt notwendig "von Hand" zu schreiben. Anständige C-Compiler erzeugen - wenn man damit umgehen kann - Code, der für >99% aller Anwendungsfälle mehr als ausreichend ist. Das restliche Prozent (ganz frühen Startup-Code, z.B.) kann man mit Inline-Assembly erschlagen. Mehr tu' ich mir nicht mehr an. > Vielleicht bin ich auch zu verwöhnt, denn als ich von > 31 Jahren anfing zu programmieren, hatte ich den Luxus, auf Motorola > 68000 programmieren zu dürfen, zwischen 12.000-20.000 Zeilen! Das hieß > vorbildliche Orthogonalität des Befehlssatzes, in Sachen Adressierung > konnte man weitgehend seine Phantasien ausleben! Ok, ich weiß, das ist > unfair, denn 68000 Familie war CISC total. Natürlich reichten da keine > 1-2 Takte, verständlich. Ähm, nö. Ein mc68060 schafft mit dem Grossteil des m68k Instruction-Sets mindestens einen Befehl pro Takt, in den allermeisten Fällen (superskalar) sogar zwei. ColdFire V4 nicht ganz so gut, aber ähnlich. Trotzdem: die Zeiten sind (leider) vorbei.
Scotty60 schrieb: > Also wenn man dich so hört, kommt man glatt zu Schluss, Compiler machen > keine Fehler, benötigen keine Wartung! Ich musste meinen Compiler noch nie schmieren oder Kühlwasser wechseln. :-) Selbstverständlich machen Compiler Fehler, aber die, bei denen tatsächlich falscher Code generiert wird, sind dabei in der völligen Minderzahl. Gerade bei einem Compiler wie GCC ist die Bug-Datenbank öffentlich, da kannst du das problemlos nachvollziehen. GCC hat sicher seine Macken (gerade beim AVR kann Georg-Johann Lay dir ein Lied davon singen), aber mittlerweile hat er eine Stabilität erreicht, dass man oft genug mit einer Compilerversion produktiv arbeiten kann, die schon viele Jahre alt ist. Schau dir doch einfach mal an, wie viele Leute hier noch mit der letzten WinAVR-Version aus dem Jahr 2010 AVRs programmieren. Bei ARMs ist es auch nicht grundlegend anders, außer dass man halt für Architekturen wie Cortex-M0+ einen nicht ganz so alten Compiler haben darf. Aber ob man nun mit einem GCC 4.8, 5.x, 6.x oder 7.x arbeitet – funktionieren tut das Compilat mit dem einen wie mit dem anderen, sofern man nicht gerade mit irgendwas so sehr „auf Kante“ ist (bezüglich der Laufzeit), dass die letzten 10 % an Optimierung wichtig sind. Zu direkter Assemblerprogrammierung habe ich bei AVR noch ganz selten gegriffen, seit ich ARMs in der Hand habe, noch nie. Wozu auch? Cortex-M hat ARM so aufgebaut, dass man sogar den Startup-Code komplett in C schreiben kann, wenn man das möchte. (Selbstverständlich heißt das nicht, dass man deshalb den Assemblercode nicht wenigstens rudimentär verstehen können sollte, wenn man in diesem Bereich unterwegs ist.)
Markus F. schrieb: > Nun, um ehrlich zu sein bin ich nie auf die Idee gekommen, auch nur eine > einzige Zeile ARM Thumb mehr als unbedingt notwendig "von Hand" zu > schreiben. Marcus, ich kann dich absolut verstehen! Ich betreibe meine Liebe zu Assembler auch nicht beruflich, und ich will auch keinen Religionskrieg gegen Hochsprachen starten! Aus meinem Ingenieurberuf habe ich mich vor Jahren frühzeitig verabschiedet. Aber gerne erinnere ich mich zurück, wie ich vor 31 Jahren angefangen habe, da geht es um Nostalgie. Natürlich ist mir klar, daß Portabilität und Aufwand bei Assembler schlecht sind, keine Frage. Aber dennoch lernt man nur mit Assembler eine Prozessorarchitektur am besten von Innen kennen, und ich wage zu behaupten, auch auf die Gefahr eines Shitstorms, daß Assembler für sicherheitskritische Anwendungen die mit Abstand beste Wahl ist, da viel transparenter und kürzerer und einfacheren Code! Und ein Assembler ist erheblich einfacher aufgebaut als der generell komplexe Compiler, so daß der Assembler erheblich weniger Fehler machen wird. Den Höhepunkt (und das Ende) der 68000 Familie, den 68060 kenne ich leider persönlich nicht. Beim 68000er hatten die schnellsten Befehle 4 Takte (68020 2 Takte bei Cache hit), die sich schnell vervielfachten bei komplexen Adressierungsarten. Der Vergleich zum 68060 ist etwas unfair mit seinen oft Eintaktbefehlen, da superskalare Architektur, wo komplexe CISC instructions in viele RISC Befehle zerlegt, die aber auf viele ALUs verteilt wurden.
Beitrag #5278193 wurde von einem Moderator gelöscht.
Scotty60 schrieb: > Aber gerne erinnere ich mich zurück, > wie ich vor 31 Jahren angefangen habe, da geht es um Nostalgie. Dann programmiere doch eine historische Architektur in Assembler, einen Z80 oder sowas. ;-) > Aber dennoch lernt man nur mit Assembler > eine Prozessorarchitektur am besten von Innen kennen, Bei Controllern muss man im Allgemeinen relativ wenig über den Prozessor selbst wissen. Das A und O bei Controllerprogrammierung ist die Bedienung der Peripherie. Die kannst du auch in einer höheren Sprache genauso low-level wie in Assembler haben, wenn du halt nicht solche Dinge wie ASF oder CubeMX oder dergleichen benutzt. > und ich wage zu > behaupten, auch auf die Gefahr eines Shitstorms, daß Assembler für > sicherheitskritische Anwendungen die mit Abstand beste Wahl ist, da viel > transparenter und kürzerer und einfacheren Code! Das mit dem kürzeren Code ist eine Mär. Die wurde hier selbst bei sehr simplen Beispielen bereits eindrucksvoll widerlegt, und wenn das Programm komplexer als ein paar Kilobyte ist, dann ist es mit der Übersicht in einem 50seitigen Assemblerprogramm sowieso schnell Essig. Anders sieht das sicher noch aus, wenn du in sowas wie einem ATtiny10 einen Mini-Controller realisierst mit sehr stark abgegrenztem Aufgabenfeld. > Und ein Assembler ist > erheblich einfacher aufgebaut als der generell komplexe Compiler, so daß > der Assembler erheblich weniger Fehler machen wird. Der Assembler ist natürlich einfacher aufgebaut. Es ist aber in der überwiegenden Anzahl der Fälle nicht der Compiler selbst, der die Fehler macht (dieser Mythos vom „wartungsaufwändigen“ Compiler, von dem du den Eindruck erweckst, als würde er fehlerhaften Code am laufenden Band produzieren, zieht sich nun schon durch den ganzen Thread), sondern der fehleranfälligste Teil der Programmierung sitzt immer noch vor der Tastatur. Dieser Teil wiederum ist deutlich weniger codesicher als ein Compiler. Der Compiler arbeitet stur seine formale Sprachbeschreibung ab. Wenn ich einem 10seitigen Hochsprachprogramm ein 50seitiges Assemblerprogramm gegenüberstelle (ungefähr darauf wird's wohl hinauslaufen), dann habe ich auch eine (grob) fünfmal so hohe Fehlerwahrscheinlichkeit – mal in der Annahme, dass natürlich der Programmierer jeweils „fit ist“, also keine grundlegenden Probleme mit seiner Programmiersprache hat.
Beitrag #5278225 wurde von einem Moderator gelöscht.
Beitrag #5278246 wurde von einem Moderator gelöscht.
Beitrag #5278248 wurde von einem Moderator gelöscht.
Beitrag #5278249 wurde von einem Moderator gelöscht.
Beitrag #5278250 wurde von einem Moderator gelöscht.
Beitrag #5278251 wurde von einem Moderator gelöscht.
Beitrag #5278252 wurde von einem Moderator gelöscht.
Beitrag #5278253 wurde von einem Moderator gelöscht.
Beitrag #5278255 wurde von einem Moderator gelöscht.
Beitrag #5278257 wurde von einem Moderator gelöscht.
Beitrag #5278259 wurde von einem Moderator gelöscht.
Beitrag #5278260 wurde von einem Moderator gelöscht.
Beitrag #5278261 wurde von einem Moderator gelöscht.
Beitrag #5278262 wurde von einem Moderator gelöscht.
Jörg W. schrieb: > Wenn ich einem 10seitigen Hochsprachprogramm > ein 50seitiges Assemblerprogramm gegenüberstelle (ungefähr darauf > wird's wohl hinauslaufen), dann habe ich auch eine (grob) fünfmal so > hohe Fehlerwahrscheinlichkeit Hier werden Äpfel (Hochsprache) mit Birnen (Assembler) verglichen! Wenn ein Assemblerprogramm 50 Seiten lang ist, dann werden die 10 Seiten Hochsprache in etwa 70 Seiten Assembler übersetzt werden, dazu auch noch schwer lesbar! Nicht selten erlebt man, daß Compiler die vielen Register nicht benutzen, stattdessen eine Stackorgie betrieben wird, daß es der Sau graust!
Scotty60 schrieb: > Nicht selten erlebt man, daß Compiler die vielen Register nicht > benutzen, stattdessen eine Stackorgie betrieben wird, daß es der Sau > graust! Solange du bei derart unbelegten pauschalisierten Aussagen bleibst, lohnt es sich nicht, weiter zu diskutieren. Es kann ja sein, dass du noch nie einen gut funktionierenden Compiler in den Fingern hattest, aber dann verallgemeinere doch bitte nicht von dem, was du kennst, auf moderne Compiler. ps: OK, wenn du einem Compiler natürlich die Optimierung untersagst, dann produziert er in der Tat den von dir hier dargelegten Codewust.
:
Bearbeitet durch Moderator
Beitrag #5278286 wurde von einem Moderator gelöscht.
Beitrag #5278287 wurde von einem Moderator gelöscht.
Beitrag #5278290 wurde von einem Moderator gelöscht.
Beitrag #5278291 wurde von einem Moderator gelöscht.
Beitrag #5278294 wurde von einem Moderator gelöscht.
Beitrag #5278295 wurde von einem Moderator gelöscht.
Beitrag #5278296 wurde von einem Moderator gelöscht.
Beitrag #5278297 wurde von einem Moderator gelöscht.
Beitrag #5278311 wurde von einem Moderator gelöscht.
Scotty60 schrieb: > Markus F. schrieb: >> Nun, um ehrlich zu sein bin ich nie auf die Idee gekommen, auch nur eine >> einzige Zeile ARM Thumb mehr als unbedingt notwendig "von Hand" zu >> schreiben. > > Marcus, ich kann dich absolut verstehen! Ich betreibe meine Liebe zu > Assembler auch nicht beruflich, und ich will auch keinen Religionskrieg > gegen Hochsprachen starten! Wenn man Assembler als Hobby betreibt, dann ist das völlig OK. Bei mir war es vor allem die Neugierde zu "wissen was die Welt, im Innersten zusammenhält", weil ich eben nicht mit Assembler, sondern mit C und anderen "Hochsprachen" aufgewachsen bin und wenn man die komplette Kette von der 1. Instruktion bis zum Aufruf von main() bei einem Cortex-M verstehen will, dann ist Assembler zumindest hilfreich. Program-Counter und Stack-Pointer waren für mich vormals komplett böhmische Dörfer. Den einzig sinnvollen Einsatz von Assembler, der mir bisher in freier Wildbahn begegnet ist, sind jedoch lediglich Teile von RTOS-Kerneln gewesen und damit meine ich auch nicht den kompletten Kernel, sondern typischerweise ist es eben dann nur der eigentliche context switch der bis auf die letzte Instruktion optimiert ist. Für alles andere ist C einfach praktikabler und nur weil man theoretisch besser sein kann als der Compiler, trifft das in der Praxis in gefühlt >99% der Fälle nicht zu. Ich kann dazu nur folgenden Vortrag von Matt Godbolt empfehlen: https://www.youtube.com/watch?v=bSkpMdDe4g4&t=1688s Die Pointe des Vortrags ist etwa zwischen Minute 28 und 32. Die Zeit davor ist lediglich ein x86-Assembler-Crashkurs aber auch sehr interessant.
:
Bearbeitet durch User
Jörg W. schrieb: > lohnt es sich nicht, weiter zu diskutieren. Stimmt! Wahrscheinlich hatten wir in der Automobilindustrie nur Mist verwendet gepaart mit Vollidioten von Ingenieuren.... Leider gab es damals noch nicht mikrocontroller.net, wo wir gelernt hätten, die Optimierung einzuschalten und bei der Variablendeklaration das Wort "register" zu verwenden, sarkasm off!
Christopher J. schrieb: > eile von RTOS-Kerneln gewesen und damit meine ich auch nicht den > kompletten Kernel, sondern typischerweise ist es eben dann nur der > eigentliche context switch der bis auf die letzte Instruktion optimiert > ist. Nicht nur optimiert: dort werden ja hart irgendwelce CPU-Ressourcen umgeschaltet (Stack & PC), da möchte man wohl in der Tat exakt hinschreiben, was zu tun ist. Ich glaube, der berühmte Kommentar im V6 UNIX "You are not supposed to understand this" bezog sich auch auf so eine Stelle. Dennis Ritchie hat im Netz eine Erklärung dafür hinterlassen.
Jörg W. schrieb: > Ich glaube, der berühmte Kommentar im V6 UNIX "You are not supposed > to understand this" bezog sich auch auf so eine Stelle. Dennis Ritchie > hat im Netz eine Erklärung dafür hinterlassen. Der Kommentar lautete m.W. präzise "You are not expected to understand this". Das wirklich spassige an der Sache ist aber, dass die ursprünglichen Programmierer es wohl auch nicht ganz verstanden haben. Im Nachgang stellte sich nämlich heraus, dass die besagte Stelle noch einen Bug hatte ;).
Markus F. schrieb: > Das wirklich spassige an der Sache ist aber, dass die ursprünglichen > Programmierer es wohl auch nicht ganz verstanden haben. Yep, genau deshalb haben sie den Kommentar so geschrieben. Einen Inline-Assembler hatten sie dazumals noch nicht.
Beitrag #5278361 wurde von einem Moderator gelöscht.
Den Kommentar "You are not expected to understand this" kannte ich noch nicht aber man lernt ja nie aus. Zum Glück gibt es aber relativ viel "Dokumentation" zu diesem Kommentar ;) Unter anderem https://thenewstack.io/not-expected-understand-explainer/ Und natürlich noch der Source-Code dazu: https://github.com/dspinellis/unix-history-repo/blob/Research-V6-Snapshot-Development/usr/sys/ken/slp.c#L318
:
Bearbeitet durch User
Beitrag #5278369 wurde von einem Moderator gelöscht.
Scotty60 schrieb: > Nicht selten erlebt man, daß Compiler die vielen Register > nicht benutzen, stattdessen eine Stackorgie betrieben wird, daß es der > Sau graust! Das ist für sicherheitskritische Anwendungen irrelevant, solange am Ende der Orgie stets das Richtige rauskommt. Die Entwicklungszeiten mit Assembler sind vor allem deswegen viel länger als etwa in C, weil das Debuggen viel länger dauert. Das dauert deswegen länger, weil mehr Fehler eingebaut werden, wenn es sich nicht um kleine Codestellen handelt. Wenn man initial mehr Fehler drin hat, ist die Wahrscheinlichkeit aber höher, daß am Ende davon noch welche drinbleiben. Selbst 100% code coverage hilft einem da nicht zwingend weiter. Und bei der mangelhaften Wartbarkeit des Codes ist man dann noch gar nicht angekommen. 10 Seiten C wird der nachfolgende Programmierer nämlich viel schneller verstehen als 50 Seiten Assembler.
Beitrag #5278374 wurde von einem Moderator gelöscht.
Beitrag #5278375 wurde von einem Moderator gelöscht.
Beitrag #5278378 wurde von einem Moderator gelöscht.
Beitrag #5278394 wurde von einem Moderator gelöscht.
Beitrag #5278412 wurde von einem Moderator gelöscht.
Nop schrieb: > 10 Seiten C wird der nachfolgende Programmierer > nämlich viel schneller verstehen als 50 Seiten Assembler. Nop, ich stimme dir uneingeschränkt zu, daß die Fehleranfälligkeit in Assembler deutlich höher ist, d'accord! Allerdings wird man bei hoch sicherheitskritischen Anwendungen sich sicherlich auch den Assemblercode anschauen, und dann werden aus den 10 Seiten C mindestens 70 Seiten Assembler (gegenüber 50 selbstgeschriebenen), der schwer zu verstehen ist, besonders bei den häufigen Stackorgien. Fakt ist nun mal, daß der erzeugte Assemblercode meistens schwerer zu verstehen ist als selbstkreierter, womit das Debuggen im Disassemblypart ganz lustig wird. Sicherlich wird man erst den Hochsprachenteil debuggen, aber beim schnellen Aufspüren des Fehlers schaut man sich auch den Assemblerteil an, um herauszufinden, hat der Compiler gepatzt oder hat man schlampig programmiert.
Christopher J. schrieb: > Ich hab dir mal ein absolut minimalistisches Blinky (für STM32) > angehängt, Hallo Christopher! Ich habe mal deine Files als Vorlage genommen, nun sieht das Disassembly schon verdammt gut aus, die Reihenfolge Vektoren - Programmcode stimmt jetzt, hab nochmals vielen Dank! Zwar sieht das Intel Hexfile schlecht aus, funktioniert nicht, aber das werde ich auch noch hinbekommen. Vermutlich der falsche Typ, da Intelhex nur 16 Bit Adressen kennt laut Wikipedia, auch wenn Atmel Studio das File klaglos nimmt und selber das Ihexformat erzeugt, zumindest beim SAMD10D14, dessen 16kb Flash Adressraum bei 0 beginnt. Das wahre Problem aber ist, wie bekomme ich das Zeug debuged? Wenn ich Atmel Studio kein compilierbares C File gebe, wird das Debugmenue deaktiviert. Mit dem Assemblerfile kann AS nichts anfangen. Ich befürchte, ich muss die die hier schon propagierte Lösung nehmen, C Projekt erstellen, die C-Dateien rausschmeißen und dann das Assemblerfile integrieren.... Manno, sind das Klimmzüge! Gruß Uwe
Naja, wenn du es richtig martialisch willst, dann nimmst du einfach nur den GDB-Server der zu deinem Debugger passt, also sehr wahrscheinlich OpenOCD oder den J-Link GDB-Server, öffnest dann deine .elf mit GDB, verbindest dich mit dem GDB-Server und debuggst das in der Kommandozeile bzw. im TUI. Alternativ kannst du eines der unzähligen GDB-Frontends verwenden, die entweder eigenständig sind, wie z.B. https://github.com/cs01/gdbgui oder andere, die wiederum in IDEs integriert sind, z.B. "Native Debug" für Visual Studio Code oder QT-Creator mit "Bare-Metal"-Plugin. Letzteres benutze ich selber. Das direkt in AS zu machen ist sicherlich die deutlich bequemere Variante. Am bequemsten ist es vermutlich alle C-Dateien einfach drin zu lassen und aus der main() einfach eine asm_main() aufzurufen, die du dann irgendwo in ASM implementierst. Dann hast du halt nicht mehr die absolute Kontrolle über den Startup aber bist vermutlich deutlich schneller bei einem funktionierenden Ergebnis.
Christopher J. schrieb: > wenn du es richtig martialisch willst, dann nimmst du einfach nur > den GDB-Server Auja, ich mag es martialisch! Allerdings muss ich vorher einen Fehler wegbekommen, der auch bei deinen unveränderten Files auftritt, nämlich es wird eine ungerade Startadresse in der Vektortabelle erzeugt, um 1 zu hoch, obwohl dein Programm an einer geraden, und durch 4 teilbaren Adresse beginnt. Diesen Fehler habe ich auch, der Rest stimmt in den Hexfiles, wobei bei deinem Hexfile der Eintrag des SPs fehlt, siehe Anhänge.
Uwe H. schrieb: > nämlich > es wird eine ungerade Startadresse in der Vektortabelle erzeugt, um 1 zu > hoch, Das ist korrekt und muss so. Da alle Instruktionen 2 oder 4 Byte lang sind, sind alle Adressen gerade, wodurch das unterste Bit nicht benötigt wird. Dies wird daher dazu verwendet, um anzugeben, ob beim Sprung an die jeweilige Adresse in den Thumb Mode (1) oder den ARM Mode (0) geschaltet werden soll. Da die Cortex-M nur Thumb können, muss das unterste Bit jeder Sprung-Adresse immer 1 sein, inkl. der Adressen im ISR-Vektor.
Dr. Sommer schrieb: > Uwe H. schrieb: >> nämlich >> es wird eine ungerade Startadresse in der Vektortabelle erzeugt, um 1 zu >> hoch, > > Das ist korrekt und muss so. Da alle Instruktionen 2 oder 4 Byte lang > sind, sind alle Adressen gerade, wodurch das unterste Bit nicht benötigt > wird. Dies wird daher dazu verwendet, um anzugeben, ob beim Sprung an > die jeweilige Adresse in den Thumb Mode (1) oder den ARM Mode (0) Gut zu sehen, wie weit die Vorbereitung auf ARM-Assembler-Programmierung schon gediehen ist.
Oha, danke Dr. Sommer, das wußte ich echt nicht, was sind die Dinger komplex!!!
Dr. Sommer, gilt diese Regel mit dem +1 nur für den Resetvektor?
Nop schrieb: > Gut zu sehen, wie weit die Vorbereitung auf ARM-Assembler-Programmierung > schon gediehen ist. Hehehe! Uwe H. schrieb: > Oha, danke Dr. Sommer, das wußte ich echt nicht, was sind die > Dinger > komplex!!! Ja, und deswegen nutzt man auch Hochsprachen... Uwe H. schrieb: > Dr. Sommer, gilt diese Regel mit dem +1 nur für den Resetvektor? Nein, auch für Sprünge/Funktionsaufrufe, wenn die "B[L]X" Instruktionen genutzt werden (X= eXchange, aktiviert die Modus-Umschaltung). Bei "B" wird das Bit ignoriert. Der Linker fügt das +1 automatisch hinzu wenn du Sprungmarken/Labels nutzt.
Der Beispiel-Assembler-Code ist zwar sehr kurz, aber zeigt, daß nicht alles was man in der Sprache schreibt gut sein muß. Der ARM braucht keine händischen Adressrechnungen, um die verschiedenen Register eines IO-Bausteins anzusprechen.
Du meinst sicher solche Stellen wie
1 | ldr R1, =0x40021000 // = RCC_APB2 Base |
2 | add R1, #0x18 // add offset of APB2ENR to R1 |
3 | // The last two steps are the same as ldr R1, =0x40021018 |
4 | |
5 | ldr R1, =0x44244444 // Configure PIN13 as PP output @ 2MHz |
6 | |
7 | str R1, [R0] // Write config to GPIOC_CRH |
Die überflüssigen adds sind tatsächlich quasi sinnfreies Padding. Ich wollte als ich das geschrieben hatte, das die Größe des Binärprogramms in Byte genau durch 42 teilbar ist. Eine Anspielung darauf steht sogar ganz oben in der ersten Zeile :D Ne, im ernst: Der Code war nie dazu gedacht einen Schönheitspreis zu gewinnen. Man könnte das sicher viel eleganter lösen und die Größe auf ~60 Byte bekommen. Das war aber im Prinzip nie das Ziel von diesem Programm, sondern es ging mir persönlich nur darum einmal ein sehr minimales Programm in Assembler geschrieben zu haben und naja, es funktioniert sogar, d.h. die LED blinkt.
Uwe H. schrieb: > nämlich es wird eine ungerade Startadresse in der Vektortabelle erzeugt Eine gerade Adresse führt sofort zu einem HardFault.
Uwe H. schrieb: > Diesen Fehler habe ich auch, der Rest stimmt in den > Hexfiles, wobei bei deinem Hexfile der Eintrag des SPs fehlt, siehe > Anhänge. Ich habe nicht so den Plan von Hexfiles aber wenn ich dein Hexfile nehme und es mittels Objcopy in ein Binary umwandle kommt exakt das gleiche Binary heraus, wie wenn ich das Binary aus meiner .elf erstelle. Die zweite Zeile des Hex-Files erinnert auch verdächtig an das Binary: .hex
1 | :1020000000500020092000084FF001004FEA0010A6 |
.bin
1 | 00000000: 0050 0020 0920 0008 4ff0 0100 4fea 0010 .P. . ..O...O... |
Ich frage mich allerdings sowieso wofür man bei den ARM-Controllern noch das hex-Format benutzen sollte. Irgendwelche low-level Bootloader wollen dann doch lieber direkt das Binary und für alles andere benutze ich das elf-Format.
Hallo Christopher! Ich hatte mal dein super dokumentiertes Beispiel total umgeschrieben. Kein Wunder, STM und SAMD10D14 sind total anders von der Peripherie. Auch hast du bei deinem M3 einen viel größeren Befehlssatz, den mein M0+ nicht versteht, bei vielen Befehlen gehörte noch das 'S' dran, etwa bei lsl, sub, mov. Ja, der M0+ ist total auf kleines Silizium getrimmt, damit der Strom spart auf Teufel komm raus. Ergebnis: Der Blinker funktioniert, super!!! Allerdings ist es mir ein Rätsel, warum er funktioniert. :-) Du verwendest bei den 32 Bit Immediate Ladebefehlen die Pseudoin-struktion LDR rx,=0x......... Du hättest auch Mov32 verwenden können, die dann in Movew und movt umgewandelt wird. Ok, du bist schneller mit dieser literalen Adressierung, gut. Jedenfalls wird dein LDR Befehl erwartungsgemäß in eine PC relative Adressierung zerlegt, nichts Aufregendes. Dann werden diese 32 Bit Immediates abgelegt im literal pool, auch ok. Aber was da abgelegt wird, verstehe ich nicht. Konkret:
1 | ldr r0, =0x41004400 // DIR Register |
2 | movs r1,#0x80 |
3 | strb r1,[r0,#2] @PA23=output |
4 | ldr r0, =0x4100441c // set the pointer to OUTTGL |
5 | |
6 | strb r1,[r0,#2] @toggle PA23 |
7 | ldr r3,=#500000 //delay |
8 | delay: subs R3 , #1 |
9 | bne delay // branch (goto) "delay" if not equal |
10 | b toggle // goto "toggle" |
Der Disassembler zeigt mir folgendes Szenario an:
1 | 00000008 <gpioconfig>: |
2 | 8: 4804 ldr r0, [pc, #16] ; (1c <delay+0x8>) |
3 | a: 2180 movs r1, #128 ; 0x80 |
4 | c: 7081 strb r1, [r0, #2] |
5 | e: 4804 ldr r0, [pc, #16] ; (20 <delay+0xc>) |
6 | |
7 | 00000010 <toggle>: |
8 | 10: 7081 strb r1, [r0, #2] |
9 | 12: 4b04 ldr r3, [pc, #16] ; (24 <delay+0x10>) |
10 | |
11 | 00000014 <delay>: |
12 | 14: 3b01 subs r3, #1 |
13 | 16: d1fd bne.n 14 <delay> |
14 | 18: e7fa b.n 10 <toggle> |
15 | |
16 | 1a: 44000000 strmi r0, [r0], #-0 |
17 | 1e: 441c4100 ldrmi r4, [ip], #-256 ; 0xffffff00 |
18 | 22: a1204100 ; <UNDEFINED> instruction: |
Aus den Immediates 0x41004400, 0x4100441c, 500000 macht er 0x44000000, 0x441c4100 0xa1204100 Theoretisch müsste die CPU auf die falschen Adressen zugreifen, sprich die GPIO Ports um Meilen verfehlen, was aber nicht der Fall ist. Ich erbitte Aufklärung, da ich bei Tante Google nichts zur Verschlüsselung finde, da sieht es nämlich logisch aus, etwa bei http://www.eng.auburn.edu/~nelson/courses/elec2220/slides/ARM%20prog%20model%202%20addressing.pdf.
:
Bearbeitet durch Moderator
Uwe H. schrieb: > Aus den Immediates 0x41004400, 0x4100441c, 500000 macht er > 0x44000000, 0x441c4100 0xa1204100 Nö... Schau dir mal ganz genau die Adressen an, die geladen werden. Der Disassembler fasst die Bytes "falsch " zu 32bit Words zusammen (bzw. anders als der Assembler). Wie disassemblierst Du?
Hallo Dr. Sommer! Dr. Sommer schrieb: > Wie disassemblierst Du? Diese Frage verstehe ich nicht! Das File Format ist elf32-littlearm, der Befehl lautet arm-none-eabi-objdump -D -S miniblink.elf >disassembly.txt. Zur Zeit kann ich noch keine reinen Assemblerprogramme debuggen, muss mir da noch etwas installieren. Aber auf jeden Fall stimmen die geholten Adressen aus dem literal pool, denn sonst würde die CPU nicht das OUTTGL Register treffen. Mit dem falsch zusammenfassen Bytes gebe ich dir überwiegend Recht, man könnte fast alles zusammen puzzlen, allerdings fehlt eine Zahl, denn 500000 =0x7A120, und da fehlt nur noch die 7......
Uwe H. schrieb: > Diese Frage verstehe ich nicht! Das File Format ist elf32-littlearm, der > Befehl lautet arm-none-eabi-objdump -D -S miniblink.elf >>disassembly.txt. Aber die Antwort ist richtig :D "-d" find ich schöner, weil er dann nicht versucht die Daten zu disassemblieren. Uwe H. schrieb: > Zur Zeit kann ich noch keine reinen Assemblerprogramme debuggen, muss > mir da noch etwas installieren. Wer C debuggen kann, kann auch Assembler debuggen. Uwe H. schrieb: > allerdings fehlt eine Zahl, denn > 500000 =0x7A120, und da fehlt nur noch die 7...... Ja, die 7 hast du nicht mit kopiert, die muss da aber noch folgen. Offenbar schreibt der Assembler zwei Dummy-0-Bytes in das Binary, damit die Words 4-Byte-Aligned sind. Schreib einfach mal ein "nop" ans Ende von "delay", welches dann die Dummy-Bytes ersetzt. Dann zeigt der Disassembler die Words richtig an. Der GCC macht genau das beim Kompilieren von C-Code anscheinend automatisch. Vielleicht reicht ja "-d" statt "-D" auch um das richtig anzuzeigen.
PS: Ich habs grad mal selber ausprobiert. Das hier
1 | .thumb |
2 | |
3 | foo: |
4 | ldr r0, =0x01234567 |
Assemblisiert und so wieder disassembliert
1 | arm-none-eabi-as test.S -o test.o && arm-none-eabi-objdump -d test.o |
ergibt
1 | Disassembly of section .text: |
2 | |
3 | 00000000 <foo>: |
4 | 0: 4800 ldr r0, [pc, #0] ; (4 <foo+0x4>) |
5 | 2: 0000 .short 0x0000 |
6 | 4: 01234567 .word 0x01234567 |
Also korrekte Darstellung, die Füll-Bytes werden korrekt separat gezeigt. Was machst du anders?
Dr. Sommer schrieb: > Schreib einfach mal ein "nop" ans Ende von "delay" Sollte man ohnehin viel öfter machen. ^^
Nop schrieb: > Sollte man ohnehin viel öfter machen. ^^ Einfach mal Pause machen, und sich 1/SYSCLK lang Ruhe gönnen.
Dr. Sommer schrieb: > "-d" find ich schöner Stimmt total! Jetzt kommt ein lupenreines Disassambyl listing heraus, die Adressen stimmen, danke dir für den Tipp!!! Mit dem Alignment nop konnte ich mir daher ersparen: Disassembly of section .text: 00000000 <min_vectors>: 0: 20000200 .word 0x20000200 4: 00000009 .word 0x00000009 00000008 <gpioconfig>: 8: 4804 ldr r0, [pc, #16] ; (1c <delay+0x8>) a: 2180 movs r1, #128 ; 0x80 c: 7081 strb r1, [r0, #2] e: 4804 ldr r0, [pc, #16] ; (20 <delay+0xc>) 00000010 <toggle>: 10: 7081 strb r1, [r0, #2] 12: 4b04 ldr r3, [pc, #16] ; (24 <delay+0x10>) 00000014 <delay>: 14: 3b01 subs r3, #1 16: d1fd bne.n 14 <delay> 18: e7fa b.n 10 <toggle> 1a: 0000 .short 0x0000 1c: 41004400 .word 0x41004400 20: 4100441c .word 0x4100441c 24: 0007a120 .word 0x0007a120 Zum Schluss möchte ich Allen hier danken für ihre Hilfe, besonders Christopher für das Lernbeispiel!!! Diese "Anschiebehilfe" war wohl notwendig, denn mit den ARMs habe ich mir echt etwas Kompliziertes angelacht, dagegen war die 68000 Familie, Freescale (nun NXP, ich weiß) und AVR Spielzeug. Aber es macht Spaß. Naja, um ARM kommt man nicht herum, sind bei uCs wohl schon seit Jahren Marktführer.
Uwe H. schrieb: > Stimmt total! Jetzt kommt ein lupenreines Disassambyl Wenn du jetzt noch lokale Sprungmarken verwendest, werden auch deine Funktionen nicht mehr zerpflückt... dafür ist der Quellcode dann weniger gut lesbar.
Dr. Sommer schrieb: > Wer C debuggen kann, kann auch Assembler debuggen Vom persönlichen Können ja. Aber ich kann Atmel Studio kein reines Assemblerprogramm unterschieben, ich muss immer ein C File (main()) als trojanisches Pferd mitliefern!
z.B. so:
1 | .syntax unified |
2 | .cpu cortex-m0plus |
3 | .thumb |
4 | |
5 | .type function1, %function |
6 | function1: |
7 | ldr r0, =0x41004400 // DIR Register |
8 | movs r1,#0x80 |
9 | strb r1,[r0,#2] @PA23=output |
10 | ldr r0, =0x4100441c // set the pointer to OUTTGL |
11 | |
12 | 1: |
13 | strb r1,[r0,#2] @toggle PA23 |
14 | ldr r3,=#500000 //delay |
15 | 2: subs R3 , #1 |
16 | bne 2b // branch (goto) "delay" if not equal |
17 | b 1b // goto "toggle" |
18 | |
19 | bx lr |
20 | .pool |
21 | |
22 | .type function1, %function |
23 | function2: |
24 | 1: |
25 | b 1b |
Dann sind in der Disassembly die Funktionen am Stück:
1 | Disassembly of section .text: |
2 | |
3 | 00000000 <function1>: |
4 | 0: 4804 ldr r0, [pc, #16] ; (14 <function1+0x14>) |
5 | 2: 2180 movs r1, #128 ; 0x80 |
6 | 4: 7081 strb r1, [r0, #2] |
7 | 6: 4804 ldr r0, [pc, #16] ; (18 <function1+0x18>) |
8 | 8: 7081 strb r1, [r0, #2] |
9 | a: 4b04 ldr r3, [pc, #16] ; (1c <function1+0x1c>) |
10 | c: 3b01 subs r3, #1 |
11 | e: d1fd bne.n c <function1+0xc> |
12 | 10: e7fa b.n 8 <function1+0x8> |
13 | 12: 4770 bx lr |
14 | 14: 41004400 .word 0x41004400 |
15 | 18: 4100441c .word 0x4100441c |
16 | 1c: 0007a120 .word 0x0007a120 |
17 | |
18 | 00000020 <function2>: |
19 | 20: e7fe b.n 20 <function2> |
20 | 22: 46c0 nop ; (mov r8, r8) |
Ohne das ".pool" werden Constant Pools hier ganz ans Ende gepackt, was zwar nicht schlimm ist aber etwas komisch aussieht. Uwe H. schrieb: > ich muss immer ein C File (main()) als > trojanisches Pferd mitliefern! Glaube ich kaum, geht bestimmt irgendwie. Mit Atmel Studio hab ich aber keine Erfahrung, ich benutze meistens eclipse, da ist das kein Problem.
Dr. Sommer schrieb: >> ich muss immer ein C File (main()) als >> trojanisches Pferd mitliefern! > Glaube ich kaum, geht bestimmt irgendwie. Die haben für Assemblerprojekte beim ARM einfach mal keine Vorlage. Wird vermutlich so gut wie nie nachgefragt …
Jörg W. schrieb: > Die haben für Assemblerprojekte beim ARM einfach mal keine Vorlage. Das klingt schon wahrscheinlicher. Aber woher die ELF-Datei kommt sollte doch den Debugger nicht interessieren.
Dr. Sommer schrieb: > Aber woher die ELF-Datei kommt sollte doch den Debugger nicht > interessieren. Vermutlich bekommt man den Studio-eigenen Debugger jedoch nur angeworfen, wenn man ihm ein Projekt aufsetzt, und das will er dann natürlich erstmal „bauen“, bevor er den Debugger anwirft … Vermutlich könnte man sich nun hinsetzen und schauen, wie man ihm ein ARM-Assemblerprojekt definiert, aber man könnte seine Zeit auch mit anderen Dingen verbringen.
Jörg W. schrieb: > und das will er dann natürlich erstmal „bauen“, bevor er den Debugger > anwirft … Soll er doch... Man kann doch wohl ein Projekt nur aus .S Dateien machen (zB durch Löschen der C Dateien) und von Atmel Studio builden lassen?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.