Skocz do zawartości

Programowanie w assemblerze ARM Cortex-Mx - ćwiczenia po kursie "ARM Assembly Language From Ground Up™ 1"


FlyingDutch

Pomocna odpowiedź

Cześć,

 

Ten kurs i prowadzący niestety dla mnie jest bardzo niesłownym i niechlujnym instruktorem. Posiada podstawową wadę, że jest to keil, ale jak ktoś nauczy się i zrozumie w keilu, to i pociągnie w GCC. Ten instruktor Israel Gbati jest człowiekiem chaosu, do tego niesłownym. Przypominam sobie dyskusję, gdzie ludzie pytali o jego błędnie przedstawione lekcje, na co on (wymijająco) odpowiedział, że już mu to zniknęło z dysku, ale robi nowy kurs, a ten nowy kurs będzie dla użytkowników tego kursu bezpłatny bla bla - to już kilka lat minęło a Israel nie dał userom tego kursu, innego nowego (w domyśle lepszego) kursu... Pojawiły się za to inne kursy będące dosłownie zlepkami jego wypocin z poprzednich kursów (osobno płatne oczywiście) i firmowane przez 3 instruktorów, z których nie rozpoznaję nikogo, a głos na filmach jest tylko Gbatiego... No ale... 3 instruktorów i użytkownik się pyta o odejmowanie 64 bitowe (przykład z kursu akurat był z błędem - czemu mnie to nie dziwi u tego instruktora) i użytkownik po kilku miesiącach sam sobie odpowiedział - jak powinno wyglądać odejmowanie 64 bit. Kiedy zwróciłem uwagę, że instruktorzy (3 osoby) nie reagują na zgłoszenie przez wiele miesięcy - jeden z nich pyta się mnie w czym jest nasz problem i jaki mamy procesor (w jego mniemaniu wszystko było OK) - wówczas wyjaśniłem mu że jego przykład substrakcji 64bit nie może działać poprawnie, chociażby z tego względu, że nie używa flagi przeniesienia - wtedy zatrybił - i co z tego jak użytkownikom nie odpowiadają miesiącami, a na błędny przykład mówią że jest dobry.

Do dzisiaj, a już będzie z rok albo więcej nikt z nich nie odpowiedział mi w jaki sposób się to dzieje, że kod startowy wystartuje bez tablicy wektorów (!!!). 

No dobra powieszałem psy na Gbattim i jego spółce, ale dla Ciebie jest znakomitą sprawą że zająłeś się na poważnie assemblerem, bo to bardzo poszerzy Twoje horyzonty i możliwości. Mogę polecić fajne przykłady jak debuguje assembler Miro Samek na YT i buduje prosty MiROS RTOS (Minima Realtime OS).

 

ps musiałem się odezwać jak wątek o assemblerze 🙂

  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

(edytowany)
13 godzin temu, virtualny napisał:

Cześć,

Do dzisiaj, a już będzie z rok albo więcej nikt z nich nie odpowiedział mi w jaki sposób się to dzieje, że kod startowy wystartuje bez tablicy wektorów (!!!). 

No dobra powieszałem psy na Gbattim i jego spółce, ale dla Ciebie jest znakomitą sprawą że zająłeś się na poważnie assemblerem, bo to bardzo poszerzy Twoje horyzonty i możliwości. Mogę polecić fajne przykłady jak debuguje assembler Miro Samek na YT i buduje prosty MiROS RTOS (Minima Realtime OS).

ps musiałem się odezwać jak wątek o assemblerze 🙂

Cześć,

dzięki za podzielenie się na forum twoim doświadczeniem.

Odnośnie tablicy wektorów przerwań, to nie jest wymagana cała tablica wektorów przerwań. Aby program wystartował wystarczy wektor przerwania dla "Reset Handler" (po fizycznym resecie - pin reset program startuje od tego przerwania) i deklaracja minimalnego stosu (oczywiście z przydziałem pamięci.stosu - rozmiar wystarcza z 256 bajtów)  Testowałem taką minimalną konfigurację i to działa 🙂

Trochę znam "Free RTOS" - uczyłem się z serii tutoriali na YT, ale z chęcią obejrzę materiały podane przez Ciebie.

Jeszcze raz dzieki za feedback, jeśli masz duże doświadczenie z assemblerem ARM Cortex, może zamieścisz jakiś fajny wątek na forum (mnie szczególnie interesują algorytmy obliczeniowe z użyciem FPU).

