Pages List
List view
STM32 Peripheral
ARM Cortex-M3/M4 Processor - 1
Introduction
Processor Core vs Processor
Processor Core는 Procssor Specific Peripherals로 둘러싸여 있다.
그러면 Processor Core는 어떻게 구성되어 있을까?
- Core는 ALU로 구성되는데, ALU는 데이터 계산이 수행되고 결과값이 생성되는 곳이다.
- Core는 명령을 디코드하고 수행하는 로직을 갖는다.
- 데이터를 저장하거나 조작(manipulate)하기 위한 많은 레지스터를 갖는다.
- 명령어 수행속도를 부스트하기 위한 Pipe-Line 엔진을 갖는다.
- 하드웨어 곱셈, 나눗셈 엔진을 갖는다.
- 주소 생성 유닛을 갖는다.
여기서 CPU는 Cortex-M4 processor이고, CPU Core는 Cortex-M4 Core이다.
CPU는 다수의 코어를 가질 수 있는데 Cortex-M4 processor는 하나의 코어를 갖는 싱글 코어 CPU이다.
결론,
프로세서 = 프로세서 코어 + Surrounding Processor Specific Peripherals
프로세서 = 프로세서 코어 + Surrounding Processor Specific Peripherals
그리고 프로세서는 ICode, Dcode, System interface로 외부와 소통한다.
Processor VS MicroController
ARM은 Cortex-M4 Processor (Core와 Surrounding Processor Specific Peripherals)의 Software IP를 각 Vendor로 판매하고, Vendor는 Surrounding Processor Specific Peripherals를 추가하거나 제거할 수 있다. (ex. FPU 추가, MPU 제거)
예를 들어, ST는 STM32F446RE에 option인 FPU를 추가했다.
다음은 STM32F446x의 전체 Block Diagram이다.
빨간색으로 쳐진 부분만 ARM이 설계하고, 나머지는 전부 ST가 설계한다.
(빨간 부분이 위의 Cortex-M4 Processor이다. ICode, DCode, System Bus Interface도 보인다. 이 버스들로 다른 Peripheral들과 명령과 데이터를 주고 받는다.)
다음은 ARM Cortex-M4 Processor를 사용한 Ti의 MicroController이다.
이 MCU도 I-Code bus, D-Code bus, System bus로 다른 Peripheral들과 명령 또는 데이터를 주고 받는다.
(뒤에 F는 Floating Point Unit이 들어있다는 의미)
(뒤에 F는 Floating Point Unit이 들어있다는 의미)
Access level & Operation modes of the processor
Features of Cortex-Mx Processor
다음은 ARM Cortex-Mx Processor의 특징이다.
- Operational mode of Processor
- The different access levels of the processor
- The Register set of the processor
- Register = General purpose register + Special purpose register.
( C언어와 같은 고급언어에서는 레지스터 셋을 이해하는 것은 그렇게 중요하지 않지만, 정보 획득을 목적으로 접근하겠다. )
- The banked stack design of the processor
( 여기서는 프로세서의 Stack memory handling mechanism을 얘기할 것이다. )
- Exceptions and Exception handling
- Interrupt handling
- Bus interfaces (ICode, Dcode, System bus) and Bus matrix
- Memory architecture of the processor, bit banding, memory map, etc를 얘기하겠다.
- Endianness
- Aligned & Unaligned data transfer
- Bootloader & IAP (In Application Programming)
Operational Modes of the Cortex-Mx Processor
Processor는 두 가지의 oprational mode로 동작한다.
- Thread mode
⇒ 프로그래머의 Application code는 Processor의 Thread mode에서 실행된다. (Thread mode는 User mode라고도 불린다.)
- Handler mode
⇒ 모든 Exception hanlders 또는 Interrupt handlers는 Processor의 Handler mode에서 실행된다.
Processor는 항상 Thread mode로 시작한다.
그런데 Core가 System exception 또는 임의의 external interrupt를 만나게 되면, Core는 이와 관련된 Interrupt Service Routine을 실행하기 위해서 Handler mode로 스위칭된다.
Operation modes code demonstration
Interrupt가 발생하면, Processor의 mode는 Handler mode로 전환된다.
위의 예제에서는 generate_interrupt라는 함수에서 IRQ3에 대한 Software interrupt를 발생시켜서, Processor의 mode를 Thread mode에서 Handler mode로 전환한다. (ISR은 Handler mode에서 실행된다.)
다음과 같이, Processor의 현재 mode는 Interrupt Program Status Register를 보면 알 수 있다.
ISR_NUMBER [8:0]가 0이면 Thread mode인데, 위의 예제에서 처음에는 ISR_NUMBER가 0이므로 Thread mode임을 확인할 수 있다.
그런데 RTC Handler의 ISR에 들어가면, ISR_NUMBER는 0에서 19로 바뀐다. 값 19는 IRQ3에 해당된다. 그리고 ISR에서 User Application으로 빠져나오면 ISR_NUMBER는 다시 19에서 0이 된다.
결과적으로 System Exception 또는 Interrupt가 발생하면, Processor의 mode는 Thread mode에서 Handler mode로 전환된다.
(추가)
Handler mode는 항상 Privileged Access level이기 때문에, Processor의 모든 것을 컨트롤할 수 있다.
다시 말해, Processor의 모든 리소스(Cortex-Mx Processor의 모든 System level registers)에 접근할 수 있고 수정할 수 있다.
(Interrupt configuration을 바꾸거나 Control register를 수정할 수도 있다.)
(Interrupt configuration을 바꾸거나 Control register를 수정할 수도 있다.)
Thread mode의 default Access level도 Privileged Access level인데, 필요하면 Access level을 Non-Privileged Access level로 바꿀 수 있다.
그런데 Non-Privileged Access level에서는 프로세서의 System level setting (System specific register 등)을 바꿀 수 없기 때문에 주의해야 한다.
Access level of Processor
- Processor는 다음과 같이 두 가지 종류의 Access level을 제공한다.
① Privileged Access Level (PAL)
② Non-Privileged Access Level (NPAL)
- 만약 코드가 PAL로 실행된다면, 코드는 Processor에 대한 모든 권한(Processor specific resources, restricted registers)을 갖는다.
- 만약 코드가 NPAL로 실행된다면, 코드는 Processor에 대한 권한이 제한된다.
- Thread mode에서 기본적으로 코드는 PAL로 실행된다.
- Processor가 Thread mode일 때 기본적으로 Code는 PAL로 실행되는데, 프로그래머의 필요에 따라 Access level을 PAL에서 NPAL로 바꿀 수 있다.
그런데, 일단 Access level이 PAL에서 NPAL로 바뀌면, Processor의 Operational mode를 Handler mode로 바꾸지 않는 이상 PAL로 돌아갈 수 없다.
- Handler mode에서 코드는 항상 PAL로 실행된다.
- Access level을 바꾸고 싶다면, Processor의 CONTROL 레지스터를 사용하면 된다.
Core registers part – 1~2
- R0 ~ R12 레지스터들은 General purpose로 사용된다.
( Data operation, Data manipulation, Data store, …)
- 모든 Core 레지스터들은 32비트의 크기를 갖는다.
- R13은 SP (Stack Pointer)로 불리고, Stack memory를 Track 하는데 사용된다.
- PSP : Processor Stack Pointer
- MSP : Main Stack Pointer
- PSP와 MSP은 Banked version of SP라고 불린다.
- R14는 LR (Link Register)로 불리고 Subroutines / Function calls, Exceptions의 Return information을 저장한다.
fun2 함수를 호출하면 PC는 fun2의 주소로 Load되고, 동시에 LR은 다음 명령어인 fun1_ins_2의 주소를 갖게 된다. (Return address 저장)
fun2가 종료될 때, PC는 이전에 저장한 LR값으로 Load된다.
- R15는 PC (Program Counter)로 불리고, 현재 프로그램의 주소를 값으로 가진다.
Reset될 때, PC는 Reset vector의 값 (0x0000_0004)으로 Load된다.
그리고 PC의 0번 비트는 Reset될 때 EPSR T-bit로 Load되는데, 이 값은 반드시 1이어야 한다.
(그렇지 않으면 Fault exception이 발생한다.)
Core registers part – 3
PSR (Program Status Register)은 프로그램의 현재 실행 상태를 알려주는 레지스터이다.
PSR은 3개의 Sub 레지스터로 구성되어 있다.
- APSR (Application Program Status Register) : APSR은 이전 명령의 실행에 대한 Condition flag의 현재 상태를 갖는다.
- IPSR (Interrupt Program Status Register) : IPSR은 현재 실행되고 있는 ISR (Interrupt Service Routine)의 Exception type number 정보를 갖는다.
- EPSR (Execution Program Status Register) : EPSR은 Thumb state bit와 Execution state bits를 갖는다.
(추가) EPSR의 T bit에 대해서
- 다양한 ARM Processor들은 ARM-Thumb의 interworking을 지원한다.
즉, ARM state와 Thumb state 사이에 전환이 가능하다는 말이다.
- Processor가 ARM ISA의 명령을 실행하려면, ARM state이어야 한다.
반대로 Processor가 Thumb ISA의 명령을 실행하려면, Thumb state이어야 한다.
- 만약 EPSR의 T bit가 1이면, Processor는 다음에 실행할 명령이 Thumb ISA의 명령이라고 간주한다.
- 만약 EPSR의 T bit가 0이면, Processor는 다음에 실행할 명령이 ARM ISA의 명령이라고 간주한다.
- Cortex-Mx Processor는 ARM state를 지원하지 않는다.
그러므로 T bit의 값은 반드시 항상 1로 유지되어야 한다.
만약 T bit가 1이 아닌 0이 된다면, Hard Fault Exception (Usage Fault Exception)이 발생한다.
Memory mapped and Non-Memory mapped Registers of the MCU
- Non-memory mapped register들은 고유 주소가 없어서, 프로그래머가 C언어의 주소 역참조 방식으로 접근할 수 없다.
접근하려면 어셈블리 명령을 사용해야 한다. (그래서 Processor memory map에 포함되지 않는다.)
- Memory mapped register들은 고유 주소가 있기 때문에 Processor memory map에 포함된다.
프로그래머가 C언어의 주소 역참조 방식으로 접근할 수 있다.
Memory mapped register들은 Processor peripheral에 속하는 Register들 (NVIC, MPU, SCB, DEBUG, etc)과 Microcontroller peripheral에 속하는 Register들 (RTC, I2C, TIMER, CAN, USB, etc)의 두 종류로 나뉘어진다.
ARM GCC Inline assembly coding part – 1
Inline assembly code는 C 프로그램 안에서 Pure assembly code를 작성하는데 사용된다.
기본적으로 우리는 Processor core register에 access하는데 Inline assembly code를 사용한다.
(General purpose registers, Special registers, Stack pointers 등)
(General purpose registers, Special registers, Stack pointers 등)
각 Compiler마다 서로 다른 Syntax를 갖기 때문에, Inline assembly code를 사용하기 위해서는 Compiler의 Inline assembly syntax를 따라야 한다.
GCC Inline assembly code syntax는 다음과 같다.
Assembly instruction : MOV R0, R1
Inline assembly statement : __asm volatile(“MOV R0, R1”);
다음은 GCC Inline assembly code의 예시이다.
왜 C 프로그램에서 인라인 어셈블리 코드를 쓸까?
예를 들어, C변수 data를 ARM 레지스터 R0로 옮기고 싶다고 가정하자.
이건 C statement만으로 수행이 불가능하고, Inline assembly code를 사용해야 수행이 가능하다.
반대로 Control 레지스터의 값을 C변수 control_reg로 옮기고 싶다고 가정하자.
이 작업 또한 Inline assembly code를 사용해야 수행이 가능하다.
이러한 상황들에서 C 프로그램에 Inline assembly code가 필요하다.
다음은 GCC Inline assembly statement의 General form이다.
volatile attribute는 컴파일러에게 해당 Assembly statement를 최적화하지 말라는 의미이다.
ARM GCC Inline assembly coding part – 2
다음은 Inline assembly code를 사용해서 메모리에 저장된 두 값을 더하고 다시 메모리에 저장하는 예제이다.
일단, 0x2000_1000번지에 값 1, 0x2000_1004번지에 값 2를 써넣는다.
다음과 같이, 레지스터 R1과 R2에 메모리 주소 0x2000_1000, 0x2000_1004를 저장한다.
__asm volatile("LDR R1, =#0x20001000") __asm volatile("LDR R2, =#0x20001004")
그러면, 그림의 오른쪽 General Registers 창에서 R1과 R2에 메모리 주소 0x2000_1000, 0x2000_1004가 각각 저장된 것을 확인할 수 있다.
그리고 다음의 명령을 실행하면,
__asm volatile("LDR R0, [R1]") __asm volatile("LDR R1, [R2]")
다음 그림과 같이 R0에는 R1이 가리키는 주소(0x2000_1000)의 값인 1이 저장되고, R1에는 R2가 가리키는 주소(0x2000_1004)의 값인 2가 저장된다.
그리고 다음의 명령을 실행하면,
__asm volatile("ADD R0, R0, R1")
다음 그림과 같이 R0에는 기존의 R0와 R1을 더한 값인 3이 저장된다.
그리고 다음의 명령을 실행하면,
__asm volatile("STR R0, [R2]")
다음 그림과 같이 R2가 가리키는 주소 (0x2000_1004)에 R0값인 3이 저장된다.
ARM GCC Inline assembly coding part – 3
이번에는 ARM GCC Inline assembly code에서 Input 또는 Output operand와 Constraint string이 있는 경우를 살펴본다.
각각의 Input 또는 Output operand는 괄호안의 C 표현과 constraint string으로 표현된다.
다음은 Inline assembly code로 C의 변수 val을 ARM 레지스터 R0로 옮기는 예제이다.
이 경우는 input operand만 있고, output operand는 존재하지 않는다.
다음은 ARM GCC Inline assembly code의 Constraint와 Constraint modifier를 정리한 표이다.
Constraint ‘r’은 Data manipulation에 General purpose register를 사용하라고 Compiler에게 말하는 것이다.
다음은 위의 예제의 코드이다.
위의 예제 코드를 실행하면, C변수 val의 값 50이 레지스터 R0에 Copy되는 것을 다음 그림에서 확인할 수 있다.
Project의 list파일에서는 Project의 DisAssembly를 볼 수 있다.
위의 Inline assembly statement에서 Compiler는 다음과 같은 절차를 수행한다.
- 변수 val의 값을 memory (지역변수라서 Stack에 할당)에서 읽어서 R3에 저장한다.\
- 마지막 Data copy 명령 (mov)에서 R3는 소스 레지스터 역할을 한다.
다음은 Inline assembly code로 CONTROL 레지스터의 값을 C의 변수 control_reg로 옮기는 예제이다.
mov 명령은 General purpose register를 다룰 때 사용하는 명령이라서, CONTROL 레지스터와 같은 Special register를 다룰 때는 사용할 수 없다.
그래서 mov 명령 대신에 MRS (Move from special register to register) 명령을 사용한다.
이 경우에는 Input operand는 없고, output operand만 존재한다.
‘=’는 Constraint modifier이고, ‘r’은 Constraint character이다.
‘=’는 ‘Write-only operand, usually used for all output operands’의 의미이다.
다음은 위의 예제의 코드이다.
위의 예제 코드를 실행하면, 다음 그림과 같이 C변수 control_reg의 값이 초기값 9에서 CONTROL 레지스터의 값인 0으로 변경된다.
그리고 R7이 가지고 있는 값은 변수 control_reg의 주소값(SRAM에 있는 스택영역)이다.
다음은 위의 예제의 Project의 list파일이다.
위의 Inline assembly statement에서 Compiler는 다음과 같은 일을 수행한다.
- CONTROL 레지스터의 값을 r3에 저장한다.
- r3에 저장된 CONTROL 레지스터의 값을 C의 변수 control_reg에 저장한다.
다음은 Inline assembly code로 C 변수 var1의 값을 C 변수 var2로 복사하는 예제이다.
다음은 Inline assembly code로 pointer의 값을 다른 C 변수로 복사하는 예제이다.
Reset sequence of the processor
Reset sequence of the processor
다음은 Cortex-M Processor의 Reset sequence이다.
- Processor를 reset하면, PC는 0x0000_0000의 값으로 Load된다.
- Processor는 0x0000_0000번지의 값을 읽어서 MSP에 저장한다.
즉, Processor는 가장 처음에 Stack Pointer를 0x0000_0000번지의 값으로 초기화한다.
(MSP는 Main Stack Pointer이다.)
- Processor는 0x0000_0004번지의 값을 읽어서 PC에 저장한다.
(0x0000_0004번지의 값은 Reset Handler의 주소이다.)
- PC는 Reset Handler로 Jump한다.
(Reset Handler는 그저 필요한 초기화 작업을 수행하는 C 또는 assembly 함수이다.)
- Reset Handler에서 Application의 main 함수가 호출된다.
Reset sequence of the processor cntd
Processor는 처음에 Vector table에서 MSP 값을 fetch하고, Vector table의 Reset Handler의 주소를 보고 Reset Handler로 Jump한다.
Reset Handler는 대개 startup 파일에 정의되어 있다.
Reset Handler의 일은 몇몇의 early initialization을 수행하고, User Application의 main 함수를 호출하는 것이다.
Reset Handler는 Data section initialization, BSS section initialization, Initialization of Standard C library (필요한 경우)를 수행하고, User Application의 main 함수를 호출한다.
Access level and T bit
Demonstration of access level of the processor
다음은 Processor의 Access level을 Non-Privileged로 바꾸고, System Control Register에 접근을 시도하는 예제이다.
이것들(0xE000_EF00, 0xE000_E100)은 ARM Cortex-Mx Processor의 System Control Register의 주소들이라서 오직 Privileged Access Level에서만 접근할 수 있다.
Non-Privileged Access Level에서 이 Register들에 접근하려고 시도하면, Processor Fault Exception이 발생한다.
CONTROL 레지스터는 Non-memory mapped register라서 C언어의 주소 역참조 방식으로 접근할 수 없다. 어셈블리 코드를 사용해야 한다.
CONTROL 레지스터의 0번 비트를 1로 바꿈으로써, Processor의 Access level을 Non-Privileged Access Level로 변경하였다.
그리고 나서 ‘ *pISER0 |= ( 1 << 3); ‘ 명령을 실행하면, 위와 같이 Usage Fault Error (Hard Fault Error)가 발생한다.
그러면 어떻게 Non-priv에서 Priv level로 돌아갈 수 있을까?
처음 Thread mode에서 시작해서 CONTROL 레지스터의 0번 비트를 1로 바꾸면, Priv에서 Non-Priv로 바뀐다.
Non-Priv에서는 더이상 CONTROL 레지스터에 접근할 수 없기 때문에, 다이렉트로 Priv로는 돌아갈 수 없다.
Priv로 돌아갈 수 있는 방법은 Processor의 mode를 Thread mode에서 Handler mode로 변경하는 것이다. (By Triggering System Exception or Interrupt)
ISR은 항상 Priv로 수행되기 때문에, 여기서 CONTROL 레지스터의 0번 비트를 0으로 modify하고 Priv로 전환해서 Thread mode로 돌아갈 수 있다.
Importance of T bit of the EPSR
- 다양한 ARM Processor들은 ARM-Thumb의 interworking을 지원한다.
즉, ARM state와 Thumb state 사이에 전환이 가능하다는 말이다.
- Processor가 ARM ISA의 명령을 실행하려면, ARM state이어야 한다.
반대로 Processor가 Thumb ISA의 명령을 실행하려면, Thumb state이어야 한다.
- 만약 EPSR의 T bit가 1이면, Processor는 다음에 실행할 명령이 Thumb ISA의 명령이라고 간주한다.
- 만약 EPSR의 T bit가 0이면, Processor는 다음에 실행할 명령이 ARM ISA의 명령이라고 간주한다.
- Cortex-Mx Processor는 ARM state를 지원하지 않는다.
그러므로 T bit의 값은 반드시 항상 1로 유지되어야 한다.
만약 T bit가 1이 아닌 0이 되는 순간에 Usage Fault Exception이 발생하게 된다.
- PC의 lsb(0th bit)는 T bit과 연결되어 있다.
만약 PC에 어떤 주소나 값을 Load한다고 가정하면, 0번 비트는 T bit로 Load 된다.
- 그래서 프로그래머가 PC에 Load하는 어떤 주소라도 0번 비트는 1로 유지된다.
(컴파일러가 알아서 하기 때문에 프로그래머가 걱정하지 않아도 된다.)
- 0번 비트가 1로 유지되어 PC에 Load되는 값이 모두 홀수이므로 벡터 테이블에서 모든 벡터 주소들이 홀수이고 1씩 증가하는 이유이다.
다음은 T-bit를 살펴보는 예제이다.
0번 비트가 1이다.
함수의 주소를 함수 포인터에 저장하기 전에 컴파일러가 함수 주소의 0번 비트를 1로 만들어버린다.
함수의 진짜 주소는 odd가 아니라 even이다. 이는 list 파일에서 확인할 수 있다.
fun_ptr();를 실행하면, 함수 포인터의 값이 PC로 Load 되는데, 이 때 PC의 0번 비트값이 T-bit로 복사된다.
만약 PC의 0번 비트값이 1이 아닌 0이면, Fault Exception이 발생해버린다.
이번에는 ‘ fun_ptr = change_access_level_unpriv; ‘ 대신, 함수의 주소를 함수 포인터에 하드 코딩 해본다.
그러면 Processor는 Hard Fault Exception이 발생한다.
ARM Cortex-M3/M4 Processor - 1IntroductionProcessor Core vs ProcessorProcessor VS MicroControllerAccess level & Operation modes of the processorFeatures of Cortex-Mx ProcessorOperational Modes of the Cortex-Mx ProcessorOperation modes code demonstrationAccess level of ProcessorCore registers part – 1~2Core registers part – 3(추가) EPSR의 T bit에 대해서Memory mapped and Non-Memory mapped Registers of the MCUARM GCC Inline assembly coding part – 1ARM GCC Inline assembly coding part – 2ARM GCC Inline assembly coding part – 3Reset sequence of the processorReset sequence of the processorReset sequence of the processor cntdAccess level and T bitDemonstration of access level of the processorImportance of T bit of the EPSR