Cortex-M 은 4GB 주소 공간을 미리 용도별로 나눠 둔다.
0x0000_0000 코드 (플래시)
0x2000_0000 SRAM
0x4000_0000 주변장치 레지스터 ← GPIO·타이머·UART 가 여기 산다
0xE000_0000 코어 내부 (NVIC·SysTick·DWT)
주변장치가 주소라는 것의 의미
#define GPIOA_ODR (*(volatile uint32_t *)0x40020014)
GPIOA_ODR |= (1u << 5); // 이 대입이 곧 핀에 전압을 거는 동작이다
포인터에 값을 쓰는 평범한 C 코드가 하드웨어를 움직인다. 메모리 접근과 하드웨어 제어가 같은 문법이라 편하지만, 그래서 잘못된 포인터가 곧 하드웨어 오작동이 된다.
왜 이렇게 설계했나
별도의 I/O 명령을 두지 않아도 되므로 명령어 집합이 단순해지고, C 로 하드웨어를 다룰 수 있다.
대신 컴파일러가 이 주소의 특별함을 모른다 — 그래서 volatile 이 필요해진다.