[Embedded] Cortex M4 프로세서
임베디드 시스템은 일반 PC와 다르게 하나의 기능에 최적화 되어 있다.
그냥 설계부터가 다름.

Flash는 프로그램을 저장하고, SDRAM은 실행 중 데이터를 처리한다.
GPIO는 디지털 I/O를 처리하고, ADC/DAC와 UART는 통신을 처리한다.
저 모든 구성요소들이 하나의 System On a Chip으로 집적된 형태로 구성됨. SoC가 하나의 프로세서라고 보면 된다.
프로세서도 여러 종류가 있음
CPU (Central Processor Unit) : 명령어 처리 중심의 프로세서로 특정 작업을 잘 수행한다기보다는 범용적인 작업을 처리함.
DSP (Digital Signal Processor) : 아날로그 신호를 디지털로 변환해 필터링하거나 신호변환을 처리함.
GPU (Graphic Processing Unit) : 원래는 게임 화면 영상처리 전용 프로세서로 개발됐는데 지금은 AI 가속기로 사용됨.
NPU (Neuromorphic Processing Unit) : AI를 가속시키는 용도의 뉴럴넷 전용 프로세서. TPU도 NPU의 일부임.
MCU (Microcontroller Unit) : 제어 목적을 위한 소형 프로세서. Cortex M4도 MCU 이다.
당연한 말이지만 센서가 수집하는 데이터는 연속적인 성격을 가지지만, 컴퓨터은 이산 데이터만 처리할 수 있으니 변환이 필요함.
요즘은 GPU나 NPU처럼 특정 성격을 가진 칩이 등장하고 있다.
GPU도 사실은 게임 전용으로 개발됐기에 AI에 완전히 fit하지는 않음. 그래서 엔비디아에서는 트랜스포머를 가속 할 수 있는 회로를 따로 만들어서 판매하고 있다.
GPU 아키텍처는 덧셈 곱셈 연산만 잘해서 범용성이 떨어지니, 센서 I/O나 시스템 부팅처럼 범용적인 작업은 CPU가 담당하고 특정 도메인에서 필요한 연산은 GPU가 담당하는 방식으로 구성된다.
우선 Cortex M4 프로세서는 RISC 방식으로 설계됨.
CISC와 다르게 RISC는 단순한 명령어를 빠르게, 많이 처리할 수 있어 파이프라인 설계가 쉽고 임베디드 시스템에 적합하다.
핸드폰이나 임베디드 시스템은 개인 PC와 다르게 배터리로 동작하니, 전력을 많이 먹는 CISC보다는 저전력에 유리한 RISC를 사용하는 편이 합리적임.
추가로 단순한 구조 기반에 Add-On 방식으로 기능을 붙이는것도 굉장히 쉽고..
요즘 디바이스들도 웬만하면 SoC 방식으로 RISC + a로 설계되어있다.
칩 하나에 다 때려박으면서 최적화도 기가막히게 하는..
ARM Holdings는 Apple과 Acorn과 VLSI가 만든 회사로, 설계도만 만들어서 반도체 회사에게 라이센스 장사를 하는 회사.
CPU 설계도를 삼성, 퀄컴같은 팹리스 회사에게 팔아넘기는 식으로 돈을 벌고있음.
ARM은 애초에 RISC방식으로 설계된 회사라서 언급.. (Acorn RISC Machines)
RISC-V는 ARM이 수익을 너무 추구해서 새로 도입됐지만, 여전히 ARM의 시장 점유율이 너무 크긴 하다.
그냥 ARM은 임베디드 프로세서의 신임

Cortex는 A / R / M 계열으로 제품군이 나뉜다.
Cortex-Application : 스마트폰이나 태블릿 같은 고성능 앱을 실행할 때 사용됨.
Cortex-RealTime : 자동차, SSD 컨트롤러같은 실시간 제어를 다룰 때 사용됨.
MCortex-Microcontroller : 저전력 제어, 임베디드 MCU를 다룰 때 사용됨.