BTW: a znasz może assembler'a dla x86/x64? Ja programowałem trochę w assemblerze x86 (w latach 90-tych XX wieku), ale od tego czasu dużo się zmieniło w procesorach Intela i AMD. Teraz odświeżyłem sobie tą wiedzę drugim kursem z Udemy. Jeśl możesz polecić jakieś fajne materiały w necie to z chęcią poczytam. Szczególnie interesują mnie znów algorytmy obliczniowe z wykorzystaniem FPU i rozszerzeń typu SIMD.

Pozdrawiam

Edytowano przez FlyingDutch
  • Lubię! 2
Link do komentarza
Share on other sites

Nie nie, wrzucasz jakikolwiek kod (lub prawie jakikolwiek) pod 0x08000000 bez jakiejkolwiek tablicy wektorów, bez reset handlera i stack_pointer_value (bo to niby są te 2 główne wartości żeby odpalić proca) i odpali się twój kod nawet bez 2 pierwszych Uint32_t values of vector table(!!!) Sprawdź - napisz goły kod w assemblerze mrugający diodą, potem wrzuć go pod 0x08000000 i zobaczysz, że dioda zacznie mrugać! - Ja sprawdziłem.

Zacząłem się po prostu domyślać, że jest to jakiś specjalny stan procesora natychmiast po resecie i PC jest ładowany z kodu, co na 99% musi wywołać jakiś har fault handler, że PC nie jest ustawiony w przestrzeni zamapowanego przez procesor RAM, tylko na randomowy nieistniejący adres i wówczas na pewno musi następować ze strony proca jakieś automatyczne ustawienie PC dokładnie na 0x08000000. Nie wiem czy ma taką możliwość CM3, ja sprawdzałem na jakimś NUCLEO z CM4

Przy okazji np kod dla NUCLEO-F1036RB bodaj 48 bajtów do ledblinka:

/**
 ******************************************************************************
 * @file      startup_stm32f103rbtx.s
 * @author    Auto-generated by STM32CubeIDE
 * @brief     STM32F103RBTx device vector table for GCC toolchain.
 *******************************************************************************
 */
//---
#define RCC_BASE               0x40021000
#define RCC_APB2ENR_OFFSET     0x18
#define GPIOA_BASE             0x40010800
#define GPIOA_DIFFERENT        (RCC_BASE - GPIOA_BASE)
//---
#define GPIOA_CRL_VALUE        0x44244444
#define GPIOA_CRL_OFFSET       0
#define GPIOA_ODR_OFFSET       0x0C
//---
#define GPIOA_BIT5_VALUE       (1 << 5)
//---
#define DELAY_VALUE            500000
//---
#define CRL_DIFFERENT          (GPIOA_CRL_VALUE - GPIOA_BASE)
//---
//===============================================================================

.syntax unified
.cpu cortex-m3
.fpu softvfp
.thumb

/******************************************************************************
*
* The STM32F103RBTx vector table.  Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
******************************************************************************/
  .section .isr_vector,"a",%progbits
  //.type g_pfnVectors, %object


g_pfnVectors:
  .word 0x20005000
  .word Reset_Handler


  .section .text.Reset_Handler
  .type Reset_Handler, %function

Data_01:
   .word RCC_BASE
   .word GPIOA_CRL_VALUE
//Data_02:
//   .word 500000                         // counter delay value

Reset_Handler:

  LDRD R0, R1, Data_01                  // R0 = RCC BASE, R1 = GPIOA_CRL_VALUE [0x44244444]
  MOVS R2, #4                           // IOPAEN
  STR  R2, [R0, RCC_APB2ENR_OFFSET]     // RCC->APB2ENR = IOPAEN - ENABLE GPIO PORT A CLOCK

  SUB  R0, GPIOA_DIFFERENT           // R0 = GPIOA BASE
  STR  R1, [R0, GPIOA_CRL_OFFSET]       // SET PIN PA5 AS OUTPUT [R2 = 0x44244444]

  /* BLINK SECTION */
//---
LoopForever:

  LDR.N R1, Data_02                       // R1 = count value from 500 000

Delay_01:
  SUBS R1, #1
  BNE  Delay_01                         // delay count

  EOR  R2, GPIOA_BIT5_VALUE             // change LED bit value
  STR  R2, [R0,GPIOA_ODR_OFFSET]        // blinking LED by stor value to GPIOA->ODR

  b LoopForever                         // over again
//---
.align (2)
Data_02:
   .word 500000                         // counter delay value
/*******************************************************************************/
/************************ (C) COPYRIGHT STMicroelectonics *****END OF FILE****/ 

 

repo z zabawy z blupillem:

