Pages List
List view
STM32 Peripheral
ARM Cortex-M3/M4 Processor - 2
Memory map and the bus interfaces of ARM Cortex-Mx Processor
Memory map
- Memory map은 Peripheral Register들의 mapping과 Processor Addressable Memory location range의 Memory들을 설명한다.
- Processor Addressable Memory location range는 Address bus의 크기에 따라 결정된다.
- Addressable Memory location에 서로 다른 각 영역들을 mapping한 것을 Memory map이라고 부른다.
다음은 Processor가 Peripheral들과 통신하는 과정에 대한 그림이다.
System bus라고 불리는 Bus interface는 Processor와 다양한 Memories / Peripherals들을 연결한다.
그리고 System bus는 32bit Address channel (bus)과 32bit Data channel (bus)을 가진다.
(여기서 Code memory는 프로그램의 명령을 저장하고, Data memory는 프로그램의 temporary data들을 저장한다.)
ADC Peripheral이 Processor가 필요로 하는 Data를 가지고 있고, Processor는 ADC의 Data를 fetch해서 Data memory에 저장하고 싶다고 가정하자.
Processor는 Peripheral들과 통신할 때, 가장 먼저 Address bus에 해당 Peripheral Register의 Address를 Address bus에 올린다.
그리고 해당 Peripheral의 Register가 Un-lock되면, 해당 Register의 Data가 Data bus에 올라온다.
그러면 그 Data는 Processor의 Internal Register로 전달되고, 다시 Data memory에 저장된다.
여기서 두 개의 명령을 실행하는데, 하나는 Load 명령이고 다른 하나는 Store 명령이다.
Load 명령은 ADC에서 Processor internal register로 값을 Load 하는 것이고, Store 명령은 Processor internal register에서 Data memory로 값을 Store 하는 것이다.
Cortex-Mx Processor에서 Bus interface는 32bit Address bus를 가지기 때문에, 4G의 Memory 주소를 관리할 수 있다. (0x0000_0000 ~ 0xFFFF_FFFF : Processor Addressable Memory location range)
이 4G의 Processor Addressable Memory location range에는 각 Memory 영역이 Fix되어 있고, 전체를 Memory Map이라고 부른다.
예를 들어 프로세서가 메모리0를 올리면, 코드 메모리와 대화하고 싶은 것이다.
이 코드 메모리 영역에서는 오로지 코드만 존재해야 한다.
임베디드 플래시든지 EEPROM이던지 ROM이던지 OTP이던지 이것들은 프로그램의 코드나 명령을 호스트할 수 있다.
SRAM 영역에서는 MCU vendor가 RAM을 연결해야 한다.
그리고 MCU vendor는 모든 페리페럴 레지스터들을 Peripheral 영역에 배치해야 한다.
만약 당신이 외부 RAM이 있다면, External RAM 영역에 배치해야 한다.
Memory map의 Code region을 살펴보자.
- Code region은 MCU Vendor가 Code memory를 연결하는 영역이다.
- Code memory에는 Embedded Flash, ROM, OTP, EEPROM 등이 있다.
- Processor는 기본적으로 Reset하자마자 Code memory에서 Vector table을 Fetch한다.
Memory map의 SRAM region을 살펴보자.
- SRAM region은 Code region로부터 512MB 위에 위치한다. (이건 MCU에 따라 다르다.)
- SRAM region은 이름처럼 SRAM이 주로 연결되는데, 그 중에서 On-Chip SRAM이 연결된다.
- SRAM 영역의 처음 1MB는 Bit Addressable하다. (Bit 단위로 접근이 가능하다.)
- SRAM 영역에서 Program Code를 실행할 수 있다.
Memory map의 Peripherals region을 살펴보자.
- Peripherals region은 512MB의 크기를 가진다. (MCU에 따라 다르다.)
- Peripheral region은 대개 On-chip peripherals들이 사용한다.
- SRAM region처럼 Peripheral region의 처음 1MB는 Bit Addressable하다. (Bit-band 기능이 포함되어 있다면)
- Peripheral region은 eXectue Never (XN) region이다.
이 영역에서 Code를 실행할 수 없고, Code를 실행하려고 하면 Fault exception이 발생한다.
이는 외부로부터의 Code Injection Attack을 방지하기 위함이다.
Code Injection Attack이란 외부의 누군가가 Peripheral을 통해 Code를 송신하고, Processor가 해당 Code를 실행하게 만드는 것이다.
- External RAM region은 Project에 더 많은 RAM이 필요한 경우에 사용된다.
예를 들어, 그래픽과 관련된 프로젝트를 수행하는 경우나 Frame Buffer를 만들어야 하는 경우, 또는 비디오나 오디오 정보를 저장해야 하는 경우와 같이 더 많은 RAM이 필요한 경우를 말한다.
그러므로 추가로 연결되는 External RAM의 레지스터들은 Memory map의 External RAM region에 할당될 것이다.
- External Device region은 External Device 또는 Shared memory에 사용되는 영역이다.
- External Device region 또한 eXecute Never (XN) 영역이다.
- Private Peripheral Bus region은 ARM Cortex-Mx Processor의 모든 Control / Configuration Register (Processor peripheral register)들이 위치한 영역이다.
예를 들어, NVIC, SysTick timer 등의 레지스터가 이 영역에 위치한다.
- Private Peripheral Bus region 또한 eXecute Never (XN) 영역이다.
Bus protocols and Bus interfaces
- Cortex-Mx Processor에서 Advanced Microcontroller Bus Architecture (AMBA) Specification에 기반을 두고 있다.
- AMBA는 ARM에서 설계한 Bus interface Specification인데, Chip 내부 시스템의 On-chip Communication (Processor – Peripherals or Memories)을 주관한다.
- AMBA specification은 다양한 Bus protocol들을 지원한다.
- AHB Lite (AMBA High-performance Bus)
- APB (AMBA Peripheral Bus)
AHB와 APB에 대해 자세하게 알아보자.
- AHB Lite bus는 주로 Main bus interfaces에 사용된다.
- APB bus는 Private peripherals와 On-chip peripherals에 Access하는데 사용된다.
(AHB-APB bridge를 사용한다.)
- AHB Lite bus는 주로 High-Speed Communication이 요구되는 Peripheral들과 Communication하는데 사용된다.
- APB bus는 상대적으로 Low-Speed Communication의 Peripheral들과 Communication하는데 사용된다.
이제 ARM Cortex-Mx Processor가 어떻게 외부 Peripheral들과 통신하는지 알아보자.
Processor는 4개의 Bus interface를 가지는데 모두 AHB bus interface이다.
이것들을 이용해서 Processor는 여러 Memory들과 Peripheral들에 연결될 수 있다.
I-Code bus를 이용해서 Code region(memory)로부터 Instruction을 fetch하거나 Vector table을 Read한다.
D-Code bus를 이용해서 Code region으로부터 data를 가져온다. (Constant data 또는 어떤 table 등)
즉, Processor는 두 개의 dedicated bus interface (AHB protocol)를 사용해서 Code region으로부터 Instruction과 data를 동시에 fetch할 수 있다.
즉, Processor는 두 개의 dedicated bus interface (AHB protocol)를 사용해서 Code region으로부터 Instruction과 data를 동시에 fetch할 수 있다.
Processor는 System bus라는 또 다른 AHB bus를 사용한다.
System bus는 MicroController의 다양한 On-chip peripheral들을 연결한다.
그리고 이 bus를 통해 Processor도 SRAM(Data memory)과 통신할 수 있다.
(임의의 Data memory Access는 이 System bus를 통해 이루어지고, MicroController의 다양한 peripheral들(ADC, DAC, TIMER, CAN 등)의 통신도 System bus를 통해 이루어진다.)
그리고 이 bus를 통해 Processor도 SRAM(Data memory)과 통신할 수 있다.
(임의의 Data memory Access는 이 System bus를 통해 이루어지고, MicroController의 다양한 peripheral들(ADC, DAC, TIMER, CAN 등)의 통신도 System bus를 통해 이루어진다.)
그리고 Processor는 Private Peripheral Bus(PPB)라는 또 다른 AHB bus를 사용한다.
PPB를 통해 Processor는 Private Peripheral Bus region과 통신할 수 있다.
다음은 STM32F407VG의 Datasheet이다.
- NVIC, ETM, MPU, JTAG & SW(Debug unit)는 Processor의 Peripheral이다.
이러한 Processor specific peripheral은 Private Peripheral Bus region에 속하고, Access 하기 위해 PPB bus가 사용된다.
- 오른쪽의 Flash memory는 Code memory이다. (MCU Vendor가 설계)
Processor는 Flash memory로부터 Instruction과 Data를 동시에 Fetch하기 위해 I-Code bus (for Instruction)와 D-Code bus (for Data)를 사용한다.
- I-Code bus와 D-Code bus 이외에 System bus라는 또 다른 Bus interface가 존재한다.
System bus는 SRAM 그리고 MCU의 On-chip peripheral과 통신하는데 사용된다.
I-bus와 D-bus 그리고 S-bus는 모두 Bus matrix에 연결되는데, 이 Bus matrix도 MCU vendor가 설계한다.
Bus matrix는 다양한 Bus masters들의 Bus Access를 Synchronize하는 역할을 한다.
(정확하게는 Bus matrix의 Bus arbiter가 수행한다.)
Bus master로 동작할 수 있는 것들에는 Processor, Ethernet, USB, DMA가 있다.
- AHB Bus는 AHB Bus matrix를 통해 System bus로 연결되는 MCU의 Main bus이다.
Bit-banding
- Bit-banding은 어떤 Memory 주소의 한 bit를 Address할 수 있는 능력을 말한다.
- Bit-banding 기능은 Optional이라서 MCU manufacturer가 지원해야 사용할 수 있다.
- Bit-banding은 bit data에 대한 Atomic operations를 제공하기 때문에, System exception이나 Interrupt에 의해 방해되지 않는다.
MCU의 Memory는 1Byte 단위로 Addressing이 되어 있다. (Byte Addressing)
그래서 LDRB (1Byte를 Load하는 명령), LDRH (Half word를 Load하는 명령), LDR (Word를 Load하는 명령)은 가능하지만, 일반적으로 1Byte보다 작은 Bit 단위로 Load하는 명령은 수행이 불가능하다.
1Byte를 모두 읽어서 특정 Bit의 값을 바꾸는 방법은 가능한데, 이 과정은 Byte Access to Read → Bit modify → Byte Access to Store 으로 복잡하다.
1Byte를 모두 읽어서 특정 Bit의 값을 바꾸는 방법은 가능한데, 이 과정은 Byte Access to Read → Bit modify → Byte Access to Store 으로 복잡하다.
그런데 특정 Memory 위치에서 Bit 단위로 Addressing 할 수 있는 Bit-banding 기능을 사용하면, LDRB 명령으로 각 Bit에 할당된 Alias Address를 통해 다른 Bit에 영향없이 특정 Bit에 Access하고 그 값을 Modify할 수 있다.
Bit-banding 기능은 SRAM과 Peripheral region의 처음 1MB에서만 가능하다.
각 영역에는 Bit-band region과 Bit-band Alias address가 존재한다.
예를 들어, 0x2000_0000번지의 0번 bit에 Access하고 싶다면, 이 bit에 해당되는 Alias address 0x2200_0000를 사용하면 된다.
Bit-band Exercise
메모리 주소 0x2000_0200의 7번 bit에 해당하는 Bit-band Alias address를 계산해보자.
Alias address의 General formula는 다음과 같다.
Alias address = Alias_base + ( 32 * (bit_band_memory_address – bit_band_base ) ) + bit * 4 = 0x2200_0000 + ( 32 * ( 0x2000_0200 – 0x2000_0000) ) + 7 * 4
다음은 위의 예제에 대한 코드이다.
위의 코드를 실행하면, 기존의 방법과 같이 Bit-banding 기능을 이용한 방법에서 0x2000_0200의 7번 bit를 modify할 수 있다.
Stack memory and Placement
Introduction to Stack memory
Stack memory에 대해 알아보자.
- Stack memory는 Main memory(Internal RAM or External RAM)의 일부인데, Data의 임시 저장소로 사용된다.
- 주로 Function, Interrupt/Exception Handling에서 사용된다.
- Stack memory는 LIFO (Last In First Out) 방식으로 Access 된다.
- Stack은 PUSH/POP Instruction 또는 LD/STR과 같은 Memory manipulation Instruction으로 Access 될 수 있다.
- Stack은 Stack Pointer (SP) 레지스터에 의해 trace된다. PUSH/POP instruction은 Stack Pointer 레지스터의 값을 증가시키거나 감소시킨다.
Stack memory가 어떻게 사용되는지 자세히 알아보자.
- Stack memory는 Processor의 Register 값을 임시 저장하는데 사용된다.
- Function의 Local variables를 임시 저장하는데 사용된다.
- System exception이나 Interrupt 과정에서 Stack memory는 현재 실행중인 코드의 Context를 저장하는데 사용된다. (Context : some General-purpose registers, Processor status register, Return address 등)
RAM은 세 가지 영역으로 나뉠 수 있다.
- Global Data Section :
이 영역에는 Global data 또는 Static local variables들이 저장된다.
그리고 이 영역을 Program memory처럼 사용해서 Instruction을 저장하고 실행할 수 있다.
- Heap Section :
malloc, free API를 사용하면, 이 영역에 Dynamic memory가 할당된다.
- Stack Section :
이 영역은 Function call에서 임시 data들을 저장하기 위해, Function의 local variables의 임시 저장소로, Interrupt/Exception에서 Stack frames들의 임시 저장소로 사용된다.
프로그래머는 Standard library 또는 Compiler에게 RAM의 어디서부터 어디까지를 Global data section, Heap, Stack으로 사용할지 알려주어야 한다.
이러한 Setting은 Tool-chain 또는 Linker script에서 수행해야 한다.
이러한 Setting은 Tool-chain 또는 Linker script에서 수행해야 한다.
이제 Stack operation models들에 대해 알아보자.
- Full Ascending stack (FA)
- Full Descending stack (FD) → ARM Cortex-Mx Processor가 FD 방식의 Stack operation을 사용.
- Empty Ascending stack (EA)
- Empty Descending stack (ED)
Different stack operation models
- Full Ascending stack 운영 방식은 item이 Push될 때마다 memory address가 증가하고, SP는 항상 마지막에 적재된 item을 가리킨다.
(POP될 때는 memory address 감소)
- Full Descending stack 운영 방식은 item이 Push될 때마다 memory address가 감소하고, SP는 항상 마지막에 적재된 item을 가리킨다.
(POP될 때는 memory address 증가)
- Empty Ascending stack 운영 방식은 item이 Push될 때마다 memory address가 증가하고, SP는 항상 마지막에 적재된 item 다음의 Empty location을 가리킨다.
(POP될 때는 memory address 감소)
- Empty Descending stack 운영 방식은 item이 Push될 때마다 memory address가 감소하고, SP는 항상 마지막에 적재된 item 다음의 Empty location을 가리킨다.
(POP될 때는 memory address 증가)
정리하면,
- Full : SP는 항상 마지막에 적재된 item을 가리킨다는 의미.
- Empty : SP는 항상 마지막에 적재된 item 다음의 Empty location을 가리킨다는 의미.
ARM Cortex-Mx Processor는 Full descending stack 운영 방식을 사용한다.
(우리가 임의로 바꿀 수 없다.)
(우리가 임의로 바꿀 수 없다.)
Stack placement
Stack placement에 대해 알아보자.
RAM에 Stack memory를 할당하는 방식에는 여러가지가 있는데, 위의 두 가지 방식을 예로 들자.
프로그램의 Linker script나 Tool-chain의 Configuration file에 의해 Stack, Heap, Data 영역의 시작과 끝이 결정된다. (각 영역의 시작과 끝은 Linker script symbol로 표현된다. ex) ‘_estack’ )
ARM Cortex-Mx Processor의 Stack 운영 방식은 Full descending 방식이라서, item이 적재될수록 Stack이 Low memory address로 확장된다.
그래서 일반적으로 ARM Cortex-Mx Processor에서는 오른쪽 Stack placement를 사용한다.
그리고 Stack이 사전에 설정한 경계를 넘어가는 Stack overflow 발생을 검출하기 위해, SP값과 경계값을 비교하는 소프트웨어 루틴을 포함하는 것이 바람직하다.
Banked stack pointer registers of ARM Cortex-Mx Processor
ARM Cortex-Mx Processor의 Banked stack pointer registers들에 대해 알아보자.
- Cortex-Mx Processor는 물리적으로 3개의 Stack pointers들을 갖고 있다.
- SP (R13) : SP는 Current stack pointer로 불린다.
- MSP : Main Stack Pointer
- PSP : Process Stack Pointer
- Processor가 Reset되면, 기본적으로 MSP가 Current stack pointer로 선택된다.
즉, SP는 MSP의 내용을 복사한다.
- Thread mode는 CONTROL 레지스터의 SPSEL bit를 configuring함으로써 Current stack pointer를 PSP로 바꿀 수 있다.
다음 그림과 함께 자세히 알아보자.
(즉, MSP가 Current stack pointer로 사용되어, SP는 0x2000_0034의 값을 가진다.)
그러나 CONTROL 레지스터의 SPSEL bit를 1로 바꾸면, SP는 PSP의 내용을 복사해온다.
(즉, PSP가 Current stack pointer로 사용되어, SP는 0x2000_00D4의 값을 가진다.)
- Handler mode일 때, 코드는 항상 MSP를 Current stack pointer로 사용한다.
CONTROL 레지스터의 SPSEL bit에 대한 Write는 무시된다.
- Reset 직후, MSP는 Processor에 의해 0x0000_0000번지의 값으로 자동으로 초기화된다.
그래서 프로그래머는 0x0000_0000번지에 유효한 값이 저장되어 있는지 확인해야 한다.
- PSP를 Current stack pointer로 사용하고 싶다면, PSP를 적절한 값으로 초기화하고, CONTROL 레지스터를 통해 SP를 PSP로 설정해 주어야 한다.
(PSP가 올바른 값으로 초기화 되지 않고 PUSH 또는 POP 명령이 수행되면, 재앙이 도래한다.)
Stack exercise
ARM Cortex-Mx Processor에서 SP는 Initial stack address인 RAM의 Highest memory address로 초기화되는데, 이 값은 Linker script에 정의되어 있고 Vector table의 첫 번째 entry이다.
앞서 말했듯이 Processor가 Reset되었을 때, 가장 먼저하는 일이 Vector table의 첫 번째 entry인 Initial stack address를 MSP 레지스터로 fetch하는 것이다. (Thread mode에서 MSP는 Current stack pointer라고 말했다.)
다음 그림은 STM32F103RCTx Startup file의 Vector table이다.
Vector table의 첫 번째 entry인 _estack은 Linker symbol인데, 이 Symbol은 Linker script에 정의되어 있다.
다음 그림은 STM32F103RCTx의 Linker script이다.
보면_estack은 ‘RAM의 시작 주소 + RAM의 길이’로 정의됨을 알 수 있다.
_estack은 ‘RAM의 시작 주소 + RAM의 길이’로 정의된다.
(RAM의 Length는 STM32F407 기준으로 SRAM1 + SRAM2 이다.)
(RAM의 Length는 STM32F407 기준으로 SRAM1 + SRAM2 이다.)
Linker script는 Linker에 의해 해석되고, 프로그래머가 Linker script안에 작성한 statement를 기반으로 Linker는 프로젝트의 다양한 section들을 assemble함으로써 최종 실행파일을 만든다.
위의 Linker script에서 다양한 Linker symbol을 찾을 수 있다. (_estack, _etext, _sidata, _edata, _ebss 등)
다음 그림은 Processor mode에 따라 사용하는 Stack 공간을 분리한 경우이다.
1KB의 Stack 공간을 512Byte 크기의 두 공간으로 나누고, 위 Stack 공간은 MSP가 track하고, 아래 Stack 공간은 PSP가 trace한다고 하자.
Thread mode 코드는 PSP가 track하는 아래 Stack 공간을 사용하고, Handler mode 코드는 MSP가 trace하는 위 Stack 공간을 사용하게 만들 수 있다.
OS 환경에서는 이와 같은 방식으로 Stack을 사용한다.
Kernel mode 코드는 Handler mode에서 실행되고, Kernel을 위한 Stack은 MSP가 trace한다.
User task 코드는 Thread mode에서 실행되고, 이를 위한 Stack은 PSP가 trace한다.
Kernel mode 코드는 Handler mode에서 실행되고, Kernel을 위한 Stack은 MSP가 trace한다.
User task 코드는 Thread mode에서 실행되고, 이를 위한 Stack은 PSP가 trace한다.
Stack exercise contd
다음은 Processor mode에 따라 사용하는 Stack 공간을 분리한 경우에 대한 예제 코드이다.
처음 SP는 MSP이지만, change_sp_to_psp 함수를 실행하면, SP는 PSP로 바뀐다.
그러다 SVC_Handler가 호출되면, SP는 다시 MSP로 바뀌게 된다. (Handler mode에서 SP는 항상 MSP이다.)
MSP와 PSP에 대해 요약해보자.
- Cortex-Mx Processor에는 2개의 Stack Pointer 레지스터가 물리적으로 존재한다.
- MSP (Main Stack Pointer)는 MCU의 Reset 이후에 Thread mode에서 default Stack Pointer로 사용되고, 모든 Exception/Handler에서 항상 SP로 사용된다.
- PSP (Process Stack Pointer)는 Thread mode에서 alternate Stack Pointer로 사용된다.
대체로 Embedded OS에서 User task를 위해 사용된다.
- MCU가 Reset되면, Processor는 자동으로 MSP를 Vector table의 첫 번째 entry의 값으로 초기화한다.
SP를 바꾸는 방법을 요약해보자.
- Assembly 코드로 MSP 또는 PSP에 접근하기 위해서는 MSR / MRS 명령을 사용해야 한다.
- 현재 선택된 Stack Pointer를 바꾸기 위해, 프로그래머는 C 프로그램 내부에서 Naked function을 작성할 수 있다.
Naked function은 C의 function과 비슷한 Assembly function인데 epilogue, prologue sequences를 갖지 않는다.)
일반 함수 대신에 Naked function을 사용하는 이유에 대해 Shishir 조교는 다음과 같이 대답했다.
Shishir : Normal function을 호출하면, extra code(epilogue, prologue)에 의해 Core registers들의 state가 바뀐다.
위의 예제에서 우리는 다른 Core registers들의 값이 바뀌지 않게 하기 위해서 Naked function을 쓴다.
(내가 이해하기로는 다른 Core 레지스터에 우리가 임의로 값을 넣어 놓았는데, 함수 호출 과정에서 이 값이 바뀌면 안 되기 때문에 Naked function을 쓰는 것 같다.)
Function call and AAPCS standard
Function call 또는 Subroutine call에서 따라야할 기준인 AAPCS standard에 대해 알아보자.
AAPCS (ARM Architecture Procedure Call Standard)
ACCPCS standard는 많은 사람들이 하나의 프로젝트에 사용될 Assembly routines들을 작성할 때,
모두가 따라야할 규칙 또는 Specification이다.
모두가 따라야할 규칙 또는 Specification이다.
Caller는 Callee를 호출하기 전에 R0 ~ R3의 값을 Stack으로 Push하고 Callee가 종료되면 다시 Pop한다.
R0 ~ R3는 Callee로 inpute argument를 보내는데 사용된다.
만약 Callee가 R4 ~ R11을 modify 하고 싶다면, 기존 값을 Stack에 Push하고, return 하기 전에 Pop한다.
그리고 Callee는 R0, R1을 사용해서 Caller에게 결과를 return 할 수 있다.
(결과값의 크기가 32bit 이하이면 R0만 사용하고, 32bit ~ 64bit 이면 R0, R1을 모두 사용한다.)
이와 같은 것들은 프로그래머가 Assembly code로 Function이나 Subroutine을 작성할 때, 고려해야 하는 것들이다.
그러나 C 프로그램에서는 Compiler가 AAPCS에 따라 Instruction을 generate하므로 C 프로그래머는 AAPCS를 고려하지 않아도 프로그래밍에 큰 문제가 없다.
Stack activities during interrupt and exception
C 함수를 Exception 또는 Interrupt Handler로 사용하기 위해, Exception mechanism은 Processor Hardware의 통제 하에 R0 ~ R3, R12, LR, XPSR을 Exception entrace에서 자동으로 저장하고 Exception exit에서 복구해야 할 필요가 있다.
이러한 방식으로 Interrupt된 프로그램으로 돌아왔을 때, 모든 레지스터들의 값이 Interrupt 진입 시퀀스가 시작될 때와 동일한 값을 가진다.
Caller와 Callee가 모두 존재하는 경우에 레지스터들은 AAPCS에 따라 Caller 또는 Callee에 의해 저장된다.
그러나 Exception / Interrupt은 모두 Asynchronous하게 발생하고, Processor Hardware가 Handler를 Call하기 때문에, 이 경우에는 Caller function이 존재하지 않는다.
그래서 Caller 대신에 Processor가 Caller saved registers들을 Save하는데, 이 덕분에 프로그래머는 AAPCS 표준에 대한 걱정없이 Exception / Interrupt Handler를 일반적인 C 함수처럼 작성할 수 있다.
Thread mode 코드를 실행하는 도중에 Exception / Interrupt가 발생하는 경우를 가정해보자.
Processor는 Thread mode에서 Task A를 실행하고 있다.
그러던 중에 Exception / Interrupt가 발생하면, Processor는 Thread mode에서 Handler mode로 바뀌면서 자동으로 Stack Frame (Caller saved registers, Return address, Thread mode 코드의 Status registers)를 Save한다.
이를 Stacking이라고 한다.
Exception / Interrupt가 종료되면, Processor는 Save했던 Stack Frame을 자동으로 복구한다.
이를 Un-Stacking이라고 한다.
이를 Un-Stacking이라고 한다.
Stacking과 Un-Stacking은 Processor가 Exception / Interrupt Entry와 Exit에서 자동으로 수행한다.
이제 Stack Initialization을 알아보자.
Stack을 초기화하는 방법에는 두 가지가 있다.
- main 함수에 진입하기 전에 초기화하는 방법
- main 함수에 진입하고, 프로그래머가 Stack pointer를 다시 초기화하는 방법
SRAM1, 2가 아닌 SRAM3를 Stack memory로 사용하고 싶거나 External SRAM을 Stack memory로 사용하고 싶은 경우에 이 방법을 사용한다.
다음은 Stack Initialization할 때, 명심해야할 내용이다.
Exception model of ARM Cortex-Mx Processor
Exception model
Exception은 Processor의 operational mode를 바꿈으로써 평범하게 실행되고 있던 프로그램에 끼어든다.
Exception에는 두 가지 종류가 있다.
- System exception
- Interrupt
System exception은 Processor 내부에서 자체적으로 발생한다.
Interrupt는 외부에서 발생해서 Processor로 들어온다.
Processor가 Exception이 발생했다는 것을 알게 되면, Operational mode를 Handler mode로 바꾼다.
ARM Cortex-Mx Processor는 최대 15개의 System exceptions과 최대 240개의 Interrupts를 지원한다.
Different system exceptions
ARM Cortex-Mx Processor의 Exception은 다음과 같다.
- ARM Cortex-Mx Processor는 최대 15개의 System exceptions를 지원할 수 있지만, 오직 9개의 System exception들만 구현되어 있고, 나머지 6개는 추후의 구현을 위해 reserve되어 있다.
- Exception 1번은 Reset exception이고, Exception 16번은 IRQ1 (Interrupt 1)이다.
이제 다양한 System exceptions에 대해 알아보자.
- Reset exception :
Processor의 Reset은 전원이 공급되거나 Reset 신호를 인가할 때, Reset 회로가 동작하면서 수행된다.
Reset 신호 인가가 끝나면, Processor는 Vector table의 Reset entry로 주어진 Address(Reset handler)에서부터 명령을 실행한다.
- NMI exception (Non-maskable exception) :
NMI exception은 System exception이 아니라 Peripheral 또는 Software에 의해 trigger되는 Exception이다.
(사실 모든 Exception은 Software로 trigger할 수 있다.)
NMI exception은 Reset exception을 제외하면, 가장 높은 Priority를 갖는 Exception이고, 영구적으로 Enabled된다.
(절대 Disable되거나 Mask되지 않는다.)
- HardFault exception :
HardFault exception은 두 가지 요인에 의해 trigger된다.
첫째, HardFault exception은 Exception을 처리하는 과정에서 발생하는 Error에 의해 trigger된다.
예를 들어, 어떤 Exception handler에서 illegal instruction, illegal code access, illegal instruction execution을 하면, 그 순간 HardFault exception이 발생한다.
둘째, HardFault exception은 어떤 Exception이 발생했는데, 그것을 처리할 Exception mechanism (Exception handler) 이 존재하지 않을 때 trigger된다.
예를 들어, T-bit을 0으로 만드는 예제를 실행했을 때, HardFault exception이 발생했었다.
이 예제는 Usage Fault exception을 trigger하는 예제였는데, 우리가 Usage Fault exception을 Enable하지 않았고 이를 처리할 Exception mechanism도 없었기 때문에, Usage Fault exception이 아닌 HardFault exception이 발생한 것이다.
- MemManage Fault exception :
MemManage Fault exception은 Memory protection과 관련된 Fault가 발생했을 때 trigger되는 Exception이다.
예를 들어, Processor의 MPU가 Enable된 상태에서 Processor Core가 Protected된 Memory에 접근하려고 할 때, MemManage Fault exception이 trigger한다.
- BusFault exception :
BusFault exception은 Bus Error와 관련있는데, 거의 발생하지 않는 Exception이다.
- UsageFault exception :
UsageFault exception은 Instruction execution과 관련된 Fault로 인해 발생하는 Exception이다.
발생 원인은 다음과 같다.
– an undefined instruction (‘T-bit = 0’ 예제 → Thumb이 아닌 undefined된 ARM instruction 실행)
– an illegal unaligned access
– invalid state on instruction execution
– an error on exception return
그리고 다음과 같은 경우를 report하도록 Core를 Configure하면, UsageFault exception이 발생시킬 수 있다.
– an unaligned address on word and halfword memory access
– division by zero
- SVCall exception (Supervisor Call exception) :
SVC exception은 SVC instruction에 의해 trigger된다.
OS 환경에서 Application은 SVC instruction (Service number가 포함되어 있다)을 사용해서 OS kernel functions와 Device drivers에 Access할 수 있다.
- PendSV exception :
PendSV exception은 System-level service에 대한 interrupt-driven request이다.
OS 환경에서 다른 Exception이 Active 상태가 아닐때, Context switching에 PendSV를 사용한다.
- SysTick exception :
SysTick exception은 System timer가 0에 도달했을 때, System timer가 발생시키는 Exception이다.
(Software로 SysTick exception을 발생시킬 수도 있다.)
OS 환경에서 Processor는 SysTick exception을 System tick으로 사용한다.
- Interrupt (IRQ) :
Interrupt는 System exception이 아니라 Peripheral 또는 Software request에 의해 trigger되는 Exception이다.
모든 Interrupts들은 Instruction execution에 Asynchronous하다.
System 내부에서 Peripherals들은 Processor와 통신하기 위해 Interrupts를 사용한다.
System exception vector addresses
다음 Vector table에서 System exception handler의 address가 저장되는 Vector address를 알 수 있다.
Vector table의 첫 번째 entry는 MSP값이다.
System exception control registers
System exception control register는 Processor의 System exception을 제어하는 레지스터이다.
ARM Cortex-Mx Processor는 많은 Peripheral들을 갖고 있다.
(CPU Core, NVIC, FPU, MPU, SCB, SysTick Timer, Debug unit, …)
(CPU Core, NVIC, FPU, MPU, SCB, SysTick Timer, Debug unit, …)
이 Peripherals들을 제어하기 위한 레지스터들은 PPB (Private Peripheral Bus region)에 속하고, PPB bus를 통해서 Access된다.
다음은 PPB 영역에 존재하는 ARM Cortex-M3 Core peripheral들의 목록이다.
이 중에서 System Control Block (SCB)만 먼저 살펴보겠다.
System Control Block은 System implementation information과 System control을 제공한다.
이는 Configuration, Control, System exceptions의 reporting을 포함한다.
(다시 말해, SCB는 ARM Cortex-Mx Processor의 System에 관련된 기능을 Control, Configure하기 위한 레지스터를 갖는다.)
(다시 말해, SCB는 ARM Cortex-Mx Processor의 System에 관련된 기능을 Control, Configure하기 위한 레지스터를 갖는다.)
다음은 SCB의 레지스터로 설정할 수 있는 항목들이다.
- Enable fault handlers
- Get pending status of the fault exceptions
- Trap processor for divide by zero and unaligned data access attempts
- Control sleep and sleep wake-up settings
- SysTick timer control and status
다음은 System exception의 Default status이다.
NVIC (Nested Vector Interrupt Controller)
NVIC은 ARM Cortex-Mx Processor의 Peripheral 중의 하나이고, 240개의 External Interrupts를 configure하는데 사용된다.
NVIC 레지스터를 사용하면, 다양한 Interrupts들을 Enable / Disable / Pend 시킬 수 있고, 현재 Active 또는 Pending 중인 Interrupts의 Status를 알 수 있다.
그리고 다양한 Interrupts의 Priority grouping과 Priority를 설정할 수 있다.
Nested라고 이름 붙여진 이유는 더 높은 Priority의 Interrupt가 NVIC에 도착했을 때, 상대적으로 낮은 Priority의 Interrupt handler를 pre-empting하는 기능을 NVIC이 지원하기 때문이다.
MCU Vendor에 따라 NVIC의 IRQ line 개수와 연결이 다르다.
예를 들어, STM32F407의 경우에는 IRQ line이 83개이지만, Ti의 경우에는 IRQ line이 154개이다.
그리고 STM의 IRQ0는 WWDG이지만, Ti의 IRQ0는 GPIOA이다.
NVIC registers
- ISER (Interrupt Set-enable Registers) :
최대 240개의 Interrupt를 Enable하기 위해 8개의 ISER 레지스터(ISER0 ~ ISER7)가 존재한다.
Enable 하고 싶은 Interrupt에 해당하는 bit를 Set하면 된다.
- ICER (Interrupt Celar-enable Registers) :
최대 240개의 Interrupt를 Disable하기 위해 8개의 ICER 레지스터(ICER0 ~ ICER7)가 존재한다.
Disable 하고 싶은 Interrupt에 해당하는 bit를 Clear하면 된다.
- ISPR (Interrupt Set-pending Registers) :
최대 240개의 Interrupt를 Trigger(Pending)하기 위해 8개의 ISPR 레지스터(ISPR0 ~ ISPR7)가 존재한다.
Trigger(Pending) 하고 싶은 Interrupt에 해당하는 bit를 Set하면 된다.
- ICPR (Interrupt Clear-pending Registers) :
최대 240개의 Interrupt의 Pending 상태를 확인하거나 제거하기 위해 8개의 ICPR 레지스터(ICPR0 ~ ICPR7)가 존재한다.
Pending 상태를 확인하거나 제거하고 싶은 Interrupt에 해당하는 bit를 Read / Set하면 된다.
- IABR (Interrupt Active Bit Registers) :
최대 240개의 Interrupt의 Service Routine이 현재 Processor가 실행하고 있는지(Active 상태인지) 확인하기 위해 8개의 IABR 레지스터(IABR0 ~ IABR7)가 존재한다.
Active 상태인지 확인하고 싶은 Interrupt에 해당하는 bit를 Read하면 된다.
- IPR (Interrupt Priority Registers) :
최대 240개의 Interrupt의 Priority를 Configure하기 위해 60개의 IPR 레지스터(IPR0 ~ IPR59)가 존재한다.
Priority를 Configure하고 싶은 Interrupt에 해당하는 bit field에 Priority 값을 Write하면 된다.
- STIR (Software Trigger Interrupt Registers) :
최대 240개의 Interrupt를 Software적으로 Trigger하기 위해 1개의 STIR 레지스터가 존재한다.
STIR 레지스터의 하위 [8:0] bit field에 Trigger하고 싶은 Interrupt의 ID를 Write하면 된다.
Peripheral Interrupt excercise
Peripheral (USART3)의 interrupt를 Enable하고 Pend시키는 Excercise를 해보자.
USART3는 Vendor specific peripheral이라서 MCU vendor에 의해 implementation된다.
USART3가 연결되는 NVIC의 IRQ line은 39이다. 이는 Vendor 또는 Family에 따라 다를 수 있기 때문에 사용하는 MCU의 Vector table을 살펴봐야 한다.
다음은 MCU Peripheral interrupt를 프로그램하기 위한 절차이다.
- MCU의 Vector table을 참조해서 Peripheral의 IRQ number를 확인해야 한다.
(IRQ number는 Vendor, Family specific 하다.)
- 해당하는 IRQ를 Enable 하기 위해 Processor registers를 프로그래밍해야 한다.
(IRQ를 Enable해야 Processor가 해당 Line의 Interrupt Req를 Accept한다.)
그리고 필요하다면 Priority도 설정한다.
- Peripheral의 Configuration registers를 사용해서 Peripheral을 Configure해야 한다.
예를 들어, USART3가 Packet을 수신할 때마다 자동으로 IRQ 39에 Interrupt Req를 보내도록 만들어야 한다.
- Interrupt Req이 IRQ line으로 보내지면, 해당 Interrupt Req는 NVIC의 Pending register에서 pend된다.
- NVIC은 새로 들어온 Interrupt Req의 Priority가 현재 실행중인 Interrupt Handler보다 높은 경우에만 새로 들어온 Interrupt Req의 Handler가 실행되도록 허용한다.
새로 들어온 Interrupt Req의 Priority가 현재 실행중인 Interrupt Handler보다 낮다면, 새로 들어온 Interrupt Req는 Pending 상태가 된다.
- Peripheral이 Interrupt Req를 Processor로 보냈는데 해당하는 IRQ number가 Disabled된 상태라면, Interrupt Req는 NVIC의 Pending register에서 Pend된다.
그러던 중에 해당 Interrupt Req가 Processor에서 Enable되면, 해당 Interrupt Req의 ISR이 실행된다. (단, 현재 Active 상태인 ISR이 존재한다면, 이것의 Priority보다 높아야 한다.)
이제, USART3 Peripheral이 어떻게 Processor로 Interrupt Req를 보내고, ISR이 어떻게 실행되는지 알아보자.
- 외부에서 USART peripheral의 Rx buffer로 Data packet이 도착한다.
- USART peripheral이 할당받은 IRQ line에 Interrupt Req를 보낸다.
- Processor NVIC의 Pending register에 Interrupt Req가 pending 되고, 만약 IRQ가 Enable되면 NVIC이 Procesesor에 Interrupt를 건다. (단, 현재 Active한 ISR이 있는 경우, 이것의 Priority보다 높아야 한다.)
- CPU가 Interrupt를 Accept하면, 가장 먼저 Vector table에서 해당 IRQ number의 ISR address를 fetch한다. 그리고 CPU는 해당 ISR address로 Jump한다.
- ISR에서 CPU는 USART3 Rx buffer의 data를 SRAM으로 Copy하는 등의 작업을 수행할 수 있다.
다음은 USART3의 interrupt를 Enable하고 Pend시키는 예제 코드이다.
다음 그림은 USART3의 IRQ 39에 대해서 ISPR1과 ISER1을 Configure한 결과이다.
IRQ 39에 해당하는 bit가 정상적으로 Set되었다.
다음 그림은 USART3의 ISR이 실행되었을 때, 레지스터들의 값이다.
CPU가 ISR에 진입하는 동시에 해당 IRQ에 대한 Pending 레지스터의 bit가 HW에 의해 Clear됨을 알 수 있다. 그리고 현재 USART3의 ISR이 실행되고 있음을 IABR1 레지스터에서 알 수 있다.
ARM Cortex-M3/M4 Processor - 2Memory map and the bus interfaces of ARM Cortex-Mx ProcessorMemory mapBus protocols and Bus interfacesBit-bandingBit-band ExerciseStack memory and PlacementIntroduction to Stack memoryDifferent stack operation modelsStack placementStack placement에 대해 알아보자.Banked stack pointer registers of ARM Cortex-Mx ProcessorARM Cortex-Mx Processor의 Banked stack pointer registers들에 대해 알아보자.Stack exerciseStack exercise contdFunction call and AAPCS standardAAPCS (ARM Architecture Procedure Call Standard)Stack activities during interrupt and exceptionException model of ARM Cortex-Mx ProcessorException modelDifferent system exceptions이제 다양한 System exceptions에 대해 알아보자.System exception vector addressesSystem exception control registersNVIC (Nested Vector Interrupt Controller)NVIC registersPeripheral Interrupt excercise다음은 MCU Peripheral interrupt를 프로그램하기 위한 절차이다.이제, USART3 Peripheral이 어떻게 Processor로 Interrupt Req를 보내고, ISR이 어떻게 실행되는지 알아보자.다음은 USART3의 interrupt를 Enable하고 Pend시키는 예제 코드이다.