architektura armmarpe/swiss/akarm.pdf · 2012-03-01 · i hardfault – priorytet −1. i...
TRANSCRIPT
Architektura ARM
Marcin Peczarski
Instytut Informatyki Uniwersytetu Warszawskiego
17 lutego 2012
ARM
I Międzynarodowa firma, mająca główną siedzibę w Cambrdgew Wielkiej Brytanii.
I Projektuje i sprzedaje licencje na rdzenie ARM.I Nie produkuje chipów.I Podobno ok. 3/4 telefonów komórkowych i systemów
wbudowanych zawiera rdzenie ARM.
ARM = Advanced RISC Machines
I Zestaw instrukcji wzorowany na RISC, aby łatwo potokować:
ldr r3, [r1, #36]adds r2, r3, #1
I RISC-owe wywoływanie procedur:
bl etykietabx lr
I Prawie każda instrukcja może być wykonywana warunkowo:
movge r7, r4
I Argument można przesunąć przed wykonaniem operacji:
orr r1, r1, r4, lsl #12str r2, [r4, r0, lsl #2]
I Występują instrukcje typowo CISC-owe:
push {r4, r5, lr}; prolog funkcjipop {r4, r5, pc}; epilog funkcji
Cortex-M3/M4
I Rdzenie do zastosowań mikrokontrolerowychI CienkokońcówkoweI Jednolita przestrzeń adresowa – architekura typu PrincetonI Osobne szyny do pamięci danych i programu – organizacja
typu Hardward
I Na kolejnych slajdach omawiam architekturę Cortex-M3,posługując się przykładami przetestowanymi namikrokontrolerach z rodziny STM32.
Bezpośrednie adresowanie bitów
bitAddr = baseAddr + 32× dwordOffset + 4× bitNumber
Źródło: http://www.kimura-lab.net
Organizacja pamięci programu
Flash
.isr vector
.text
.rodata
.data
0x08000000
sidata = etext
.data
.bss
heap
stack
sdata = 0x20000000
edata = sbss
ebss
end
estackRAM
Kod startowy – deklaracje
extern unsigned long _sidata;extern unsigned long _sdata;extern unsigned long _edata;extern unsigned long _sbss;extern unsigned long _ebss;extern unsigned long _estack;
int main(void);
Kod startowy wykonywany po wyzerowaniu
static void Reset_Handler(void) {unsigned long *src, *dst;
for (dst = &_sdata, src = &_sidata;dst < &_edata;++dst, ++src)
*dst = *src;
for (dst = &_sbss; dst < &_ebss; ++dst)*dst = 0;
main();
for (;;);}
Tablica przerwań
__attribute__ ((section(".isr_vector")))void (* const g_pfnVectors[])(void) = {(void*)&_estack,Reset_Handler,
/* Przerwania rdzenia Cortex-M3 */NMI_Handler,HardFault_Handler,MemManage_Handler,BusFault_Handler,UsageFault_Handler,...SysTick_Handler,
/* Przerwania układów peryferyjnych */...};
Zaślepki dla nieużywanych przerwań
static void Default_Handler(void) {for (;;);}
#define WEAK __attribute__ ((weak))
void WEAK NMI_Handler(void);
#pragma weak NMI_Handler = Default_Handler
Połączenie tego w całość (1)
MEMORY{FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128KRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
_minimum_stack_and_heap = 2560;
SECTIONS{...
}
Połączenie tego w całość (2)
.text :{KEEP(*(.isr_vector))*(.text)*(.text.*)*(.rodata)*(.rodata.*). = ALIGN(4);_etext = .;_sidata = _etext;
} >FLASH =0xff
Połączenie tego w całość (3)
.data : AT(_sidata){. = ALIGN(4);_sdata = .;*(.data)*(.data.*). = ALIGN(4);_edata = .;
} >RAM =0
Połączenie tego w całość (4)
.bss :{. = ALIGN(4);_sbss = .;*(.bss)*(.bss.*)*(COMMON). = ALIGN(4);_ebss = .;
} >RAM
Połączenie tego w całość (5)
_estack = (ORIGIN(RAM) + LENGTH(RAM)) & 0xFFFFFFFC;
ASSERT(_ebss + _minimum_stack_and_heap <= _estack,"There is not enough space in RAM.")
PROVIDE(end = _ebss);
...
Zaświecenie diody – hard-code
I Dioda jest podłączona do wyprowadzenia PB8.
typedef volatile uint32_t reg_t;
uint32_t tmp;
*(reg_t*)(0x40021000 + 0x18) |= 0x00000008;
tmp = *(reg_t*)(0x40010c00 + 0x04);tmp &= 0xfffffff0;tmp |= 0x00000001;*(reg_t*)(0x40010c00 + 0x04) = tmp;
*(reg_t*)(0x40010c00 + 0x10) = 1 << 8;
Biblioteki
I CMSIS – Cortex MicrocontrollerSoftware Interface Standard
I STM32. . . x StdPeriph Driver –biblioteka obsługująca peryferiespecyficzne dla danego modelumikrokontrolera
Zaświecenie diody z użyciem CMSISI Trzeba włączyć właściwy plik nagłówkowy (jeden):#include <stm32f10x.h>#include <stm32f2xx.h>#include <stm32l1xx.h>
I Potem już jest łatwiej:uint32_t tmp;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
tmp = GPIOB->CRH;tmp &= ~(GPIO_CRH_MODE8_1 |
GPIO_CRH_CNF8_0 | GPIO_CRH_CNF8_1);tmp |= GPIO_CRH_MODE8_0;GPIOB->CRH = tmp;
GPIOB->BSRR = GPIO_BSRR_BS8;
Zaświecenie diody z użyciem StdPeriph DriverI Trzeba włączyć właściwe pliki nagłówkowe, np.:#inlcude <stm32f10x_gpio.h>#inlcude <stm32f10x_rcc.h>
I Potem już jest łatwo, choć nie zawsze wydajnie:GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_StructInit(&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_WriteBit(GPIOB, GPIO_Pin_8, Bit_SET);
CMSIS dostarcza funkcji dla operacji niedostępnych wjęzyku C
I Włączamy odpowiedni plik nagłówkowy:
#include <stm32...x.h>
I I możemy używać różnych ciekawych funkcji:
#define htons(x) __REV16(x)#define htonl(x) __REV(x)
Używając GCC, można je zdefiniować nieco bardziejefektywnie
#if defined __GNUC__#define htons(x) ({ \uint16_t result; \asm ("rev16 %0, %1" : "=r" (result) : "r" (x)); \result; \})
#define htonl(x) ({ \uint32_t result; \asm ("rev %0, %1" : "=r" (result) : "r" (x)); \result; \})#endif
Rejestry
I R0 do R12 – rejestry ogólnego przeznaczeniaI SP (R13, MSP, PSP) – wskaźnik stosuI LR (R14) – adres powrotuI PC (R15) – licznik programuI PSR (APSR, IPSR, EPSR) – rejestr znacznikówI PRIMASK, FAULTMASK, BASEPRI – rejestry maskujące
przerwaniaI CONTROL – rejestr sterujący trybami pracy rdzenia
Przerwania
Reset Zerowanie rdzeniaNMI Przerwanie niemaskowalneHard fault Błąd podczas obsługi przerwaniaMemory mana-gement fault
Naruszenie ochrony pamięci
Bus fault Błąd szyny pamięciUsage fault Niepoprawna instrukcja lub argument instrukcjiSupervisor call Wywołanie usługi systemu operacyjnego za po-
mocą instrukcji SVCPendSV Wywołanie planisty w celu przełączenia kontek-
stu, w założeniu niski priorytetSysTick Licznik systemowy, cykliczne przerwanie syste-
mu operacyjnego, w założeniu wysoki priorytetIRQ0. . . IRQ67 Przerwania zgłaszane przez peryferie
ABI funkcji i przerwań
I Pierwsze 4 argumenty funkcji są przekazywane w rejestrach R0do R3, pozostałe przez stos.
I Funkcja może dowolnie modyfikować rejestry R0 do R3 i R12.I Funkcja musi przywrócić wartości rejestrów R4 do R11, LR
i SP sprzed wywołania.I Przed wywołaniem procedury przerwania procesor odkłada na
stos rejestry R0 do R3, R12, adres powrotu, PSR, LR.I Funkcja, która ma obsługiwać przerwanie, to najzwyklejsza
funkcja języka C o sygnaturze
void Handler(void);
I Nie są potrzebne żadne dodatkowe atrybuty!attribute ((interrupt))
Łańcuchowa obsługa przerwań
Źródło: http://www.arm.com
I Może dochodzić do zagłodzenia!
ABI przerwań, problem
I Skąd procesor wie, czy instrukcja bx lr jest powrotem zezwykłej funkcji, czy z obsługi przerwania (trzeba coś zdjąć zestosu)?
I Czy ktoś zauważył problem?
Tryby pracy rdzenia
Źródło: http://sigalrm.blogspot.com
I Są dwa stosy:I MSP dla trybów uprzywilejowanych (ang. privileged thread,privileged handler),
I PSP dla trybu użytkownika (ang. user thread).
ABI przerwań, rozwiązanie problemu
I Przed wywołaniem funkcji obsługującej przerwanie procesorzapisuje do rejestru LR wartość:
I 0xfffffff1 – powrót do obsługi innego przerwania (ang.privileged handler), odtwórz stan z MSP, po powrocie używajMSP;
I 0xfffffff9 – powrót do wątku uprzywilejowanego (ang.privileged thread), odtwórz stan z MSP, po powrocie używajMSP;
I 0xfffffffd – powrót do wątku użytkownika (ang. userthread), odtwórz stan z PSP, po powrocie używaj PSP.
Priorytety przerwań
I Przerwanie może mieć priorytet od −3 do 15.I Czym mniejsza wartość, tym większy priorytet.I Trzy przerwania mają ustalony, niekonfigurowalny priorytet:
I Reset – priorytet −3,I NMI – priorytet −2,I HardFault – priorytet −1.
I Pozostałym przerwaniom można nadać priorytet, który jestliczbą 4-bitową i składa się z dwóch pól:
I priorytet wywłaszczania,I podpriorytet.
I Liczba bitów przeznaczonych na priorytet wywłaszczania jestkonfigurowalna.
I Pozostałe bity są przeznaczone na podpriorytet.
Numery przerwań
I CMSIS definiuje typ wyliczeniowy:
typedef enum IRQn {...};
I Przerwania zgłaszane przez rdzeń mają ujemne numery:
NonMaskableInt_IRQn = -14,MemoryManagement_IRQn = -12,BusFault_IRQn = -11,UsageFault_IRQn = -10,...SysTick_IRQn = -1,
I Przerwania zgłaszane przez peryferie mają nieujemne numery:
WWDG_IRQn = 0,...TIM2_IRQn = 28,...
Blokowanie przerwań o konfigurowalnych priorytetachI Użyjemy CMSIS:
#include <stm32f10x.h>#include <stm32f2xx.h>#include <stm32l1xx.h>
I Makro deklarujące zmienną, przechowującą poprzedni stan:
#define SYS_ARCH_DECL_PROTECT(x) \uint32_t x
I Makro zapisujące aktualny stan i blokujące przerwania:
#define SYS_ARCH_PROTECT(x) \(x = __get_PRIMASK(), __disable_irq())
I Makro przywracające poprzedni stan:
#define SYS_ARCH_UNPROTECT(x) \__set_PRIMASK(x)
Użycie
SYS_ARCH_DECL_PROTECT(x);
/* Tutaj może zostać zgłoszone przerwaniew dowolnym momencie. */
SYS_ARCH_PROTECT(x);
/* Tutaj kod wykonuje się przyzablokowanych przerwaniach. */
SYS_ARCH_UNPROTECT(x);
Wersja makr dla GCC
#if defined __GNUC__#define SYS_ARCH_DECL_PROTECT(x) \uint32_t x
#define SYS_ARCH_PROTECT(x) ({ \asm volatile ( \"mrs %0, PRIMASK\n\t" \"cpsid i" : \"=r" (x) : \); \})
#define SYS_ARCH_UNPROTECT(x) ({ \asm volatile ("msr PRIMASK, %0" : : "r" (x)); \})#endif
Konfigurowanie priorytetu przerwania
I Wystarczą 4 priorytety wywłaszczania i 4 podpriorytety:
#define PRE_PRIO_BITS 2
#define SET_IRQ_PREEMPTION_PRIORITY_BITS() \NVIC_SetPriorityGrouping(7 - PRE_PRIO_BITS)
I Priorytety (wywłaszczania) wygodnie jest nazwać:
#define HIGH_IRQ_PRIO 1#define MIDDLE_IRQ_PRIO 2#define LOW_IRQ_PRIO 3
I CMSIS dostarcza kilka funkcji:
NVIC_SetPriority(SysTick_IRQn,NVIC_EncodePriority(NVIC_GetPriorityGrouping(),
HIGH_IRQ_PRIO, 0));
Konfigurowanie priorytetu przerwania, cd.
I Przerwania zgłaszane przez peryferie można też konfigurowaćza pomocą StdPeriph Driver:
#include <misc.h>
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority= LOW_IRQ_PRIO;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);
Blokowanie przerwań wg priorytetu
I Makro deklarujące zmienną, która przechowuje informację oblokowanych przerwaniach:
#define IRQ_DECL_PROTECT(prv) \uint32_t prv
I Makro zapisujące stan aktualnie blokowanych przerwańi blokujące przerwania o priorytecie niższym lub równym lvl:
#define IRQ_PROTECT(prv, lvl) \(prv = __get_BASEPRI(), \__set_BASEPRI((lvl) << (8 - PRE_PRIO_BITS)))
I Makro przywracające poprzedni stan blokowania przerwań:
#define IRQ_UNPROTECT(prv) \__set_BASEPRI(prv)
Wersja makr dla GCC
#if defined __GNUC__#define IRQ_DECL_PROTECT(prv) \uint32_t prv#define IRQ_PROTECT(prv, lvl) ({ \uint32_t tmp; \asm volatile ( \"mrs %0, BASEPRI\n\t" \"movs %1, %2\n\t" \"msr BASEPRI, %1" : \"=r" (prv), "=r" (tmp) : \"i" ((lvl) << (8 - PRE_PRIO_BITS)) \); \})#define IRQ_UNPROTECT(prv) ({ \asm volatile ("msr BASEPRI, %0" : : "r" (prv)); \})#endif
Inna wersja makr dla GCC
#if defined __GNUC__#define IRQ_DECL_PROTECT(prv) \uint32_t prv#define IRQ_PROTECT(prv, lvl) ({ \uint32_t tmp; \tmp = (lvl) << (8 - PRE_PRIO_BITS); \asm volatile ("mrs %0, BASEPRI" : "=r" (prv)); \asm volatile ("msr BASEPRI, %0" : : "r" (tmp)); \})#define IRQ_UNPROTECT(prv) ({ \asm volatile ("msr BASEPRI, %0" : : "r" (prv)); \})#endif
O czym jeszcze można opowiedzieć?
I Tryby uśpieniaI Wsparcie dla DSPI Przykłady użycia peryferiiI . . .