kod przepisujący się z flasha do ram i naprzemiennie po resecie wykonujący się z RAM, lub ROM 🙂

https://github.com/wegi1/BLUE_PILL_BLINK_LED_RAM_EXECUTE

Poniżej baaaardzo dobry kurs bare metal (dla GCC)

 Autor znakomicie pokazał, jak dogadywać się z linkerem, z kompilatorem, assemblerem, jak zostawić "przejściowe" pliki asm ze skompilowanych plików C np jak wygląda funkcja main skompilowana do assemblera. (gdzieś w moich postach też to pokazałem...)

Bardzo dobre wstawki można znaleźć w filmach na YT ControllersTech, w odcinkach o MPU, gdzie pokazuje jak zlinkować zażądaną zmienną pod wskazanym adresem absolutnym w RAM.

Miro Samek, to w zasadzie cały Modern Embedded Course, bo praktycznie wszędzie debuguje i spogląda jak to wygląda w ASM

 

Moja wiedza o ASM ARM jest mała, kiedyś co nieco programowałem w ASM na C64, gdzie potrafiłem np. zakodować 320 realtime calculated vectordots 50FPS. I program z tego linku:

Jest bardzo dokładnym portem assemblera do C metody liczenia vectordotów. I cały program w C włącznie z deklaracjami, separacjami, pustymi klamrami itd zajmuje raptem około 350 codelines, co w ASM C64 (6502) zajmowało około 2000 linii kodu. Przyznam szczerze i ze wstydem, że niektóre optymalizacje z tej metody zrozumiałem dopiero po wielu miesiącach zastanawiania się nad tym, jak to działa (mowa o kodzie na C64). I tak dla zabawy i przykładu 6502 nie miał rozkazów mnożenia, ale można to było zrobić np przez lookup table dla zadanej wartości np:

// X = X*5

  lda #$07 // X=7
  ldx #$05 // *5
  lda multab, x
    
    lda multab,x to coś na kształt w C data = multab[5]
    
    multab db 0, 7,14,21,28, 35...

Więc chociaż ARM ma rozkazy mnożenia, to zostało to identycznie skopiowane jak w C64, bez rozkazów mnożenia - tak po prostu prove of concept, czy dla zasady 🙂 No i możesz sobie wyobrazić, że jeżeli to na 8bit 1MHz hulało 320dots 50FPS, to ile można wyciągnąć stosując to na 400MHz ARM32bit 🙂

  • Lubię! 2
Link do komentarza
Share on other sites

(edytowany)
4 godziny temu, virtualny napisał:

Nie nie, wrzucasz jakikolwiek kod (lub prawie jakikolwiek) pod 0x08000000 bez jakiejkolwiek tablicy wektorów, bez reset handlera i stack_pointer_value (bo to niby są te 2 główne wartości żeby odpalić proca) i odpali się twój kod nawet bez 2 pierwszych Uint32_t values of vector table(!!!) Sprawdź - napisz goły kod w assemblerze mrugający diodą, potem wrzuć go pod 0x08000000 i zobaczysz, że dioda zacznie mrugać! - Ja sprawdziłem.

Zacząłem się po prostu domyślać, że jest to jakiś specjalny stan procesora natychmiast po resecie i PC jest ładowany z kodu, co na 99% musi wywołać jakiś har fault handler, że PC nie jest ustawiony w przestrzeni zamapowanego przez procesor RAM, tylko na randomowy nieistniejący adres i wówczas na pewno musi następować ze strony proca jakieś automatyczne ustawienie PC dokładnie na 0x08000000. Nie wiem czy ma taką możliwość CM3, ja sprawdzałem na jakimś NUCLEO z CM4

Przy okazji np kod dla NUCLEO-F1036RB bodaj 48 bajtów do ledblinka:

/**
 ******************************************************************************
 * @file      startup_stm32f103rbtx.s
 * @author    Auto-generated by STM32CubeIDE
 * @brief     STM32F103RBTx device vector table for GCC toolchain.
 *******************************************************************************
 */
//---
#define RCC_BASE               0x40021000
#define RCC_APB2ENR_OFFSET     0x18
#define GPIOA_BASE             0x40010800
#define GPIOA_DIFFERENT        (RCC_BASE - GPIOA_BASE)
//---
#define GPIOA_CRL_VALUE        0x44244444
#define GPIOA_CRL_OFFSET       0
#define GPIOA_ODR_OFFSET       0x0C
//---
#define GPIOA_BIT5_VALUE       (1 << 5)
//---
#define DELAY_VALUE            500000
//---
#define CRL_DIFFERENT          (GPIOA_CRL_VALUE - GPIOA_BASE)
//---
//===============================================================================

