logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Lokalizacja sekcji programu: RAM, EEPROM, FLASH?

Jakub17 28 Lut 2018 16:27 1872 14
REKLAMA
  • #1 17070274
    Jakub17
    Poziom 6  
    Posty: 484
    Ocena: 15
    Witam

    Tak jak w temacie, mam problem ze zrozumieniem gdzie właściwie poszczególne sekcje programu. Czy wszystkie sekcje znajdują się w pamięci RAM, a przy starcie programu są wypełniane zawartością lub adresami innych rodzajów pamięci jak np. FLASH? I program przez adresy umieszczone w tych sekcjach skacze do odpowiednich rodzajów pamięci?
    Kiedy np. tworzę zmienną EEMEM to znajduje się ona w sekcji eeprom, ale czy sama sekcja znajduje się w RAM i przechowuje adres zmiennej z pamięci eeprom, czy może cała sekcja jest w eeprom?
    Gdzie znajdują się sekcje .data i .bss?
  • REKLAMA
  • Pomocny post
    #2 17070471
    grko
    Poziom 33  
    Posty: 1386
    Pomógł: 247
    Ocena: 141
    Twoje pytanie jest dość ogólne ponieważ wszystko o czym mówisz jest definiowane w skrypcie linkera. Ale zazwyczaj jest tak, że:
    - sekcja .text jest umieszczana w pamięci flash
    - sekcja .data jest umieszczana w pamięci RAM (kopiowana z pamięci flash w rozbiegówce)
    - sekcja .bss jest umieszczana w pamięci RAM (ustawiana na wartość zero w rozbiegówce)

    Jak tworzysz zmienną EEMEM to jest ona umieszczana pod rzeczywistym adresem pamięci EEPROM (w AVR wszystkie rodzaje pamięci są w odrębnej przestrzeni adresowej zaczynającej się od 0) ale i tak musisz się odwoływać do tych zmiennych poprzez odpowiednie API z avrlibc. Tego typu zmienne są umieszczane w sekcji eeprom i możesz sobie zaprogramować pamięć EEPROM mając tylko plik *.elf. Program readelf może wyświetlić wszystkie sekcje z pliku *.elf:
    Kod: Bash
    Zaloguj się, aby zobaczyć kod
  • REKLAMA
  • #3 17070939
    Jakub17
    Poziom 6  
    Posty: 484
    Ocena: 15
    grko napisał:

    - sekcja .data jest umieszczana w pamięci RAM (kopiowana z pamięci flash w rozbiegówce)

    Czy to znaczy, że zmienne statyczne czyli zadeklarowane ze słowem static i zmienne globalne znajdują się w pamięci FLASH i są później kopiowane do RAM?

    grko napisał:

    Jak tworzysz zmienną EEMEM to jest ona umieszczana pod rzeczywistym adresem pamięci EEPROM (w AVR wszystkie rodzaje pamięci są w odrębnej przestrzeni adresowej zaczynającej się od 0) ale i tak musisz się odwoływać do tych zmiennych poprzez odpowiednie API z avrlibc. Tego typu zmienne są umieszczane w sekcji eeprom i możesz sobie zaprogramować pamięć EEPROM mając tylko plik *.elf. Program readelf może wyświetlić wszystkie sekcje z pliku *.elf:
    [/syntax]

    Gdybym hipotetycznie utworzył wskaźnik w main() i chciał mu przypisać początkowy adres pamięci EEPROM np. adres 0 to skąd będzie wiadomo o adresowanie jakiej pamięci chodzi skoro wszystkie rodzaje pamięci zaczynają się od adresu 0. Musi chyba istnieć jakaś wzajemna korelacja między tymi pamięciami pozwalająca jednocześnie na wzajemne ich odróżnienie... Czytałem gdzieś kiedyś na forum, że np. adres zerowy np. pamięci EEPROM wcale nie ma wartości 0 tylko ma jakiś offset pozwalający odróżnić go od innych rodzajów pamięci, czyli tak jakby wszystkie pamięci traktowane były jako jedna przestrzeń adresowa. To prawda?
  • REKLAMA
  • Pomocny post
    #4 17071294
    grko
    Poziom 33  
    Posty: 1386
    Pomógł: 247
    Ocena: 141
    Jakub17 napisał:

    Czy to znaczy, że zmienne statyczne czyli zadeklarowane ze słowem static i zmienne globalne znajdują się w pamięci FLASH i są później kopiowane do RAM?


    Dotyczy to tylko zmiennych globalnych (statycznych w scope funkcji), które są zainicjalizowane. Zmienne globalne niezanicjalizowane są umieszczane w sekcji .bss i inicjalizowane wartością 0.

    Jakub17 napisał:

    Gdybym hipotetycznie utworzył wskaźnik w main() i chciał mu przypisać początkowy adres pamięci EEPROM np. adres 0 to skąd będzie wiadomo o adresowanie jakiej pamięci chodzi skoro wszystkie rodzaje pamięci zaczynają się od adresu 0. Musi chyba istnieć jakaś wzajemna korelacja między tymi pamięciami pozwalająca jednocześnie na wzajemne ich odróżnienie...


    No właśnie nie ma rozróżnienia. Dlatego przy dostępie do pamięci EEPROM musisz używać API z avrlibc.

    Jakub17 napisał:

    Czytałem gdzieś kiedyś na forum, że np. adres zerowy np. pamięci EEPROM wcale nie ma wartości 0 tylko ma jakiś offset pozwalający odróżnić go od innych rodzajów pamięci, czyli tak jakby wszystkie pamięci traktowane były jako jedna przestrzeń adresowa. To prawda?


    IMO nieprawda. Funkcje z avrlibc pobierają jako adres zwykłe o ile się nie mylę 16 bitowe wskaźniki.

    https://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html
  • #5 17071448
    Konto nie istnieje
    Konto nie istnieje  
  • #6 17071616
    Jakub17
    Poziom 6  
    Posty: 484
    Ocena: 15
    grko napisał:


    IMO nieprawda. Funkcje z avrlibc pobierają jako adres zwykłe o ile się nie mylę 16 bitowe wskaźniki.

    https://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html



    Pytanie moje było raczej ogólne, nie chodziło mi tylko o pamięć EEPROM. Z kolei funkcje z avrlibc do EEPROM ktoś musiał napisać zatem wewnątrz ich ciała pamięć musi być jakoś "ręcznie adresowana" a nie przez kolejne funkcje... Jeżeli wszystkie rodzaje pamięci są traktowane jako wspólna przestrzeń adresowa, to wydaje mi się że adresy początków tych pamięci muszą się jakoś odróżniać żeby na siebie nie nachodziły tzn. np. zerowy adres pamięci EEPROM ma tak naprawdę adres 0x8000 we wspólnej przestrzeni adresowej. A jeżeli nie ma wspólnej przestrzeni adresowej to może istnieją specjalne instrukcje do odwoływania się do różnych rodzajów pamięci?
    Zależy mi na tym by to zrozumieć, bo gdybym podłączył zewnętrzną pamięć EEPROM czy FLASH to nie wiedziałbym jak ją adresować. Najpierw chciałbym zrozumieć jak to zachodzi niskopoziomow a pozniej będę używał funkcji bibliotecznych.
  • REKLAMA
  • #7 17071743
    Konto nie istnieje
    Konto nie istnieje  
  • #8 17071774
    Jakub17
    Poziom 6  
    Posty: 484
    Ocena: 15
    Wiem, że w niektórych uC występuje interfejs XMEM, który obsługuje pamięci zewnętrzne. Moje pytanie dotyczy tylko tego czy jest możliwość odwoływania się do różnych rodzajów pamięci (zewnętrznych lub wewnętrznych) poprzez podanie odpowiedniej wartości wskaźnika czy jednak trzeba użyć jakiś specjalnych instrukcji asemblerowych. Wiem że dla pamięci FLASH i RAM są odpowiednie instrukcje asemblera, ale czy tak jest też w pamięci EEPROM i rownież w przypadku różnego rodzaju pamięci zewnętrznych?

    Dodano po 58 [minuty]:

    Odnośnie sekcji ale trochę innych:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Oczywiscie wiem, że w tych sekcjach nie powinno sie używać tak rozwlekłego kodu jak wypisywanie tekstu na ekran LCD ale funkcja jest do testów.
    Nie rozumiem, dlaczego utworzenie zmiennej lokalnej tab powoduje, że kompilator sypie mało jasnym błędem: in store_field, at expr.c:6661. W sekcji .init5 na pewno jest już zainicjalizowany stos oraz zmienne globalne, także umieszczenie takiej tablicy na stosie powinno być możliwe. Natomiast użycie zmiennych statycznych z sekcji .data jak literał "Tekst" czy statyczna tablica tab nie powoduje już błędu. Nie rozumiem takiego zachowania programu...
    Funkcja "pierwsza funkcja" jest z atrybutem naked ponieważ nie będzie już nigdzie wywoływana w main() ani nie pobiera argumentów, więc prolog i epilog jest nie potrzebny.
  • Pomocny post
    #9 17072161
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Piotrus_999 napisał:
    grko napisał:
    IMO nieprawda

    a czy Xmegi przypadkiem nie mapuja przypadkiem eepromu do data memory ?


    Tak, w tym przypadku można włączyć mapowanie EEPROM do SRAM, co ma też tą zaletę, że można programować EEPROM stronami (co bywa nawet 32x szybsze niż programowanie komórka, po komórce).

    Jakub17 napisał:
    Wiem, że w niektórych uC występuje interfejs XMEM, który obsługuje pamięci zewnętrzne. Moje pytanie dotyczy tylko tego czy jest możliwość odwoływania się do różnych rodzajów pamięci (zewnętrznych lub wewnętrznych) poprzez podanie odpowiedniej wartości wskaźnika czy jednak trzeba użyć jakiś specjalnych instrukcji asemblerowych. Wiem że dla pamięci FLASH i RAM są odpowiednie instrukcje asemblera, ale czy tak jest też w pamięci EEPROM i rownież w przypadku różnego rodzaju pamięci zewnętrznych?


    Programując w C musisz kompilatorowi w jakiś sposób przekazać na co wskazuje wskaźnik. W AVR używasz do tego PROGMEM, lub _flash lub _memx. Kompilator taki wskaźnik będzie traktował jako wskaźnik do FLASH lub do FLASH i SRAM (dla _memx) i wygeneruje odpowiednie instrukcje asemblera. W przypadku EEPROM sprawa jest bardziej skomplikowana, gdyż AVRy różnią się sposobem dostępu do tej pamięci. W efekcie póki co nikt nie zaimplementował osobnej przestrzeni adresowej dla EEPROM i trzeba korzystać z funkcji udostępnionych w nagłówku eeprom.h. Generują one odpowiednie instrukcje umożliwiające dostęp do tej pamięci - EEPROM jest dostępny przez rejestry układów IO, a nie oddzielne instrukcje asemblera. Podobnie pamięci zewnętrzne - zazwyczaj są łączone poprzez jakiś interfejs, np. SPI lub I2C, więc dostęp do nich musisz załatwić we własnym zakresie. Wyjątkiem jest pamięć podłączona przez XMEM - wtedy wystarczy tylko ew. zmienić skrypt linkera, lub odpowiednio skonfigurować Atmel Studio. Dostęp do tej pamięci z poziomu asemblera wygląda identycznie jak do SRAM.

    Jakub17 napisał:
    Funkcja "pierwsza funkcja" jest z atrybutem naked ponieważ nie będzie już nigdzie wywoływana w main() ani nie pobiera argumentów, więc prolog i epilog jest nie potrzebny.


    To nie wystarczy. ABI kompilatora czyni pewne założenia co do zawartości rejestrów. Jeśli powyższa funkcja je zmieni, co jest prawie pewne, to dalszy kod ma spore szanse się posypać. naked ma sens stosować tylko dla funkcji, które kończą program, w efekcie co dalej jest bez znaczenia, ew. dla funkcji które nie zmieniają rejestrów traktowanych jako zachowywane przez funkcję. Ew. dla funkcji napisanych w asemblerze, dla których to programista np. zadbał o przywrócenie kontekstu.
  • Pomocny post
    #10 17072202
    Konto nie istnieje
    Konto nie istnieje  
  • #11 17072922
    Jakub17
    Poziom 6  
    Posty: 484
    Ocena: 15
    tmf napisał:


    To nie wystarczy. ABI kompilatora czyni pewne założenia co do zawartości rejestrów. Jeśli powyższa funkcja je zmieni, co jest prawie pewne, to dalszy kod ma spore szanse się posypać. naked ma sens stosować tylko dla funkcji, które kończą program, w efekcie co dalej jest bez znaczenia, ew. dla funkcji które nie zmieniają rejestrów traktowanych jako zachowywane przez funkcję. Ew. dla funkcji napisanych w asemblerze, dla których to programista np. zadbał o przywrócenie kontekstu.


    Dzięki za szczegółowe wyjaśnienia :) Skoro pojawił się wątek atrybutu naked to dopytam: Czyli użycie normalnej funkcji spowoduje zapisanie stanu rejestrów podczas prologu a naked już tego nie robi, tak? Czyli wystarczy się pozbyć atrybutu naked i będzie w porządku? No bo gdy deklaruje powyższą funkcję bez atrybutu naked to cały program zapętla się na tej funkcji i nie wchodzi nawet w main(). A jeśli chodzi o modyfikacje rejestrów to mówisz o rejestrach R1-R31 czy jakich?
  • #12 17072995
    Konto nie istnieje
    Konto nie istnieje  
  • #13 17073002
    Jakub17
    Poziom 6  
    Posty: 484
    Ocena: 15
    Piotrus_999 napisał:
    Musisz jednak pamiętać że zero_reg niekoniecznie musi być zero (a to zakłada kompilator) a zależne jest to od tego co się wykonywało poprzednio.

    Możesz rozwinąć trochę myśl? Nie rozumiem tego zdania. Wychodzi na to, że bez naked sie nie obedzie bo program się zapętla jeszcze przed main() ale dlaczego?

    Dodano po 17 [minuty]:

    Dodatkowo nie panuje jeszcze nad jedną rzeczą:
    Utworzyłem własną sekcje:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    I przekazałem informację do linkera z poziomu interfejsu Atmel Studio w zakładce SRAM:
    .moja_sekcja = 0x150
    Zrobiłem do wedle wskazówek zamieszczonych w samym Atmel Studio:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Problem w tym, że pod wskazanym przeze mnie adresem nie ma łańcucha "tekst". Sprawdzałem to w podglądzie pamięci Atmel studio podczas debugowania. Dodatkowo pliki .lss i .map poświadczają, że sekcja została utworzona i zajmuje odpowiednią ilość pamięci. Co jest nie tak?
  • Pomocny post
    #14 17073067
    Konto nie istnieje
    Konto nie istnieje  

Podsumowanie tematu

✨ W dyskusji poruszono kwestie lokalizacji sekcji programu w pamięciach RAM, EEPROM i FLASH. Ustalono, że sekcja .text znajduje się w pamięci FLASH, sekcja .data jest kopiowana do RAM podczas rozruchu, a sekcja .bss również znajduje się w RAM i jest inicjalizowana na wartość zero. Zmienna EEMEM jest umieszczana w EEPROM, a dostęp do niej wymaga użycia odpowiednich funkcji z biblioteki avrlibc. Zauważono, że w architekturze Harvard pamięci są oddzielone, co wymaga stosowania specjalnych instrukcji do adresowania różnych typów pamięci. Uczestnicy dyskusji podkreślili, że zrozumienie tych mechanizmów wymaga praktyki i analizy wygenerowanego kodu.
Wygenerowane przez model językowy.
REKLAMA