Category (Click)
개발보드 덕질하기

[Embedded] 메모리는 SRAM이 아니다!!! feat. MMIO

 차완기 - @7/30/2023, 7:47:00 PM

 0x08... 아니 그게 뭔데!!!

STM32 MCU에서 USB DFU 부트로더를 만들던 중 Flash의 영역은 왜 0x0800 0000 부터 시작하는지 궁금해지게 되었습니다. 아니 그 전에 저 Hex 값은 대체 무엇을 의미하는 것일까요?
궁금증을 해결하기 위해 공부한 내용을 남겨보았습니다.

MCU에서 Flash와 Peripheral을 접근하는 방법

우선 저 Hex 값의 정채를 미리 밝히자면 STM32F103RB의 Memory Map 중 “Flash memory” 영역에 해당하는 메모리 주소입니다. 잉...? 메모리는 SRAM 아닌가? 왜 Flash...?
이 한 문장을 이해하기 위해 여러 사전지식이 필요했습니다.

MMIO

메모리 맵 입출력(영어: Memory-mapped I/O, MMIO)는 마이크로프로세서(CPU)가 입출력 장치를 액세스할 때, 입출력과 메모리의 주소 공간을 분리하지 않고 하나의 메모리 공간에 취급하여 배치하는 방식이다. 따라서 전체 메모리의 주소공간에 입출력 장치의 메모리나 레지스터를 메모리로 취급하여 전체 메모리의 일부분으로 특정영역에 할당하여 배치하는 방식이다.
위 내용에 따르면 MMIO 방식을 사용하는 MCU의 메모리에는 변수가 저장되는 영역 이외에도 하드웨어를 제어하기 위한 레지스터 등도 짬뽕되어 있습니다. “메모리”는 더 이상 주 기억장치(SRAM)만을 의미하지 않는것이죠.
대부분의 RISC 아키텍처 프로세서가 MMIO를 사용하고 있고 ARM 아키텍처 프로세서를 사용하는 STM32 MCU 역시 MMIO를 사용합니다. 실제로 이걸 눈으로 확인할 수 있으면 더 잘 이해가 될텐데요, 다소 복잡한 STM32 대신 그나마 단순한(?) 8-bit AVR MCU를 예시로 살펴보겠습니다.
학창 시절에 AVR을 맛본(?) 기억에 의하면 GPIO 제어를 위해 PORTx 레지스터를 사용했습니다. ATmega328 기준으로 IDE 상에서 PORTB 키워드의 정의(iom328p.h)로 가보면 _SFR_IO8 매크로(sfr_defs.h)를 통해 전처리 과정 이후에는 *0x25로 해석되는 것을 확인할 수 있습니다.
즉, PB0을 켜기 위해 위와 같이 코드를 작성하면 아래와 같은 의미를 가지게 됩니다.
여기서 volatile 키워드는 컴파일러의 최적화를 방지합니다.
GPIO를 연속적으로 끄고 켜는 동작을 했을 때 컴파일러는 이를 최적화하여 켜는 동작만 남겨두게 되는데, volatile 키워드를 붙여 이러한 최적화를 막습니다.
위 코드가 실행된다면 0x25 위치의 메모리 상에 0b00000001 이라는 값을 쓰게 되는데요, 메모리의 0x25 위치에 무엇이 있길레 위 코드가 동작하는것일까요?

Memory Map

MCU 데이트시트 상의 Data Memory Map 부분을 확인해보면 바로 알 수 있습니다.
0x25 위치는 0x20~0x5F 범위에 해당하는 64 I/O Registers 영역에 해당하네요. 그런데, 뒷쪽의 0x100~0x8FF 영역을 보면 변수가 저장되는 SRAM 영역인 것을 알 수 있습니다. I/O 제어를 위한 레지스터와 변수가 저장되는 SRAM이 동일한 공간에 있다는 것이죠.
이처럼 MMIO 방식을 사용하는 프로세서에서는 메모리의 위치에 따라 참조하는 대상이 SRAM이 될 수도, I/O 제어를 위한 레지스터가 될 수도 있습니다.
AVR의 경우 SRAM과 I/O가 동일한 메모리 영역에 묶여있는 MMIO 방식이지만 하버드 아키텍처를 사용하기 때문에 Data, EEPROM 영역은 메모리 영역과 분리되어 있습니다.
참고로 MMIO와 대비되는 PMIO방식은 SRAM과 I/O가 완전히 분리되어 I/O가 아무리 많아지더라도 SRAM의 영역이 줄어들지 않습니다.

정리

1.
MMIO 방식의 MCU에서 메모리는 주기억장치만을 의미하지 않는다.
2.
메모리의 특정 영역은 변수를 저장하는 SRAM, Flash 메모리의 특정 영역, Peripheral 등이 될 수 있다.
3.
이는 프로세서의 Memory Map을 통해 확인할 수 있다.

STM32F103 Memory Map

다시 돌아와서 의문점이었던 0x0800 0000을 알아내기 위해서는 STM32F103의 메모리 맵을 보아야 합니다. 데이터시트의 “Memory mapping” 섹션에서 Memory Map을 확인할 수 있습니다.
STM32F103xB Datasheet - 4-9 Memory map
의문점을 가지게 한 0x0800 0000 위치는 0x0800 0000 ~ 0x0801 FFFF 범위의 “Flash memory” 영역입니다. 즉, 만약 이 영역을 포인터로 접근하게 되면 이 위치의 내용을 읽거나 쓰게 되는것이죠.

STM32 개발환경에서 확인해보기

여기까지 왔는데, 실제로 눈으로 확인해봐야겠죠?
디버거 상에서 변수 2개(bss, stack), 문자열 리터럴(test→’t’), 함수의 메모리 주소를 읽어보았습니다.
SRAM에 저장되는 변수들은 Memory Map에서 SRAM 구역인 0x2000 0000 이후에 위치하고 있고, Flash에 저장되는 문자열 리터럴과 함수는 Flash 구역인 0x0800 0000 이후에 위치하고 있는 것을 알 수 있습니다.
궁금증 해결!!