.syntax unified
.cpu cortex-m3
.fpu softvfp
.thumb

/******************************************************************************
*
* The STM32F103RBTx vector table.  Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
******************************************************************************/
  .section .isr_vector,"a",%progbits
  //.type g_pfnVectors, %object


g_pfnVectors:
  .word 0x20005000
  .word Reset_Handler


  .section .text.Reset_Handler
  .type Reset_Handler, %function

Data_01:
   .word RCC_BASE
   .word GPIOA_CRL_VALUE
//Data_02:
//   .word 500000                         // counter delay value

Reset_Handler:

  LDRD R0, R1, Data_01                  // R0 = RCC BASE, R1 = GPIOA_CRL_VALUE [0x44244444]
  MOVS R2, #4                           // IOPAEN
  STR  R2, [R0, RCC_APB2ENR_OFFSET]     // RCC->APB2ENR = IOPAEN - ENABLE GPIO PORT A CLOCK

  SUB  R0, GPIOA_DIFFERENT           // R0 = GPIOA BASE
  STR  R1, [R0, GPIOA_CRL_OFFSET]       // SET PIN PA5 AS OUTPUT [R2 = 0x44244444]

  /* BLINK SECTION */
//---
LoopForever:

  LDR.N R1, Data_02                       // R1 = count value from 500 000

Delay_01:
  SUBS R1, #1
  BNE  Delay_01                         // delay count

  EOR  R2, GPIOA_BIT5_VALUE             // change LED bit value
  STR  R2, [R0,GPIOA_ODR_OFFSET]        // blinking LED by stor value to GPIOA->ODR

  b LoopForever                         // over again
//---
.align (2)
Data_02:
   .word 500000                         // counter delay value
/*******************************************************************************/
/************************ (C) COPYRIGHT STMicroelectonics *****END OF FILE****/ 

 

Nie mam teraz w domu żadnego zestawu na którym mógłbym wypróbować to co mówisz. Dokumentacja ARM Cortex-M4 mówi wyraźnie, że po podłączeniu zasilania MCU wchodzi w stan Reset i rozpoczyna wykonanie programu od wektora przerwania dla "Reset Handler"- tak jak masz to zrobione w przytoczonym kodzie. Ciężko też jest mi sobie wyobrazić wykonanie programu bez stosu.

A nie masz może definicji "Reset_Handler" w skrypcie linkera (bo chyba daje się to tak zrobić)?

Dzięki za linki do kursu - właśnie się z nim zapoznaje 🙂

Pozdrawiam

Edytowano przez FlyingDutch
Link do komentarza
Share on other sites

W Twoim kursie (Lekcja 74) ten nieszczęsny przykład substrakcji 64 bit też jest dokładnie w takiej samej postaci:

914533833_fake64bitsubstraction.thumb.jpg.aac2c500fa90f9ed4657b2e8b59ad411.jpg

             sub r0, r0, r2, lsl #2  
             add r1, r1, r3, lsr #3  

 

Nie wiem jaki skrót myślowy przyświecał Gbatiemu i reszcie teamu prowadzącego kurs, bo wg nich jest OK

Teraz zobacz pytanie do tej lekcji z Twojego kursu:

Cytat

ask: why are we shifting the bits in 64-bit subtraction?

 

I odpowiedź:

Cytat

ans: Hi, using shift will subtract the lower half 32-bit from the upper 32-bit and update the carry flag.

 

Wygląda że kursanci mają większe pojęcie o assemblerze niż ten team 3 instruktorów...

Teraz zobacz w drugim kursie o asm - moja dyskusja z nimi:

Cytat

This code offered as 64 bit substraction in lesson 45 doesn't work

             sub r0, r0, r2, lsl #2 (lsl #2 totally missunderstand)
             add r1, r1, r3, lsr #3 (addition + shifting as before)
Cytat

 

and even CAN'T work from 2 reasons:

1. didn't use flags update option in first opcode
2. didn't check and calculate the CARRY flag in the second opcode

let assume 64bit data = 0x22222222 22222222 minus 0x11111111 11111111

is equal to 0x11111111 11111111


below code running on the Nucleo F446RE

 

Reset_Handler:
             mov r0, 0x22222222
             mov r1, r0
             mov r2, 0x11111111
             mov r3, r2
 
             sub r0, r0, r2, lsl #2
             add r1, r1, r3, lsr #3
stop:
             b stop
R0 = 0xDDDDDDDE
R1 = 0x24444444

RESULT: 0x24444444 DDDDDDDE

 

