Skocz do zawartości

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


FlyingDutch

Pomocna odpowiedź

(edytowany)

Cześć,

zanim przejdę do bardziej zaawansowanych przykładów, chciałbym podać kilka linków do dokumentacji pomocnej przy rozpoznawaniu tematu. Ogólna dokumentacja ARM Cortex_M4 jest dostepna na stronie firmy ARM pod tym adresem:

https://developer.arm.com/documentation/ddi0439/b

Tutaj opis podstawowych rejsestrów CPU:

https://developer.arm.com/documentation/ddi0439/b/Programmers-Model/Processor-core-register-summary?lang=en

A tutaj opis mnemoników assemblera:

https://developer.arm.com/documentation/ddi0439/b/Programmers-Model/Instruction-set-summary/Cortex-M4-instructions?lang=en

-----------------------------------------------

Teraz kilka słów jak tworzyć nowe projekty w assemblerze ARM dla IDE "Keil uVision5:. Po instalacji środowiska "Keil uVison5" w OS Windows10 - uruchomienie pobranego instalatora i w większośći przypadków wybranie opcji domyslnych instalacji - uruchamiamy IDE. Nastepnie klikamy ikonkę menu opisaną jako "Pack Instaler" (ostatnia w pasku na dole po prawej). Otworzy sie podobne okno jak na zrzucie ekranu:

Keil_PackInstaller.thumb.png.f479f0cb05d7cba5570140286f046498.png

W lewej części ekranu wybiaramy z listy "STMicroelectronics" a nastepnie "STM32F4 Series". W prawej części ekranu ukaże się opis pakietu "Keil::STM32F4xx_DFP" - klikamy po jego lewej części klawisz "Insta;". Po dłuższym czasie (download przez złącze internetowe) pakiet zostanie zainstalowany.

Teraz możemy utworzyć nowy projekt, który będzie mół być implementowany w assemblerze ARM Cortex-M4. Aby to wykonać należy najpierw z głownego menu (u góry) wybrać opcję: "Project" -> "New uVision Project" - otworzy się okno:

Keil5_NewProject_.thumb.png.41324c74a8ab852fe30e14bf102f205f.png

należy utworzyć nowy katalog dla projektu i w polu "Nazwa pliku" podać nazwę projektu (bez rozszerzenia) i potem kliknąć klawisz "Zapisz". Otworzy się okno z wyborem targetu (płytki lub MCU) docelowego dla projektu:

Keil5_WyborMCU.thumb.png.9f2dfb50af638e694b3eae2b85774579.png

Wybieramy np. "STMF411"-> "STM32F411RE"->"STM32F411RET.." dla popularnej płytki rozwojowej Nucleo-F411RE:

https://botland.com.pl/stm32-nucleo/3363-stm32-nucleo-f411re-stm32f411re-arm-cortex-m4-5904422331764.html?cd=18298825651&ad=&kd=&gad_source=1&gclid=Cj0KCQiA1rSsBhDHARIsANB4EJamSkrH7bxSKv7UkMEmm5U-tIAIz2K9sRLG5-VW_QUQUaM4el1XQDsaAtWqEALw_wcB

Oczywiście możemy wybrać jako docelową inną płytkę z Cortexem-M4 (nawet innego producenta np. TI lub NXP - wtedy trzeba za pomocą "Pack installer" doinstalować do IDE Keil odpowiednie pakiety). UWAGA! - aby przetestować podane programy nie jest potrzebne kupowanie żadnej płytki ewalucyjnej - możemy to zrobić wykorzystując symulator MCU dla ARM Cortex-M4 zainstalowany łącznie z IDE Keil (i tak domyślnie będą skonfigurowane podane projekty).

Po wyborze lytki (lub MCU), trzeba jeszcze wybrać odpowiednie biblioteki dla projektu - otworzy się okno o nazwie "Manage Run-Time Environment":

Keil5_WyborLibrary.thumb.png.81c11d55a1cbdac7f5077bcd8eb60fd8.png

Z listy po lewej stronie ekranu rozwijamy "CMSIS" i zaznaczamy "ptaszka" przy opcji "CORE", a nastepnie rozwijamy "Device" i zaznaczamy opcję "Startup" i na koniec klikamy klawisz "OK". Projekt zostanie utworzony i będzie widoczny w panelu "Projects" IDE Keil.

Keil5_UtworzonyProjekt_.thumb.png.54e1b778306f5f417ccd47fb5bd48567.png

