Forum: Compiler & IDEs Check Konstante Adresse Flash / Compiler / Linker


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Kai (kai_tl91)


Lesenswert?

Hallo zusammen,

ich suche nach einer Möglichkeit eine Konstante bzw. Variable direkt im 
C-Code (Notfalls auch im Linkerscript) an eine bestimmte Flashadresse zu 
speichern. Zum einen soll in alle Codes immer an der selben Flashadresse 
eine Seriennummer stehen und zum Anderen eine 32Bit Zahl ganz am Ende 
des Flashspeichers. Die "Prüfzahl" soll später vom Bootloader ausgelesen 
werden um sicherzustellen, dass bei einer Stromunterbrechung wären einem 
Update die Firmware komplett bis zum Ende geschrieben wurde.

Wer hat eine Idee, wie sich dies einfach und schnell im C-Code umsetzen 
läßt? Ich denke mit (void *) checksum = FLASHEND-32; Lässt sich Adresse 
der Variable setzen und später auslesen. Aber wie schreibe ich den 
Inhalt beim Compiling (oder Linking) direkt an diese Adresse? Kann ich 
dem Compiler irgendwie mitteilen, an welcher Stelle im Flash diese 
Prüfzahl mit einem bestimmten Wert geschrieben werden soll?

Noch eine Frage am Rande: Wo kann ich die max. Flashgröße in AVR Studio 
begrenzen? Sprich, dass er eine Fehlermeldung abgibt, wenn die 
Flashgröße - meinem Limit überschritten wird? Was passiert eigentlich 
mit den unbeschrieben Pages im mC? Bleiben diese beim AVR ATmega dann 
einfach auf dem alten Stand?

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Kai schrieb:
> Notfalls auch im Linkerscript
1
const uint32_t my_serial __attribute__ ((section (".myserialsection"))=0xDEADBEEF;

Linker-Aufruf mit
-Wl,--section-start=.myserialsection=0x12345

Vorsicht mit "-Wl,--gc-sections".

Vorsicht beim objcopy, wenn aus dem .elf ein .hex werden soll, das muss 
die section auch kennen, wenn der Wert in's .hex soll.

Auslesen von Werten aus der Section per pgm_read_xxx aus der 
avr/pgmspace.h.
die Magie mit "__flash"-Variablen, die Zugriffe automatisch auf LPM 
mappt, geht mit deiner extra-Section nicht.
(Lässt sich evtl. aber nachrüsten? k.A.)

: Bearbeitet durch User
von Andreas B. (abm)


Lesenswert?

im Linker-Skript z. B.
  /* CRC32 checksum goes into FLASH at the very end */
  .chcksm ORIGIN(FLASH) + LENGTH(FLASH) - 4 :
  {
    . = ALIGN(4);
    KEEP(*(.chcksm))
    . = ALIGN(4);
  } >FLASH

und in einem der Source-Files:

__attribute__((section(".chcksm"), used)) const uint32_t chcksm;

Allerdings lässt sich der Inhalt (also die tatsächliche Prüfsumme) nur 
zu Fuß in die Hex-Datei hineinmogeln, z. B. im Makefile:

$(BUILD_DIR)/$(TARGET).hex: $(BUILD_DIR)/$(TARGET).elf 
$(BUILD_DIR)/$(TARGET).lst | $(BUILD_DIR)
        $(HEX) --gap-fill 0xFF $< $@
        @BEG=$$(nm $< | sed -n -E 's/^(\w+).*_sidata$$/\1/p' | tr 
[:lower:] [:upper:]); \
        END=$$(nm $< | sed -n -E 's/^(\w+).*chcksm$$/\1/p' | tr 
[:lower:] [:upper:]); \
        CHCKSM=$$(./$(BUILD_DIR)/calc_crc32 $@ 0x$${BEG} 0x$${END}); \
        sed -i -E "s/^S3..$${END}.*$$/$${CHCKSM}/" $@; \
        echo "0x$${BEG}-0x$${END}: $${CHCKSM}"

Da wird die Hex-Datei erstellt, von "calc_crc32" die Prüfsumme von 
_sidata bis chsksm (exkl.) berechnet, (in Motorola-Format) ausgegeben 
und in der Hex-Datei eingefügt.

Bei einer Seriennummer etc. ist das deutlich einfacher, weil man den 
Inhalt ja gleich in den Sourcen angeben kann. Allerdings muss man da 
wiederum die Sektion im Linker-Skript "dazwischen" schieben, also 
unmittelbar nach der Vektortabelle oder so. Man bekommt den Linker 
leider nicht dazu, den Code automatisch "um eine Lücke herum" zu 
platzieren ... Oder man legt sie einfach "eingerahmt" irgendwo hin und 
sucht sie zur Laufzeit.

von Bauform B. (bauformb)


Lesenswert?

Andreas B. schrieb:
> Bei einer Seriennummer etc. ist das deutlich einfacher, weil man den
> Inhalt ja gleich in den Sourcen angeben kann.

Zum Ausgleich muss man für jedes Gerät neu übersetzen... Aber ich hab's 
trotzdem so gemacht :)