I odpowiedź:

 

Cytat

ans: Thanks for providing further details. I see your point. That lesson shall be updated. Israel Gbati is the author of this course also. This course is supposed to be the GNU version of the ARM assembly course. Thanks for your patience.

 

 

Dobra, a teraz rozwiązałem zagadkę uruchamiania programu bez SP_init i wektoru resetu.

Muszę wyjaśnić całe tło... A więc np mój program w assemblerze do mrugania diodą w NUCLEO446RE ma 48 bajtów.

                .syntax unified
                .cpu cortex-m4
                .fpu softvfp
                .thumb

                .global Reset_Handler

//                .section .isr_vector,"a",%progbits
//     .word 0x20005000
//     .word Reset_Handler

                .section .text.Reset_Handler
                .type Reset_Handler, %function
Reset_Handler:
  LDR.N   R0, DATA_01
  MOVS    R2, #1
  STR     R2, [R0, #0x30]

  SUBS    R0, R0, 0x3800
  LDR.N   R1, DATA_02
  STR     R1, [R0, #0x00]

LoopForever:
  LDR.N   R3, DATA_03
LOOP_02:
  SUBS    R3, R3, #1
  BNE     LOOP_02
  EOR     R2, 0x20
  STR     R2, [R0, 0x14]

  b LoopForever

.align 2
DATA_01:
  .word 0x40023800
DATA_02:
  .word 0xA8000400
DATA_03:
  .word 500000

 

I ten program jak powyżej pozbawiony tych dwóch wartości z vector table (SP i @Reset_Handler) ma 40 bajtów. Można też odkomentować te 3 linie, wówczas program będzie miał 48 bajtów i będzie posiadał init SP I wskaźnik do Reset_Handler.

No i teraz co zrobił Gbati:

W CubeIDE najpierw stworzył pierwszy pusty program dla NUCLEO, w którym dodał plik assemblera (rozszerzenie "s") i umieścił w nim przykładowy kod. Zespolił ten kod z plikiem startup.asm, wyeksportował nazwę swojej funkcji "__main" i wywołał ją z końcowego etapu inicjalizacji z pliku startup.s. Pokazał debug, że działa w funkcji __main. Potem jeszcze raz stworzył taki sam projekt, a następnie usunął z niego plik startup.s, dodał nazwę funkcji Reset_Handler, opublikował ją dla linkera, dodał sekcję dla reset handlera i uruchomił debug - o dziwo zadziałał!

Programy w załączniku

F446RE_Examples.zip

Teraz idąc tropem Gabtiego powtórzyłem jego manewry, a w środku był mój program do mrugania ledem w NUCLEO446RE. I rzeczywiście, mój minimalny program do blinkania ledem ma 48 bajtów i po usunięciu sekcji z wektorami "isr_vector" ma 40 bajtów (czyli o te 8 bajtów mniej jak te 2 wektory) i ten 40 bajtowy program uruchamia się na NUCLEO446RE i mruga tą diodą (!!!)

Uruchamiam ST-Link utility i podglądam zawartość pod 0x08000000 i faktycznie jest tam 40 bajtów bez tych dwóch wektorów !

ALE...

1. Po HARD RESECIE dioda przestaje migać

2. Tak samo program nie działa (ten 40 bajtowy), jeżeli wgram binkę przez ST-Link Utility (ten 48 bajtowy z wektorami działa)

3. Program działa i uruchamia się TYLKO wówczas, gdy wgrywam go spod otwartego projektu w STM32CubeIDE.

 

A więc wniosek:

Upubliczniony dla skryptu linkera adres funkcji "Reset_Handler" jest użyty przez środowisko do programowania procesora, a adres "Reset_Handler" jest "WSTRZYKIWANY" debuggerem do rejestru PC - nie ma innej opcji.

Mała uwaga - w skrypcie linkera musi się znajdować taki wpis:

/* Entry Point */
ENTRY(Reset_Handler)

 

Sprawdziłem, także działa to dla NUCLEO103RB

pzdr

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

@FlyingDutch witam. Dla kontrolerów Pic 16 są gotowe biblioteki dla skomplikowanych operacji arytmetycznych na przykład pierwiastek kwadratowy, liczby zmiennoprzecinkowe itp. algorytmy opisane są w notach katalogowych w języku Assembler przez Microchip. Są to przykłady dla kontrolerów 8bit ale jak już mowa o assemblerze... Jedną z takich not jest np. AN660 (floating point). Taka ciekawostka o których się mało mówi...

Pozdrawiam.

  • Lubię! 2
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.