Cortex-M4는 MCU + DSP + FPU가 하나로 합쳐진 구조로 구성된다.
MCU : 저전력, 인터럽트 처리 등 일반 마이크로컨트롤러의 모든 기능을 처리한다.
DSP : 뭐 말 그대로.. 신호처리 알고리즘에 사용된다. Single-Cycle MAC 이나 Barrel Shifter 같은..
FPU : 32비트 부동소수점 연산을 하드웨어로 처리한다. 이게 없으면 float 연산을 소프트웨어로 처리해야 함.
AI 시대에서는 Precision 개념이 점점 더 중요해지고 있음.
Filter가 곧 Convolution 연산이다. 사실 CNN도 DSP의 필터 연산과 구조가 같다.
임베디드 시스템을 설계한다면 인터럽트가 정말 정말 중요하고, 인터럽트를 기반으로 코드를 작성해야 한다.
할 일 없으면 Sleep, SSE 처럼 이벤트가 발생할 때만 깨어나도록 하고.
이런식으로 설계하는게 Energy Efficient 하다.
인터럽트로 CPU를 깨울 때는 Clock Pulse를 사용함. Pulse를 올렸다가 내렸다가 반복하는 식.
Clock 속도를 높이면 CPU를 깨우는거고.. Clock 속도를 낮추면 꺼져있다는거고.. 이런식으로 파워 소모를 거의 안 하도록 설계함.
Polling 방식을 임베디드 시스템에서 사용한다면 CPU가 계속 풀파워로 돌게 되니 비효율적임.


Prefetch 버퍼가 3개의 명령어를 미리 읽어두기에, CPU가 현재 명령어를 실행하는 동안 다음 명령어를 해석하고 다음 명령어를 메모리에서 가져올 수 있다.
그냥 그림 보면 어떻게 동작하는지 보임. 1 Cycle in N Instruction이 된다는것.
그러니 분기가 없는 순차적 코드에서는 Cycle Per Instruction = 1 을 달성할 수 있다.
명령어 집합은 Thumb-2를 사용한다.
Cortex-A 계열은 ARM 32-bit Instruction Set Architecture를 사용하는데, 모든 명령어가 32비트를 사용하고 조건부 실행해야 해 임베디드 시스템에서는 적합하지 않음.
Thumb 16-bit를 사용하면 ARM 32bit를 16bit로 압축해서 코드 밀도를 향상시킬 수 있지만, 성능은 잘 안 나온다.
Cortex-M 계열에서 사용하는 Thumb-2는 16비트와 32비트를 혼합해서 사용할 수 있다.
임베디드 시스템에서는 메모리를 정말 잘 최적화하고 아껴써야 한다.
간단한 ADD 명령어 하나를 실행할 때도 컴파일 했을 때 4바이트 인 것 보다는 2바이트인게 훨씬 좋으니까.. 컴파일 옵션에서 Thumb-2를 사용한다면 원래 32비트짜리를 16비트로 사용할 수 있으니 메모리를 절약할 수 있음.
다만 이러면 해석가능한 명령어의 수가 줄어들고, 접근 가능한 레지스터의 수도 줄어든다.
그러니 32비트 명령어를 일부에서는 사용한다는 컨셉의 Thumb 2를 사용한다.

R0 ~ R7 레지스터는 16비트 Thumb 명령어도 접근할 수 있다.
가장 자주 쓰이는 레지스터지만, 16비트 명령어는 인코딩 공간이 작아서 3비트밖에 표현할 수 없어 R0 ~ R7까지만 접근 가능.
R8 ~ R12 레지스터는 32비트 명령어로만 접근할 수 있다.
R13 레지스터는 Stack Pointer로, CONTROL 레지스터 비트 1에 따라 두 가지 스택 포인터를 선택한다.
Main Stack Pointer로 사용되거나 Process Stack Pointer로 사용됨.
R14 레지스터는 Link Register로 서브루틴을 호출했을 때 복귀 주소를 저장한다.
R15 레지스터는 다음에 실행할 명령어의 주소를 저장한다. 다만!! 리셋했을 때는 0x0000_0004 에서 시작함!!
운영체제에서 다 다뤘던거 같긴 함