> Allerdings muss man da wiederum die Sektion im Linker-Skript
> "dazwischen" schieben, also unmittelbar nach der Vektortabelle oder so.

Das finde ich viel praktischer als am Flash-Ende:
 - die Adresse ist genauso konstant und absolut
 - für den Linker ist es eine normale Adresse, wie die Vektortabelle
 - es kostet kein Byte extra
 - das Image bekommt kein "Loch" (was evt. unnötig geflasht würde)
 - mehr oder weniger großes Flash ändert garnichts
 - ein Check, ob das Flash ausreicht, muss das "Loch" nicht beachten

von Peter D. (peda)


Lesenswert?

Andreas B. schrieb:
> Man bekommt den Linker
> leider nicht dazu, den Code automatisch "um eine Lücke herum" zu
> platzieren

Ja, dafür habe ich auch keine Lösung gefunden.
Ich habe gemerkt, daß der Linker die Objekte alphabetisch sortiert. Ich 
habe einen String in die Datei "_version.c" geschrieben und dann landet 
sie direkt hinter der Vektortabelle.
1
#include "_version.h"
2
#include <avr/pgmspace.h>
3
4
const char Version[] __attribute__((used));
5
const char Version[] PROGMEM = PROJECT_STRING ", "
6
                      "V:" REVISION_STRING ", "
7
                      __DATE__ "\n";

Kai schrieb:
> Die "Prüfzahl" soll später vom Bootloader ausgelesen
> werden um sicherzustellen, dass bei einer Stromunterbrechung wären einem
> Update die Firmware komplett bis zum Ende geschrieben wurde.

Dafür habe ich eine Variable im EEPROM reserviert, die der Bootloader 
zuerst löscht und nach dem Flashen ein Magic reinschreibt.

von Georg M. (g_m)


Angehängte Dateien:

Lesenswert?

Ist es nicht so, dass der Flash-Speicher aufgeteilt wird, um Daten zu 
speichern?

von Peter D. (peda)


Lesenswert?

Was Du da zeigst, trifft nur auf die neuesten AVRs zu.
Die standard AVRs, mit denen der AVR-GCC entwickelt wurde, fangen mit 
der Applikation immer an 0x0000 an. Da plaziert also der AVR-GCC die 
Vectortabelle. Danach kommen die konstanten Daten, da mit int (16 Bit) 
nur die ersten 64kB direkt zugreifbar sind. Darüber braucht man 
spezielle Hilfsregister und Befehle.
Bzw. für Funktionspointer > 16 Bit gibt es eine .trampolines section.

Für den Bootloader ist ein Bereich am Flashende reserviert. Das hat den 
Charme, daß die Applikation keine speziellen Linkerscripte benötigt. Sie 
muß nichtmal wissen, daß ein Bootloader existiert und wo der sitzt.
Bei den neuen AVRs ist das Bootloadergedöns deutlich komplizierter. Wenn 
Bootloader und Applikation nicht zueinander passen, kracht es.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Kai schrieb:
> Wo kann ich die max. Flashgröße [...] begrenzen?

Je nach verwendetem Linker Script, Symbol__TEXT_REGION_LENGTH__ 
definieren:
1
-Wl,--defsym,__TEXT_REGION_LENGTH__=32k

Georg M. schrieb:
> Ist es nicht so, dass der Flash-Speicher aufgeteilt wird, um Daten zu
> speichern?

Kann man auf AVR-Dx und AVR-Ex je nach Konfiguration von FLMAP dazu 
verwenden, um mit LD / LDS Daten aus dem Flash zu lesen.  Daran, wie die 
o.g. Variable im Flash angelegt wird, ändert das aber nix.

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