내가 사용하는 Cortex-M 보드의 Context Switching 방식이 궁금해졌고... 이를 파헤치다보니 Cortex-M3의 근간이 되는 Armv7 아키텍처의 메모리 모델을 알아야겠다는 생각이 들었다. 오늘은 이에 대해 공부해보고자 한다!
이 글은 김동현(Austin Kim)님의 '시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 책을 참고하여 작성하였음을 밝힙니다!
Arm의 메모리 모델??
OS가 메모리를 관리하기 이전에, CPU가 메모리를 어떻게 분류하고 관리하는지 알아보자. 우선 생각해보기로는 32bit아키텍처인 만큼 메모리를 32bit(4GiB)로 분류해 주소를 부여하는 것 외에 다른 특별한 점이 있을까? 생각이 든다. 과연 그럴까?!
Arm의 메모리 모델은 시스템의 메모리를 두가지 타입으로 분류한다. '노멀 메모리'와 '디바이스 메모리'로 말이다. 이름에서 먼저 유추해본다면... 노멀 메모리는 우리가 흔히 아는 메모리이고, 디바이스 메모리는 내가 사용하던 MCU의 Memory mapped IO처럼 디바이스 제어에 필요한 무언가를 위해 매핑해둔 메모리 주소를 의미하는 것이 아닐까?
추측이 얼추 맞았다! 노멀 메모리는 명령어를 실행하거나 프로그램 실행 과정에서 처리되는 데이터나 코드를 로드하기 위한 메모리 영역이고, 디바이스 메모리는 다양한 페리펄럴을 제어하기 위한 용도로 사용되는 메모리 영역으로 Memory-mapped IO 혹은 디바이스 메모리라고 불린다.
1. 노멀 메모리
먼저 노멀 메모리에 대해 자세히 살펴보자!
개발자가 입력한 대부분의 코드는 노멀로 마킹된 메모리 영역에 로딩된다고 볼 수 있다. 이 노멀 메모리를 처리하는 과정에서 Arm 아키텍처는 내부적으로 성능을 극대화시키기 위해 다음과 같은 3가지 최적화 기법을 사용한다고 한다.
1.1 머지 액세스
'머지 액세스'는 메모리 공간에 여러번 접근하거나 연속되어있는 메모리 공간에 접근하는 2개 이상의 명령어를 한번에 처리하는 동작을 말한다. 단어 그대로, 비슷한 구역에 여러번 접근하는 명령어를 ‘묶어서(merge)’ 한번에 처리해 메모리 I/O 오버헤드를 줄인다는 말!
🙂CPU가 메모리에 접근하면서 오버헤드가 생기는 이유
메모리에 접근하려면 메모리 버스에 먼저 접근해야하고, DRAM 메모리 컨트롤러와 같은 메모리 하드웨어에서 부가적인 동작을 수행하기 때문이다. 메모리 액세스 횟수가 늘어날수록 당연히 더 많은 CPU 사이클이 소요되게 되고, 이 접근 횟수를 줄여 최적화를 이뤄줄 수 있다는 말이다!
1.2 스페큘레이션 액세스
스페큘레이션 액세스는 SW에서 접근할 것으로 예상하는 데이터를 패턴 인식과 같은 알고리즘을 활용해 미리 예측해 로딩하는 동작을 말한다고 한다. 이놈도 단어 그대로 speculate(추측하다) 해서 메모리에 액세스를 한다… 이런 의미라고 우선 이해하면 될 것 같다.
1.3 리오더링 액세스
이번에도 단어의 이름으로 추측할 수 있듯, Arm 프로세서 내부에서 메모리 I/O작업 실행 순서를 바꾸어 데이터 처리량을 높이는 리오더링 액세스 메모리 처리 방식이 있다고 한다. (물론 Arm 프로세서는 명령어간 의존성이 없는 경우에만 이렇게 순서를 바꾸어 처리한다!)
메모리 리오더링 구체적 예시!
🙂책의 표현만으론 이해가 명확하지 않아 구글링을 해보다가 메모리 리오더링에 대한 좋은 예제가 있어 가져와 보았다(출처: 책 저자 Austin Kim님의 블로그 글 https://austindhkim.tistory.com/556).

1번째 명령어: X1 레지스터가 0x10000이니 0x10000 주소에 X0레지스터의 값(8byte)을 저장한다.
2번째 명령어: ‘X1의 주소+0’위치에 2바이트 사이즈(STRH)의 데이터를 저장한다. 이 때 1번 명령어에서 실행된 0x1000 주소의 데이터가 업데이트 된다.
3번째 명령어: ‘X1주소 + 2’위치에 4바이트 사이즈 데이터를 저장한다.
그림을 보면 알 수 있듯이, 2번과 3번 명령어를 실행한 결과는 주소공간이 중복되지 않는다! 따라서 두 명령어는 의존성이 없고, 3번과 2번을 바꾸어 실행하더라도 결과는 같다. 이를 메모리 리오더링이라고 한다.
(여기서 Arm 프로세서 내부에서 2번과 3번 명령어를 한번에 실행하는 것을 앞서 말했던 머지 액세스라고 한다.)
이처럼, Arm 아키텍처의 3가지 메모리 최적화 기법이 노멀 메모리 타입으로 마킹된 메모리 영역에 적용된다. (일반적으로 대부분의 개발자가 입력한 코드가 이 3가지 특징이 적용도니 상태에서 실행된다고 한다.) 그리고 이러한 특징을 가리켜 노멀 메모리 타입은 'Weakly orderd'라는 특징이 있다고 한다. 메모리 액세스 순서가 프로시저와 항상 같이 않다는 의미에서 저런 표현을 한다고 한다!
2. 디바이스 메모리
디바이스 메모리는 처음에 말했던 것 처럼, 페리펄럴을 가리키기 위해 사용되는 메모리 영역이다.

위 사진은 Arm 아키텍처의 메모리 맵이며, 여기서 Peripherals을 제외한 나머지 영역은 노멀 메모리 영역이다.
노멀 메모리와 다른 점은 디바이스 메모리는 페리펄럴을 제어하기 위한 용도로 사용되기 때문에 명령어의 순서를 바꾸는 메모리 리오더링(아까 말한 3가지)기법을 적용하기 예민하다는 점이 있다. 자칫 잘못하면 오동작이 발생할 수 있기 때문에 디바이스 메모리로 마킹된 데이터는 명령어를 순차적으로 실행한다.
또한, 디바이스 메모리는 페리퍼럴을 제어하는 용도로 사용되기 때문에 명령어는 디바이스 메모리 타입으로 마킹된 메모리 영역에 load를 해서는 안된다고 한다. 따라서 이 디바이스 메모리 영역을 미리미리 'not executeable'영역으로 설정하여 오동작을 방지해야한다고 한다!
디바이스 메모리에 구체적으로 무엇이 있길래 R/W을 할 수 있다는 것일까?
※ 책에서는 계속 디바이스 메모리 영역에는 읽기/쓰기가 가능하지만 실행할 수 없다고 설명을 한다. 디바이스 메모리에 구체적으로 무엇이 있길래 R/W을 할 수 있다는 것일까? 특정 페리펄럴을 제어하는 레지스터값을 매핑해 두었다고 말하는데, 레지스터라는 것이 구체적으로 잘 이해가 가지 않는다... 그래서 내가 사용중이던 STM32F103RB 보드, Cortex-M3 MCU보드의 메뉴얼을 참고해보았다.

위 사진은 Cortex-M3 보드의 메모리 맵이다. 여기서 하이라이트한 SPI1의 주소공간을 봐보자. 0x4001_3000 부터 0x4001_33FF 까지 총 1024byte를 사용하고있다. 저 영역을 봐보자...

요렇게 36byte를 SPI 레지스터들에 매핑해두었음을 알 수 있다(offset 0x00 ~ 0x23까지 36byte).
근데, SPI1에 할당된 1024byte중 나머지 바이트들은 어디에 할당된걸까?
구글링을 해도 적절한 답을 찾지 못하여서 Gemini에게 물어보니 1024바이트 중 나머지 영역은 Reserved 상태로 비어있다고 한다. 그 이유는
1) 하드웨어 설계의 편의성 때문이라고 한다. 하드웨어 설계 시 페리펄럴에게 주소를 할당하면서 1byte 단위로 촘촘하게 설계하는 것 보다, 1KB나 4KB단위로 큼직하게 구역을 먼저 나누고 그 안에 실제 레지스터를 배치하는 것이 편리하다는 이유라 한다.
2) 또한 나중에 같은 시리즈의 상위 칩이 나왔을 때 SPI기능이 추가되어 레지스터가 늘어날 수 있기 때문에 미래를 위해 넉넉히 공간을 비워두기도 한다고 한다.
설득되는 답변이긴 하다. 일단은 이렇게 넘어가도록 하자...(아시는 분 있으시면 알려주시면 감사하겠습니다)
아무튼!!! 중요한 점은 디바이스 메모리의 주소에는 하드웨어를 조작하기 위한 레지스터 주소들과 매핑이 되어있고, 그 구체적인 레지스터의 사용법은 manual이나 datasheet를 참고해서 사용법을 알 수 있다는 점! 만 기억하고 넘어가보도록 한다.
+ 메모리 맵의 노멀 메모리 타입과 디바이스 메모리 타입을 구분하는 차이점은?
바로 명령어나 메모리를 리오더링하는 동작이다. 노멀 메모리 타입으로 분류된 명령어나 데이터를 Arm 코어가 실행하면 내부에선 다양한 방식으로 최적화를 수행한다. 반면, 디바이스 메모리 타입으로 분류된 명령어, 데이터를 실행하면 이러한 최적화를 진행하지 않는다!
디바이스 메모리 처리 방식
그렇다면 디바이스 메모리는 그냥 순서대로 쭈우욱 처리되기만 하는걸까? 그렇다고 한다... 예를 들어서,
- 0xD000_0000 주소 접근: 페리퍼럴에 인터럽트 활성화
- 0xD000_0004 주소 접근: 페리퍼럴에 인터럽트 잘 받았다는 ACK를 전달
- 0xD000_0008 주소 접근: 페리퍼럴의 인터럽트를 비활성화
이러한 명령이 순서대로 이뤄지는데, 이를 마음대로 섞어버린다면 기기는 오작동을 일으킬 것이다.
다음 글에서는 노멀 메모리의 주요 특징인메모리 리오더링과 Weakly Ordered 속성을 살펴보자!
'Embedded Systems > Arm 아키텍처' 카테고리의 다른 글
| [Arm] 익셉션에 대해 공부해보자! (0) | 2026.04.02 |
|---|---|
| [ARM] Armv7의 Processor Mode(동작 모드) + 조금의 익셉션 (0) | 2026.03.26 |
| [Arm] '#' immediate value(즉치값) 너는 누구니? (0) | 2026.03.26 |
| [ARM] 익셉션? 너는 누구니? (0) | 2026.03.17 |