Nastepnie rozwijamy projekt po lewej stronie i klikamy prawym klawiszem myszki na "Source Group 1" i z okna, które się rozwinie wybiaramy opcję "Add New item to Group 'Source group1' ..."

DodanieNowegoPliku.thumb.png.30145d3ef94a5ebc600989837644aa24.png

Otworzy się okno wyboru nowego pliku projektu:

Keil5_NowyPlikASM.thumb.png.6311a4c667d364f462369667fe57d5e2.png

Zaznaczmy w lewej stronie okna "ASsm file (.s)" i w polu "Name" wpisujemy main (bez rozszerzenia) i na koniec klikamy przycisk "Add" w dolnej części okna. Nowy plik zostanie dodany do projektu - w panelu "Project" zaznaczamy "main.s" - dwuklik na tej pozycji otworzy plik "main.s" w edytorze kodu źródłowego IDE:

Keil5_ItemDodany_.thumb.png.55df4e9d36430abcab572f934d4204d9.png

Teraz możemy przejść do edytora kodu źródłowego po lewej stronie i zacząć pisać kod programu w assemblerze ARM Cortex-M4.

Na samym początku napiszmy bardzo prosty program, który oblicza sumę trzech podanych liczb (integer) x = Q + R +S. Najpierw zadeklarujemy trzy etykiety dla zmiennych Q,R,S:

Q		EQU		2
R		EQU		4
S		EQU		5
	

nastepnie możemy podać nazwę funkcji i entry point dla aplikacji:

		AREA	SimpleEquation,CODE, READONLY
		ENTRY
		EXPORT	__main
			

Zadeklarowaliśmy segment kodu o nazwie "SimpleEquation" i atrybucie "READONLY" (bo jest to kod źródłowy w pamięci Flash MCU).

Ponieważ jest to punkt wejściowy aplikacji podajemy "ENTRY" i w

EXPORT	__main

eksporutjemy funkcję o nazwie "__main" - ta nazwa jest domyslnie ustawiona w StartUp file - gdybysmy chcieli zmienić jej nazwę jest to mozliwe, ale wymaga dodatkowej konfiguracji projektu. Teraz możemy wpisać cały kod programu:

; x = Q + R + S
; let Q=2, R=4, S=5 


Q		EQU		2
R		EQU		4
S		EQU		5
	
	
	
		AREA	SimpleEquation,CODE, READONLY
		ENTRY
		EXPORT	__main
			

__main
		MOV		R1,#Q
		MOV		R2,#R
		MOV		R3,#S
		
		ADD		R0,R1,R2
		ADD		R0,R0,R3

Stop	B		Stop

		END

"__main" to etykieta funkcji. nastepnie za pomocą trzech instrukcji MOV do rejestrów Ro, R1, R2 kopiujemy wartości zmienncyh Q,R,S i sumujemy ich wartości za pomocą dwóch instrukcji ADD. Wynik dodawania jest na końcu w rejestrze RO. "Stop" to etykieta nieskończonej pętli (B Stop) - to skok do tej etykiety.  Instrukcja (dyrektywa) END kończy blok kodu assemblera.

Przyszedł czas aby zbudować projekt w tym celu klikamy drugą ikonkę w dolnej części menu o nazwie "build" lub klikamy klawisz F7:

Buil1_project_.thumb.png.bd2b6407e54795a6ccf1feab7c28a53a.png

Okazuje się, że wystepuje nastepujący bład:

Cytat

Build started: Project: SimpleEquation
*** Target 'Cortex-M4' uses ARM-Compiler 'Default Compiler Version 5' which is not available.
*** Please review the installed ARM Compiler Versions:

Błąd ten wynika z faktu, iż mamy komilator w wersji 6.19, a domyslnie jest ustawiana wersja 5. Aby wyeliminować ten błąd należy:

Compiler-wersion1.thumb.png.53dd71c0643135dc86a5a5bbcd9c9a89.png

kliknąć ikonkę "Options for target" - otworzy się okno z opcjami konfiguracji platformy docelowej projektu:

Options_target1.thumb.png.be0ab27739d8baf3ec06add82e0013fe.png

Teraz w zakładce "Target" zaznaczamy opcję "Use microLIB" (potrzebna do pracy krokowej), oraz z listy "ARM compiler" wybieramy opcję "v6.19". Ostatnią opcją do ustawienia jest opcja debugowania w symulatorze - w tym celu przechodzimy do zakładki "Debug" i z lewej strony zaznaczamy radio button "Use Simulator" (gdybyśmy chcieli uruchamiać program na zestawie developerskim należy wybrać odpowiedni target w tej zakładce):

