Kod startowy bez systemu operacyjnego dla inicjalizacji regionu Cortex M3 .bss


10

Stworzyłem stąd inspirowany kod startowy od podstaw dla kory mózgowej M3. Jednak napotykam następujący problem: załóżmy, że deklaruję niezainicjowaną zmienną globalną, powiedzmy typu unsigned char w main.c

#include ...
unsigned char var; 
...
int main()
{
 ...
}

powoduje to, że region .bss w STM32 f103 zaczyna się od _BSS_START = 0x20000000, a kończy na _BSS_END = 0x20000001. Teraz kod startowy

    unsigned int * bss_start_p = &_BSS_START; 
    unsigned int * bss_end_p = &_BSS_END;

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }

próbuje zainicjować, aby wyzerować cały region .bss. Jednak wewnątrz tej pętli while wskaźnik zwiększa się o 4 bajty, dlatego po jednym kroku bss_start_p = 0x20000004 zawsze będzie różny od bss_end_p, co prowadzi do nieskończonej pętli itp.

Czy istnieje jakieś standardowe rozwiązanie tego problemu? Czy mam w jakiś sposób „wymusić” wymiar regionu .bss wielokrotnością liczby 4? A może powinienem użyć wskaźnika do niepodpisanego znaku, aby przejść przez region .bss? Być może coś takiego:

    unsigned char * bss_start_p = (unsigned char *)(&_BSS_START); 
    unsigned char * bss_end_p = (unsigned char *)(&_BSS_END);

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }
```

używaj mniej niż. bootstrapy są napisane w asemblerze z jakiegoś powodu. po pierwsze teraz stworzyłeś problem .data. jest to kurczak i jajko do użycia / założenia, że ​​C działa, polegając na .text, .bss i .data, ale piszesz kod C, który zapewnia, że ​​kod C będzie działał, używając rzeczy w kodzie C, który wymaga bootstrap prawdopodobnie napisany w kodzie C, który opiera się na działaniu C.
old_timer

kod do skopiowania .data jest bardzo podobny do .bss, ale jeśli napiszesz go tak jak powyższy kod, musisz skopiować .data w celu skopiowania .data.
old_timer

Odpowiedzi:


15

Jak podejrzewasz, dzieje się tak, ponieważ typ danych unsigned int ma rozmiar 4 bajtów. Każda *bss_start_p = 0;instrukcja faktycznie usuwa cztery bajty obszaru bss.

Zakres pamięci bss musi być poprawnie wyrównany. Możesz po prostu zdefiniować _BSS_START i _BSS_END, aby całkowity rozmiar był wielokrotnością czterech, ale zwykle jest to obsługiwane przez umożliwienie skryptowi linkerowi zdefiniowania lokalizacji początkowej i końcowej.

Na przykład tutaj jest sekcja linkera w jednym z moich projektów:

.bss (NOLOAD) : ALIGN(4)
{
    __bss_start__ = .;
    *(.bss)
    . = ALIGN(4);
    __bss_end__ = .;
} >RAM

Te ALIGN(4)wypowiedzi dbać o rzeczy.

Możesz także chcieć się zmienić

while(bss_start_p != bss_end_p)

do

while(bss_start_p < bss_end_p).

To nie zapobiegnie problemowi (ponieważ możesz wyczyścić 1-3 bajty więcej niż chcesz), ale może to zminimalizować wpływ :)


@CMarius Po refleksji myślę, że Twój pomysł na wskaźnik char będzie działał świetnie, choć wymagałby więcej cykli. Ale nie jestem pewien, czy wystąpiłyby kolejne problemy z niewyrównaniem następnego obszaru pamięci, więc nie wspomnę o tym w mojej odpowiedzi ...
bitsmack

1
while(bss_start_p < bss_end_p - 1)a następnie bajtowe usunięcie pozostałego zakresu pamięci wyeliminowałoby ostatnią obawę.
glglgl,

4

Standardowe rozwiązanie to memset():

#include <string.h>
memset(&_BSS_START, 0, &_BSS_END - &_BSS_START)

Jeśli nie możesz użyć standardowej biblioteki, musisz zdecydować, czy w twoim przypadku jest ok zaokrąglenie wielkości obszaru pamięci do 4 bajtów i kontynuowanie używania unsigned int *; lub jeśli musisz być wobec tego surowy, w takim przypadku musisz użyć unsigned char *.

Jeśli zaokrąglisz rozmiar, tak jak w pierwszej pętli, bss_start_pmoże rzeczywiście okazać się większy niż, bss_end_pale łatwo jest poradzić sobie z porównaniem mniejszym niż <zamiast testu nierówności.

Oczywiście można również wypełnić większość obszaru pamięci transferami 32-bitowymi, a tylko kilka ostatnich bajtów transferami 8-bitowymi, ale to więcej pracy dla małego zysku, szczególnie tutaj, gdy jest to tylko fragment kodu startowego.


1
Zgadzam się bardzo z użyciem memset(). Ale wyrównanie do 4 bajtów jest mniej więcej koniecznością. Dlaczego więc tego nie zrobić?
Codo

3
w żadnym wypadku kształt lub forma nie jest standardowym rozwiązaniem dla bootstrapu do używania zestawu memset, co jest szalone.
old_timer

nie używasz tego samego języka do ładowania tego języka
old_timer

2
kod bootstrap i skrypt linkera są bardzo zamężne, często zdarza się, że skrypt linkera wyrównuje i dopasowuje rozmiar .bss do co najmniej 4-bajtowej granicy, aby poprawić wypełnienie (w bootstrapie) o 4x ponad bajt na raz instrukcje (przy założeniu (minimum) 32-bitowych magistrali, co jest typowe dla uzbrojenia, ale są wyjątki)
old_timer

3
@ stary_timer, standardowa funkcja C do ustawiania pamięci na określoną wartość to memset(), i wydaje się, że C to programuje. Prosta implementacja memset()jest w zasadzie tylko tą pętlą, to nie tak, że zależy od wielu innych. Ponieważ jest to mikrokontroler, zakładam również, że nie ma dynamicznego linkowania lub takiego działania (a patrząc na link, nie ma, jest to tylko wywołanie main()po tej pętli zerowania), więc kompilator powinien być w stanie memset()tam wejść wraz z innymi funkcjami (lub w celu implementacji).
ilkkachu


3

Istnieje niezliczona ilość innych stron i przykładów. Wiele tysięcy, jeśli nie dziesiątki tysięcy. Istnieją dobrze znane biblioteki c ze skryptami linkera i kodem boostrap, szczególnie newlib, glibc, ale są też inne, które można znaleźć. Bootstraping C z C nie ma sensu.

Odpowiedzi na twoje pytanie próbujesz dokonać dokładnego porównania rzeczy, które mogą nie być dokładne, mogą nie zaczynać się na znanej granicy lub kończyć na znanej granicy. Więc możesz zrobić mniej niż coś, ale jeśli kod nie działał z dokładnym porównaniem, oznacza to, że zerujesz przeszłość .bss do następnej sekcji, co może, ale nie musi, powodować złe rzeczy, więc po prostu zamień na mniej niż isnt rozwiązanie.

Więc oto idzie TL; DR jest w porządku. Nie uruchamiasz języka z tym językiem, możesz na pewno uciec, ale bawisz się ogniem, kiedy to robisz. Jeśli dopiero się uczysz, jak to zrobić, musisz zachować ostrożność, a nie głupie szczęście lub fakty, których jeszcze nie odkryłeś.

Skrypt linkera i kod bootstrap mają bardzo intymny związek, są małżeństwem, łączą się w biodrze, nie rozwijasz jednego bez drugiego, co prowadzi do ogromnej awarii. I niestety skrypt linkera jest zdefiniowany przez linker i język asemblera zdefiniowany przez asembler, więc przy zmianie łańcuchów narzędzi należy spodziewać się ponownego napisania obu. Dlaczego język asemblera? Nie wymaga bootstrapu, zwykle są to języki kompilowane. C robi, jeśli nie chcesz ograniczać używania języka, zacznę od czegoś bardzo prostego, który ma minimalne wymagania specyficzne dla łańcucha narzędzi, nie zakładasz, że zmienne .bss są zerowe (sprawia, że ​​kod jest mniej czytelny, jeśli zmienna nigdy nie jest inicjowana w tym języku , staraj się tego unikać, nie jest to prawdą w przypadku zmiennych lokalnych, więc musisz być na bieżąco, kiedy z niego korzystasz. ludzie i tak unikają globalizacji, więc dlaczego mówimy o .bss i .data ??? (globale są dobre do pracy na tym poziomie, ale to inny temat)) drugą zasadą dla prostego rozwiązania nie jest inicjowanie zmiennych w deklaracji, zrób to w kodzie. tak, pali więcej flasha, na ogół masz dużo, nie wszystkie zmienne są inicjalizowane stałymi i tak, które ostatecznie zużywają instrukcje.

Z projektu cortex-m można wywnioskować, że mogli myśleć, że w ogóle nie ma kodu ładującego, więc nie obsługuje .data ani .bss. Większość ludzi, którzy używają globałów, nie może żyć bez, więc oto:

Mógłbym to uczynić bardziej minimalnym, ale minimalnym funkcjonalnym przykładem dla wszystkich kory za pomocą gnu toolchain, nie pamiętam, które wersje można zacząć od wersji 5.xx lub wyższej w obecnych wersjach 9.xx Przełączałem skrypty linkera gdzieś w okolicach 3. xx lub 4.xx, gdy dowiedziałem się więcej i gdy gnu zmieniło coś, co złamało moje pierwsze.

bootstrap:

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

punkt wejścia do kodu C:

void bounce ( unsigned int );

unsigned int a;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

skrypt linkera.

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

Wszystkie te mogą być mniejsze i nadal działać, dodałem tutaj dodatkowe rzeczy, aby zobaczyć, jak działają.

zoptymalizowana kompilacja i link.

00000000 <_start>:
   0:   20001000
   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

00000014 <reset>:
  14:   f000 f804   bl  20 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
    ...

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

dla niektórych dostawców chcesz użyć 0x08000000 lub 0x01000000 lub innych podobnych adresów, ponieważ flash jest tam mapowany i dublowany do 0x00000000 w niektórych trybach rozruchu. niektóre mają tylko tyle pamięci flash dublowanej na 0x00000000, więc chcesz, aby tablica wektorów wskazywała na przestrzeń flash aplikacji nie na zero. ponieważ jest oparty na tabeli wektorów, wszystko działa.

po pierwsze zauważ, że cortex-ms to maszyny tylko dla kciuka iz jakiegokolwiek powodu wymusiły adres funkcji kciuka, co oznacza, że ​​lsbit jest nieparzysty. Znając swoje narzędzia, dyrektywy .thumb_func informują asembler gnu, że następna etykieta to adres funkcji kciuka. zrobienie rzeczy +1 w tabeli doprowadzi do niepowodzenia, nie daj się skusić, aby to zrobić, zrób to dobrze. istnieją inne sposoby asemblera GNU, aby zadeklarować funkcję, że jest to podejście minimalne.

   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

nie uruchomi się, jeśli nie uzyskasz prawidłowej tabeli wektorów.

prawdopodobnie wystarczy wektor wskaźnika stosu (można w nim umieścić cokolwiek, jeśli sam chcesz ustawić wskaźnik stosu w kodzie) i wektor resetu. Umieściłem tutaj cztery bez konkretnego powodu. Zazwyczaj podaje 16, ale chciał skrócić ten przykład.

Więc co musi zrobić minimum bootstrapu C? 1. ustaw wskaźnik stosu 2. zero .bss 3. skopiuj. Data 4. rozgałęzienie lub wywołanie punktu wejścia C.

punkt wejścia C jest zwykle nazywany main (). ale niektóre łańcuchy narzędzi wyświetlają main () i dodają dodatkowe śmieci do kodu. Celowo używam innej nazwy. YMMV.

kopia .data nie jest potrzebna, jeśli wszystko oparte jest na pamięci RAM. będąc mikrokontrolerem z korą-m jest to technicznie możliwe, ale mało prawdopodobne, więc kopia .data jest potrzebna ..... jeśli istnieją .data.

Mój pierwszy przykład i styl kodowania to nie poleganie na .data ani .bss, jak w tym przykładzie. Arm zajął się wskaźnikiem stosu, więc pozostaje tylko zadzwonić do punktu wejścia. Lubię to mieć, aby punkt wejścia mógł wrócić, wielu ludzi twierdzi, że nigdy nie powinieneś tego robić. możesz po prostu to zrobić:

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word centry
.word done
.word done
.word done

i nie wraca z centry () i nie ma zresetowanego kodu procedury obsługi.

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000

linker umieścił rzeczy tam, gdzie prosiliśmy. Ogólnie rzecz biorąc mamy w pełni funkcjonalny program.

Więc najpierw popracuj nad skryptem linkera:

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

podkreślając, że nazwy rom i ram nie mają znaczenia, że ​​łączą tylko kropki łącznika między sekcjami.

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

.align
.word __data_rom_start__
.word __data_start__
.word __data_end__
.word __data_size__

dodaj kilka elementów, abyśmy mogli zobaczyć, co zrobiły narzędzia

void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

dodaj kilka elementów do umieszczenia w tych sekcjach. i dostać

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, r11, lsl r0
   c:   0000001b    andeq   r0, r0, r11, lsl r0
  10:   0000001b    andeq   r0, r0, r11, lsl r0

00000014 <reset>:
  14:   f000 f80c   bl  30 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

00000030 <centry>:
  30:   2207        movs    r2, #7
  32:   b510        push    {r4, lr}
  34:   4b04        ldr r3, [pc, #16]   ; (48 <centry+0x18>)
  36:   2007        movs    r0, #7
  38:   601a        str r2, [r3, #0]
  3a:   f7ff ffef   bl  1c <bounce>
  3e:   2000        movs    r0, #0
  40:   bc10        pop {r4}
  42:   bc02        pop {r1}
  44:   4708        bx  r1
  46:   46c0        nop         ; (mov r8, r8)
  48:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

oto rzeczy, których szukamy w tym eksperymencie (nie zauważaj żadnego powodu, aby w rzeczywistości ładować lub uruchamiać kod ... poznaj swoje narzędzia, naucz się ich)

  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

dowiedzieliśmy się tutaj, że pozycja zmiennych jest bardzo wrażliwa w skryptach gnu linkera. zwróć uwagę na pozycję data_rom_start vs data_start, ale dlaczego data_end działa? Pozwolę ci to rozgryźć. Już rozumiem, dlaczego można nie chcieć bawić się skryptami linkera i po prostu przejść do prostego programowania ...

więc kolejną rzeczą, której się tutaj nauczyliśmy, jest to, że dla nas linker data_rom_start dla nas nie potrzebował ALIGN (4). Czy należy zakładać, że to zawsze zadziała?

Zauważ też, że padał on w drodze do, mamy 5 bajtów .data, ale wypełnił go do 8. Bez żadnych ALIGN (s) możemy już wykonać kopię za pomocą słów. Czy w oparciu o to, co widzimy dzisiaj dzięki temu zestawowi narzędzi na moim komputerze, może to dotyczyć przeszłości i przyszłości? Kto wie, nawet przy ALIGNach trzeba okresowo sprawdzać, czy niektóre nowe wersje nie psują rzeczy, robią to od czasu do czasu.

z tego eksperymentu przejdźmy do tego, żeby być bezpiecznym.

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   . = ALIGN(4);
   __data_end__ = .;
   } > ted AT > bob
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   . = ALIGN(4);
   __bss_end__ = .;
   } > ted
   __bss_size__ = __bss_end__ - __bss_start__;

}

przesuwając końce do środka, aby były zgodne z tym, co robią inni ludzie. I to nie zmieniło:

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

jeszcze jeden szybki test:

.globl bounce
bounce:
    nop
    bx lr

dający

0000001c <bounce>:
  1c:   46c0        nop         ; (mov r8, r8)
  1e:   4770        bx  lr
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

nie trzeba wstawiać między odbiciem a wyrównaniem

Och, racja, pamiętam teraz, dlaczego nie włożyłem _end__ do środka. ponieważ NIE DZIAŁA.

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

jakiś prosty, ale bardzo przenośny kod do połączenia z tym skryptem linkera

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
bss_zero:
    stmia r1!,{r2}
    sub r0,#4
    bne bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3}
    stmia r2!,{r3}
    sub r0,#4
    bne data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__

dający

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000003d    andeq   r0, r0, sp, lsr r0
   c:   0000003d    andeq   r0, r0, sp, lsr r0
  10:   0000003d    andeq   r0, r0, sp, lsr r0

00000014 <reset>:
  14:   480c        ldr r0, [pc, #48]   ; (48 <blen>)
  16:   2800        cmp r0, #0
  18:   d004        beq.n   24 <bss_zero_done>
  1a:   490a        ldr r1, [pc, #40]   ; (44 <bstart>)
  1c:   2200        movs    r2, #0

0000001e <bss_zero>:
  1e:   c104        stmia   r1!, {r2}
  20:   3804        subs    r0, #4
  22:   d1fc        bne.n   1e <bss_zero>

00000024 <bss_zero_done>:
  24:   480b        ldr r0, [pc, #44]   ; (54 <dlen>)
  26:   2800        cmp r0, #0
  28:   d005        beq.n   36 <data_copy_done>
  2a:   4908        ldr r1, [pc, #32]   ; (4c <rstart>)
  2c:   4a08        ldr r2, [pc, #32]   ; (50 <dstart>)

0000002e <data_copy>:
  2e:   c908        ldmia   r1!, {r3}
  30:   c208        stmia   r2!, {r3}
  32:   3804        subs    r0, #4
  34:   d1fb        bne.n   2e <data_copy>

00000036 <data_copy_done>:
  36:   f000 f80f   bl  58 <centry>
  3a:   e7ff        b.n 3c <done>

0000003c <done>:
  3c:   e7fe        b.n 3c <done>

0000003e <bounce>:
  3e:   46c0        nop         ; (mov r8, r8)
  40:   4770        bx  lr
  42:   46c0        nop         ; (mov r8, r8)

00000044 <bstart>:
  44:   20000008    andcs   r0, r0, r8

00000048 <blen>:
  48:   00000004    andeq   r0, r0, r4

0000004c <rstart>:
  4c:   00000074    andeq   r0, r0, r4, ror r0

00000050 <dstart>:
  50:   20000000    andcs   r0, r0, r0

00000054 <dlen>:
  54:   00000008    andeq   r0, r0, r8

00000058 <centry>:
  58:   2207        movs    r2, #7
  5a:   b510        push    {r4, lr}
  5c:   4b04        ldr r3, [pc, #16]   ; (70 <centry+0x18>)
  5e:   2007        movs    r0, #7
  60:   601a        str r2, [r3, #0]
  62:   f7ff ffec   bl  3e <bounce>
  66:   2000        movs    r0, #0
  68:   bc10        pop {r4}
  6a:   bc02        pop {r1}
  6c:   4708        bx  r1
  6e:   46c0        nop         ; (mov r8, r8)
  70:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

możemy się tam zatrzymać lub iść dalej. Jeśli inicjalizujemy w tej samej kolejności co skrypt linkera, to dobrze, jeśli przejdziemy do następnej rzeczy, ponieważ jeszcze tam nie dotarliśmy. i stm / ldm są wymagane / pożądane tylko do używania adresów wyrównanych do słów, więc jeśli zmienisz na:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

z bss najpierw w skrypcie linkera, i tak, nie chcesz ble.

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3804        subs    r0, #4
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000004    andcs   r0, r0, r4

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

te pętle będą szybsze. teraz nie wiem, czy magistrale ahb mogą mieć szerokość 64 bitów, czy nie, ale dla pełnego rozmiaru ramienia chciałbyś wyrównać te rzeczy na 64-bitowych granicach. cztery rejestry ldm / stm na 32-bitowej granicy, ale nie na 64-bitowej granicy, stają się trzema oddzielnymi transakcjami magistrali, przy czym wyrównanie na 64-bitowej granicy jest pojedynczą transakcją oszczędzającą kilka zegarów na instrukcję.

ponieważ robimy baremetal i jesteśmy w pełni odpowiedzialni za wszystko, co możemy umieścić, powiedzmy najpierw bss, a następnie dane, a następnie, jeśli mamy stos, to rośnie stos od góry do dołu, więc jeśli wyzerujemy bss i rozlejemy niektóre, dopóki zaczniemy od właściwe miejsce, które jest w porządku, jeszcze nie używamy tej pamięci. następnie kopiujemy .data i możemy przelać do stosu, który jest w porządku, sterty lub nie, jest dużo miejsca na stosie, więc nie nadepniemy na nikogo / nic (dopóki upewnimy się, że w skrypcie linkera to robimy. jeśli istnieje obawa, powiększ ALIGN (), abyśmy zawsze znajdowali się w naszej przestrzeni dla tych wypełnień.

więc moje proste rozwiązanie, weź to lub zostaw. witam, aby naprawić wszelkie błędy, nie uruchomiłem tego na sprzęcie ani na moim symulatorze ...

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(8);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

   . = ALIGN(8);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

}



.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3,r4,r5,r6}
    stmia r2!,{r3,r4,r5,r6}
    sub r0,#16
    ble data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__


void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-ld -o hello.elf -T flash.ld flash.o centry.o
arm-none-eabi-objdump -D hello.elf > hello.list
arm-none-eabi-objcopy hello.elf hello.bin -O binary

złóż wszystko razem, a otrzymasz:

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3810        subs    r0, #16
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000008    andcs   r0, r0, r8

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

zauważ, że działa to z arm-none-eabi- i arm-linux-gnueabi i innymi wariantami, ponieważ nie użyto żadnych świstów ghee.

Kiedy się rozejrzysz, przekonasz się, że ludzie oszaleją na punkcie skryptu ghee w swoich skryptach linkerów, ogromnych potwornych zlewozmywaków kuchennych. Lepiej po prostu wiedzieć, jak to zrobić (lub lepiej opanować narzędzia, aby kontrolować, co się dzieje), zamiast polegać na innych rzeczach i nie wiedzieć, gdzie to się zepsuje, ponieważ nie rozumiesz i / lub chcesz badać to.

jako ogólna zasada, nie ładuj języka z tym samym językiem (w tym sensie bootstrap oznacza, że ​​kod nie kompiluje kompilatora z tym samym kompilatorem), chcesz używać prostszego języka z mniejszym ładowaniem. Dlatego C jest wykonywane w asemblerze, nie ma żadnych wymagań dotyczących ładowania początkowego, które zaczynasz od pierwszej instrukcji po zresetowaniu. JAVA, na pewno możesz napisać Jvm w C i załadować C z asm, a następnie załadować JAVA, jeśli chcesz z C, ale także wykonać JAVA w C.

Ponieważ kontrolujemy założenia dotyczące tych pętli kopiowania, są one z definicji ciaśniejsze i czystsze niż ręcznie dostrojone memcpy / memset.

Zwróć uwagę, że Twoim drugim problemem było:

unsigned int * bss_start_p = &_BSS_START; 
unsigned int * bss_end_p = &_BSS_END;

jeśli są to lokalne kary, nie ma problemu, jeśli są globalne, najpierw trzeba zainicjować dane .data, aby działały, a jeśli spróbujesz wykonać tę sztuczkę, to nie powiedzie się. Zmienne lokalne, dobrze, które będą działać. jeśli z jakiegoś powodu zdecydowałeś się stworzyć statycznych miejscowych (lokalne globale lubię je nazywać), to znów jesteś w kłopotach. Za każdym razem, gdy wykonujesz zadanie w deklaracji, powinieneś się nad tym zastanowić, w jaki sposób jest ono realizowane i czy jest bezpieczne / rozsądne. Za każdym razem, gdy zakładasz, że zmienna ma wartość zero, gdy nie jest zadeklarowana, ta sama umowa, jeśli zmienna lokalna nie jest przyjmowana jako zero, jeśli jest globalna, to jest. jeśli nigdy nie zakładasz, że są zerowe, nigdy nie musisz się martwić.


super, po raz drugi przekroczyłem maksymalną liczbę znaków w odpowiedzi ....
old_timer

To pytanie dotyczy przepływu stosu, a nie elektrotechniki.
old_timer

Również poleganie na zewnętrznym linku w pytaniu nie jest dobrą formą, jeśli link zniknie przed pytaniem, pytanie może nie mieć sensu.
old_timer

W tym przypadku twój tytuł i treść wystarczą, aby wiedzieć, że próbujesz załadować C na określonym mikrokontrolerze i wędrujesz do plików .bss i .data
old_timer

ale w tym przypadku została wprowadzona w błąd przez bardzo pouczającą stronę internetową.
old_timer
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.