Hallo, es wurde letzte Woche in einem Beitrag erwähnt, die obere Grenze des RAM kann durch den Befehl "-minit-stack=N" festgelegt werden. Würde das Gerne mal ausprobieren. Doch wo leg ich diese Grenze fest? Im Code, Makefile? Ich benutze das AVR-Studio mit nem Atmega 88 und programiere in C. Eine kleine Hilfestellung wäre echt super! Vielen Dank
Hi, ich würds jetz einfach mal im Makefile zu den CFLAGS hinzufügen: CFLAGS += -minit-stack=N
Gibt es dann da irgendeinen default Wert für N? Bräuchte ca. 3 Byte die ich als eigenen Speicher nutzen kann und die nicht nach einem Reset durch den Watchdog gelöscht werden....
N ist die obere Speichergrenze. Also ins Datasheet reinschauen wo die liegt, und davon 4 Bytes abziehen.
>> Ich glaub 40. Wie kommst du auf 40? >> Also ins Datasheet reinschauen wo die liegt, Was genau stellt denn die obere Grenze da. Meine 1024 Byte? Also der Atmega88 hat 1024 Byte RAM. Ist das gleichzusetzen mit dem N oder entspricht N der Hexzahl aus dem Datenblatt? Siehe Anhang?
Gut, hab jetzt im Makefile stehen: CFLAGS += -minit-stack=0x04FA Hab also sozusagen 4 Bytes freigemacht. Wie kann ich im Programm nun auf diese 4 Bytes zugreifen?
1 | struct DeinSpeicher { |
2 | ...deine 3 Bytes... |
3 | } *ptrDeinSpeicher = (struct DeinSpeicher *)0x4FC; |
Supi, Danke! Also hab ich mir damit: struct RAM_Speicher{ unsigned char Byte_1; unsigned char Byte_2; unsigned char Byte_3; }*RAM = (struct RAM_Speicher*) 0x4FC; einen Zeiger RAM auf die Variablen Byte_1 bis 3 erzeugt die sich im Speicherbereich 0x4FC bis 0x4FE befinden. Richtig? Arbeite zum ersten mal mit Pointern.... Kann ich jetzt ganz normal über z.B. *RAM.Byte_1 = 50; auf die Speicherstellen zugreifen, oder gibt es da noch irgendetwas zu beachten.
Fast. Bei *RAM.Byte_1 passen die Prioritäten nicht [läuft auf *(RAM.Byte_1) raus]. Eher schon RAM->Byte_1.
Timo wrote: > Bräuchte ca. 3 Byte die ich als eigenen Speicher nutzen kann und die > nicht nach einem Reset durch den Watchdog gelöscht werden.... Nimm die .noinit-Section dafür.
Timo wrote: >> Nimm die .noinit-Section dafür. > > Was ist die .noinit-Section? RTFM: http://www.nongnu.org/avr-libc/user-manual/mem_sections.html#sec_dot_noinit
Ließt sich ja ganz gut! Das steht aber im dritten Punkt dass ich die "noinit section" an eine bestimmte Stelle ins RAM legen sollte, das wäre von Vorteil. Wo kann ich das Definieren? Also dies Zeile: $ avr-gcc ... -Wl,--section-start=.noinit=0x802000 ... Im Makefile?
Nein, das ist nicht irgendwie von Vorteil, du kannst es nur tun. Wenn du nichts angibst, landet .noinit hinter dem normalen .data und .bss.
Wo könnte ich das denn tun? Bekomme nämlich immer die Fehlermeldung, dass ich etwas anderes dort überschreibe......
Naja, wenn du da was anderes überschreibst, wird das wohl auch wahr sein, oder? Die Frage ist: warum willst du das denn explizit zuweisen? Die implizite Belegung ist doch sinnvoller, da sie automatisch die dichteste Packung erzeugt. Eine explizite Zuweisung braucht man eigentlich nur, wenn man auf diese Weise z. B. Daten vom Bootlader zur Applikation rüberreichen möchte. Ansonsten musst du schon ein wenig mehr von deinem Fehlerbild schreiben.
> Naja, wenn du da was anderes überschreibst, wird das wohl auch wahr > sein, oder? Schon richtig, daher möchte ich ja Wissen wo ich diese expliziete Zuweisung machen kann. Ansonsten überschreibe ich ja anscheinend einen Bereich.
Da der Linker die Aufteilung auf die Adressbereiche vornimmt, muss man sowas auf der Linker-Kommandozeile machen. Da man diese normalerweise dem GCC überlässt, übergibt man das selbigen beim Linken mit -Wl, . Nein, ich denke, das hast du alles bereits korrekt gemacht, daran liegt es nicht. Der Fehler ist woanders, aber da du uns nur kleine Bröckchen vorsetzt und meine Kristallkugel bei dem trüben Wetter auch leider nicht richtig arbeitet, kann dir gerade niemand richtig sagen, was du denn flasch gemacht hast.
Also diese explizite Zuweisung brauche ich, damit die Werte die an diesen Stellen liegen, nach einem Watchdog Reset nicht gelöscht werden. Nun noch mal zu meinem Problem wie oben beschrieben: > Bräuchte ca. 3 Byte die ich als eigenen Speicher nutzen kann und die > nicht nach einem Reset durch den Watchdog gelöscht werden.... Da hab ich zuerst die obere Stackgrenze des Speichers mit CFLAGS += -minit-stack=0x04FA im Makefile runter gesetzt und mir ein Struktur im Code der folgenden Art erzeugt: struct RAM_Speicher{ unsigned char Byte_1; unsigned char Byte_2; unsigned char Byte_3; }*RAM = (struct RAM_Speicher*) 0x4FC; Danach müssten doch die drei Byte im Bereich 0x4FC bis 0x4FE liegen und ich kann durch RAM->Byte_1 = bla bla; auf diese Stellen zugreifen. Dabei tritt aber der Fehler auf, dass ich Probleme beim Compilieren bekomen. Sprich, das AVR-Studio bleibt hängen. Dann hattest du von einer anderen Möglichkeit berichtet, die mit der .noinit-Section. Doch dabei tritt das folgende Problem auf: Ich deklariere mir eine Variable "ram" nach .noinint: volatile uc_8 ram _attribute_ ((section (".noinit"))); Hab aber zusätzlich im Code noch ein Array das folgendermaßen deklariert ist: volatile uc_8 rf_buffer_rx[32]__attribute__((section(".buffer_rx"))); Nun bekomme ich beim compilieren diese Fehlermeldung: C:\Programme\WinAVR\bin\..\lib\gcc\avr\3.4.5\..\..\..\..\avr\bin\ld.exe: section .noinit [00800108 -> 00800108] overlaps section .buffer_rx [00800100 -> 0080011f] So, das ist im Moment mein Problem. Vielleicht weiß ja jemand ne Lösung.... Gruß
Timo wrote: > Also diese explizite Zuweisung brauche ich, damit die Werte die an > diesen Stellen liegen, nach einem Watchdog Reset nicht gelöscht > werden. Naja, das macht .noinit ganz von allein, und ganz ohne feste Zuweisung einer Adresse. > Da hab ich zuerst die obere Stackgrenze des Speichers mit > CFLAGS += -minit-stack=0x04FA 0x4fb müsste es auch noch tun, eigentlich sogar 0x4fc, denn die drei Bytes brauchen nur 0x4fd, 0x4fe und 0x4ff. > Dabei tritt aber der Fehler auf, dass ich Probleme beim Compilieren > bekomen. Sprich, das AVR-Studio bleibt hängen. Der Compiler tut's aber ordentlich, und der resultierende Code würde (meiner Meinung nach, wenn du nicht noch was anderes vergeigt hast) funktionieren. Wenn AVR Studio da irgendwelchen Mist macht: sorry. Musst du denen einen Bugreport schreiben. > Hab aber zusätzlich im Code noch ein Array das folgendermaßen > deklariert ist: > volatile uc_8 rf_buffer_rx[32]__attribute__((section(".buffer_rx"))); Wofür ist das denn gut, und wie weist du dessen Adresse zu? Ich glaube mich zu erinnern, dass man zusätzliche, nicht von vornherein im Linkerscript bekannte sections nur im ROM anlegen kann. Für zusätzliche sections im RAM muss man mit einem eigenen Linkerscript arbeiten. Ganz davon abgesehen: die section .buffer_rx wird (je nach der konkreten Gestaltung deines Linkerscripts) ohnehin nicht von der Initialisierung erfasst, also kannst du eigentlich auch deine drei Statusbytes dort mit reinlegen. Es ist aber ziemlich mühselig, dir hier jedes Detail aus der Nase ziehen zu müssen.
Hi Jörg, als ich habe gerade festgestellt, dass die Adresse des buffers im Makefile fest vorgeschrieben ist. Daher dann wohl auch das Problem mit der Überschneidung. Werd mal die Adressen anpassen und .noinit auch einen festen Startwert zuweisen. wie kann ich das makefile denn ändern? Mit nem normalen Editor werden die Änderungen irgendwie nicht übernommen.....
Timo wrote: > wie kann ich das makefile denn ändern? Genauso, wie du das initial da reinbekommen hast. > Mit nem normalen Editor > werden die Änderungen irgendwie nicht übernommen..... Dann musst du deine Build-Umgebung bzw. dessen Manual befragen. Bei mir werden Makefiles (fast) nur mit 'nem normalen Editor bearbeitet. (Ausnahme: für das initiale Makefile eines neuen Projekts nehme ich auch gern mal Mfile.)
Haut bei mir nicht hin! Sobald ich das Projekt mit dem neuen Makefile compiliere, werden die Änderungen verworfen. Also das Makefile sieht wieder genauso aus wie vorher. Hängt das Makefile vielleicht noch mit ner anderen Datei zusammen, die bei der ersten Initialisierung erzeugt wird?
Nimmst du AVR Studio? Dann musst du dem Teil beibringen, dass du ein externes Makefile benutzt. Allerdings frage ich mich dann, wie du die Adresse deiner extra section rx_buffer da reinbekommen hast...
>Hängt das Makefile vielleicht noch mit ner anderen Datei zusammen, die >bei der ersten Initialisierung erzeugt wird? AVRStudio erzeugt das makefile immer neu. Das lässt sich nicht beeinflussen, und ein (editierbares) Template gibt es auch nicht. Es gibt nur zwei Möglichkeiten: - Du gibts deine zusäzuliche Compiler-/Linkeroptionen unter Project/Options an, dann werde die ins makefile geschrieben. Nur da kann eigentlich auch die feste Adresse für die ominöse buffer_rx-section herkommen. (Was bedeutet, daß du das Projekt nicht selber erstellt hast...) - Du exportierst das makefile einmalig, und definierst das dann als externes makefile. Dann kannst (und musst) du beliebig per Texteditor drin rumändern. Automatische Anpassungen, z.B. bei hinzufügen einer weiteren Source-Dateiu, gibt es dann nicht mehr. Nach wie vor ist mir aber nicht klar, warum du unbedingt eine feste Adress für die .noinit-section brauchst. Und wofür ist die buffer_rx-section? Hast du zusätzliches externes Ram am Prozesor? Oliver
Hey Oliver, das mit der ersten Möglichkeit funktioniert. Und wie du schon sagst ist dort auch diese ominöse rx_section definiert. Wofür diese rx_section ist, kann ich dir gar nicht so genau sagen. Dort befinden sich auf jeden Fall die über UART empfangenen Daten. Wenn ich diese feste Adresse für die rx_section weglasse, funktioniert das Programm nicht mehr. Nun hab ich dort auch für die noinit_section einen festen Bereich definiert und eine Variable erzeugt. volatile uc_8 Test3 _attribute_ ((section (".noinit"))); Wenn ich keinen festen Bereich für noinit definiere, behagen sich noinit und rx. Mit nem festen Bereich kann ich das Programm kompilieren und auch zum laufen bringen. Jedoch ist der Wert den die Variable Test3 im Programm zugewiesen bekommen hat nach einem Watchdog nicht mehr vorhanden. Hab ich da vielleicht noch was vergessen?
> Wenn ich > diese feste Adresse für die rx_section weglasse, funktioniert das > Programm nicht mehr. Sieht so aus, als hätte da jemand die letzten drei Bytes optimiert, die man für eine vernünftige Zeigerarithmetik gebraucht hätte. Durch das Binden von .rx_buffer auf eine 256er-Grenze braucht man für einen 256-Byte-Puffer dann nur das Lowbyte des Zeigers zu behandeln. Das ist ,,trickreiche Programmierung'' im schlimmsten Sinne, wie man sie zu besten Z80-Zeiten gemacht hat, um tatsächlich noch mit dem letzten Byte auszukommen. Würde ich auf einem AVR nicht machen, wenn es nicht wirklich zwingend notwendig ist (es also auf genau die paar Bytes Code oder die halbe Mikrosekunde Zeiteinsparung ankommt). Dann leg' doch deine drei Statusvariablen noch in die .rx_buffer Section mit rein. Du musst dann nur gucken, ob sie vor oder hinter dem rx_buffer landen. Wenn sie davon landen, musst du den Anfang dieser section drei Bytes vorziehen. Alternativ kannst du wahrscheinlich auch noch eine Vorgabe für .noinit mit unterbringen, z. B. genau vor dem rx_buffer oder genau danach. Aber ich würde das alles so umschreiben, dass man keine bestimmte vorgegebene Adresse für rx_buffer braucht, diese section dann ganz eliminieren, und mit .noinit ohne weitere Parametrierung arbeiten.
> Sieht so aus, als hätte da jemand die letzten drei Bytes optimiert, > die man für eine vernünftige Zeigerarithmetik gebraucht hätte. Durch > das Binden von .rx_buffer auf eine 256er-Grenze braucht man für einen > 256-Byte-Puffer dann nur das Lowbyte des Zeigers zu behandeln. Versteh ich nicht ganz. > Würde ich auf einem AVR nicht machen, wenn > es nicht wirklich zwingend notwendig ist (es also auf genau die paar > Bytes Code oder die halbe Mikrosekunde Zeiteinsparung ankommt). Denke das ist der Haken. Der Code regelt die Datenübertragung per Funk zwischen zwei AVR's. Da ist schon alles ziemlich Zeitkritisch. Hab gerade auch festgestellt, dass da noch mehr Sachen sind, die feste Adressen im RAM zugewiesen bekommen haben. aber wie kann denn die feste Adresse des rx_Buffers im RAM mein Programm beeinflussen? Das versteh ich nicht wirklich. Weil wenn ich sie ändere oder weglasse, funktioniert nix mehr. Hab mal die Speichereinstellungen aus dem AVR Studio angehangen. Aber sagen wir mal die Einstellungen müssen so sein. Ob das so gut ist oder nicht sei mal hinten angestellt. Dann bleibt mir ja nichts anderes übrig als dem noinit auch einen festen Speicherbereich zuzuweisen. Das hab ich einfach mal gemacht. Doch warum behält die Variable, die ich als noinit deklariert habe, nicht ihren Wert?
Timo wrote: >> Sieht so aus, als hätte da jemand die letzten drei Bytes optimiert, >> die man für eine vernünftige Zeigerarithmetik gebraucht >> hätte. Durch das Binden von .rx_buffer auf eine 256er-Grenze >> braucht man für einen 256-Byte-Puffer dann nur das Lowbyte des >> Zeigers zu behandeln. > Versteh ich nicht ganz. Dann guck dir die Puffer-Behandlung an. Sorry, du hast den Code, wir nicht. Wenn du fremder Leute Code benutzt, solltest du ihn zumindest ansatzweise verstanden haben. > Denke das ist der Haken. Der Code regelt die Datenübertragung per > Funk zwischen zwei AVR's. Da ist schon alles ziemlich Zeitkritisch. Ich mach' hier auch den ganzen lieben langen Tag lang Funkübertragung mit AVRs. ;-) Daher glaube ich dir das einfach mal nicht, dass es auf diese Mikrosekunde noch ankommt. Ich habe hier die ACK-Zeit eines IEEE-802.15.4-Stacks mal von eingangs knapp 700 µs auf die vom Standard geforderten 192 µs optimiert, und dabei keine Zeile in Assembler verfasst (hätte sowieso bei der Komplexität des Codes nichts gebracht), noch wäre ich auf die Idee gekommen, derartig schräge Hacks zu benutzen. > aber wie kann denn die feste Adresse des rx_Buffers im RAM mein > Programm beeinflussen? Das versteh ich nicht wirklich. Ja, du solltest das Projekt mitsamt seinen Haken und Ösen schon mal vor der Benutzung verstanden haben. Wenn du das Speicher-Layout dir dann mal verinnerlicht hast, weißt du auch, wo noch Platz für dein .noinit ist (vermutlich vor dem ganzen rx_buffer-Geraffel, ich denke, dass da einige Bytes unbenutzt sind). Den musst du dann nur noch so legen, dass er nicht gerade von __do_clear_bss und __do_copy_data erfasst wird (das sind Funktionen des Compilers, die siehst du zumindest im .lss).
Den Platz für die Bytes hab ich wohl gefunden. Wo kann ich denn die Adressen von __do_clear_bss und __do_copy_data ansehen? Meine Variable wird nämlich immer noch überschrieben.
Timo wrote: > Wo kann ich denn die Adressen von __do_clear_bss und __do_copy_data > ansehen? Meine Variable wird nämlich immer noch überschrieben. Da das .lss-File ein Dissassembler-Listing ist, solltest du dort die tatsächlichen Werte drin finden. Kurz gesagt: __do_clear_bss nullt alles von __bss_start bis kleiner __bss_end aus (diese beiden Symbole werden vom Linker eingesetzt). __do_copy_data kopiert die Daten aus dem ROM ab __data_load_start in den RAM ab __data_start bis unterhalb __data_end. Wiederum werden die genannten Symbole vom Linker eingetragen. Die Werte aller Symbole solltest du in der Symboltabelle (.sym) finden.
Hab mir gerade mal ein kleines Testprogramm geschrieben. Da funktioniert das mit der noinit Initialisierung. Doch bei dem anderen Code haut das nicht hin. Die Variable wird nach dem Watchdog durch irgendetwas überschrieben.
Jetzt klappt es! Super, vielen Dank Jörg! Der Wert blieb auch noch nach dem Watchdog erhalten, wurde aber von folgendem Code bei der Initialisierung gelöscht: i = input(SPL); i ^= 0xFF; extern ui_16 __bss_start; extern ui_16 __stack; uc_8 *ptr = (uc_8*)&__bss_start; while(ptr<(uc_8*)&__stack - i){/*sub because of returning of subroutine*/ *ptr++=0; } Werd jetzt erst einmal herausfinden, was diese Codestück genau macht. Vielleicht weißt du es ja? Anscheinend werden die die Inhalte der bss_start- und stack-Adressen auf 0 gesetzt. Hab noch ne Frage, wo genau finde ich im AVR Studio diese Symboltabelle(.sym)?
Ick, ich würde diesem Projekt nicht wirklich übern Weg trauen. Die reimplementieren offenbar Teile dessen, was normalerweise vom C-Startup-Code ausgeführt wird. > Hab noch ne Frage, wo genau finde ich im AVR Studio diese > Symboltabelle(.sym)? Ich benutze kein AVR Studio. Beim normalen WinAVR-Makefile-Template wird diese Datei automatisch bei »make all« mit angelegt. Wenn AVR Studio's Makefile das nicht tut, kannst du dir die Symboltabelle jederzeit mit dem Kommando
1 | avr-nm <name of your ELF file> |
angucken.
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.