OptionsDebug1.thumb.png.19221ad9311b4e7674bb6b593d4b43bb.png

Klikamy klawisz "OK", który zamknie okono z opcjami. Klikamy ponownie ikonkę "Build" lub wciskamy klawisz F7 - teraz projekt powinien sie zbudować poprawnie:

Cytat

Build started: Project: SimpleEquation
*** Using Compiler 'V6.19', folder: 'E:\Keil_v5\ARM\ARMCLANG\Bin'
Build target 'Cortex-M4'
assembling main.s...
assembling startup_stm32f411xe.s...
compiling system_stm32f4xx.c...
linking...
Program Size: Code=144 RO-data=408 RW-data=0 ZI-data=1024  
".\Objects\SimpleEquation.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed:  00:00:02

Możemy teraz przejśc do uruchomienia i debugowania projektu w tym celu klikamy ikonkę "Start/Stop Debug Session" lub wciskamy klawisze CTRL+F5:

Click_Debug.thumb.png.378f8a8e1acad7401492540dd6eccd8b.png

Nastapi przejście do perspektywy Debugowania (otworzą się nowe okna):

DebugPerspective_.thumb.png.92520fccd2eb77a7750a835b21a0d9e0.png

Z lewej strony otworzy się okno "Registers" w którym są wyświetlane rejsestry CPU ARM Cortex-M4. Rejestry od R0 do R15 są to podstawowe rejestry MCU - ich wartości są domyslnie wyświetlane heksadecymalnie. Poniżej są dodatkowe rejstry np dla FPU itp.

W oknie edytora kodu otworzy się plik Startup file - od niego zaczyna się wykonywanie programu po reseecie:

Kursor ustawi się na linii:

 LDR     R0, =SystemInit

Możemy teraz przejść do pracy krokowej - klikająć ikonki "Step" (F11) lub "Step Over" (F10):

StepRun.thumb.png.912c1c7785832a164b2031d3f02f8ecc.png

Widać, że w linii:

 LDR     R0, =__main

ładowany jest adres funkcji __main a w linii kolejnej :

BX      R0

Skok do tej funkcji. Właśnie tą funkcję zaimpletowaliśmy wcześniej w assemblerze. Teraz korzystając z ikonki "Step Over" (F10) - przeklikujemy się przez instrukcje w plikach system_stm32..  i startup_stm32.. (inicjalizacja MCU), aż kursor przejdzie do pliku "main.s" i zatrzyma się wlinii:

MOV		R1,#Q

DenbugMian.thumb.png.68e186d53154ffd99df28675d102c9f4.png

Teraz klikamy ikonkę "Step" (F11), aż kursor będzie w linii:

Stop	B		Stop

DebugResult_.thumb.png.4a088a874249da9051adf3ea416df178.png

Jak widać w rejestrach R1, R2,R3 są składowane zmienne Q, R, S a wynik (ich suma) jest w rejestrze R0 (wartość 0xB co wynosi 11 dziesiętnie). Wynik 2+4+5 wynosi 11, a więc jest poprawny. Tym sposobem utworzyliśmy pierwszy bardzo prosty projekt w assemblerze ARM Cortex-M4 i przedebugowalismy progarm korzystająć z programowego symulatora z IDE "Keil uVisin 5". W archiwum zip jest spakowany projekt do tego postu:

Simple_Equation_v1.zip

Pozdrawiam

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

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

36 minut temu, virtualny napisał:

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.

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 24 kB · 0 downloads

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

Dzięki za wyjaśnienie tego problemu - tak myślałem, że skrypt linkera musi mieć gdzieś zadeklarowany Reset_Handler. Natomiast wstrzykiwanie adresu Reste_Handler'a przez IDE do rejestru PC jest dla mnie zaskoczeniem.

Pozdrawiam

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

I teraz przynajmniej zrozumiałem, dlaczego STM w swoich startupach wykonuje taką pierwszą instrukcję:

    .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  
  ldr   sp, =_estack     /* set stack pointer */

Zawsze było to dla mnie bez sensu, bo przecież kilka cykli wcześniej SP hardware'owo zainicjował się wartością "_estack" spod 0x08000000.

Ale właśnie w przypadku takiego, konkretnego programowania przez środowisko CubeIDE ST-Linkiem wskaźnik stosu pozostaje w miejscu, w którym zatrzymał proca debugger - czyli najprawdopodobniej gdzieś w obszarze stosu, nawet w RAM, ale na 99% nie na szczycie stosu

Wcześniej myślałem że może być to używane do software'owego "miękkiego" resetu.

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

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • 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.