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