(구) 자료/면접을 위하여

[6] Memory Mapped IO, I/O Mapped I/O

뜐뜐뜐 2018. 4. 28. 01:27

하반기의 치욕을 씻겠다.



CPU와 입출력 기기에 접속하는 방법에는 크게 두 가지가 있다.


Memory Mapped I/O와 I/O Mapped I/O.


1. Memory Mapped I/O

- 메모리와 I/O가 하나의 연속된 address 영역에 할당된다. 즉, I/O가 차지하는 만큼 메모리 용량은 감소한다.


- CPU 입장에서는 메모리와 I/O가 동일한 외부기기로 간주되므로, 이들을 액세스 하기 위해 같은 신호(read, write)를 사용한다.

- 소프트웨어적으로도 메모리에대한 액세스 or I/O에 대한 데이터 입출력이 동일한 것으로 간주된다.


- Load나 Store 명령에 의해 수행된다.

- 대표적인 프로세서로는 ARM, MIPS, PowerPC, M68K가 있다.

- 위 방식의 가장 큰 장점은 포트 입출력 구현 시, 복잡성이 사라지기 때문에 CPU 내부적으로 로직이 덜 필요하다.

- 이는 저렴하고, 빠르면서 쉬운 CPU를 만들기에 충분하다.

- 이 점이 RISC가 추구하는 바이다.(임베디드 시스템 구현시 장점으로 적용됨)

- 그러나, 주소와 데이터 버스를 많이 사용하게 되어, 메인 메모리에 접근하는 것보다 매핑된 장치에 접근하는 것이 더 느리다.

- 사용시 I/O영역 변수는 volatile 타입으로 선언해야 한다. (컴파일러의 최적화 방지)

- I/O영역은 Non-cacheable로 설정해야 한다.(캐시메모리로 접근할 경우, 변경된 내용은 가져올 수 없을 수도 있다.)


<출처 : http://tkdguq0110.tistory.com/entry/Memory-Mapped-IO>



* 여기서 잠깐, volatile이란?

- C의 키워드 중 하나. 다음의 코드를 보자.


*(unsigned int *)0x70A0 = 0x4001; // 0번
*(unsigned int *)0x70A0 = 0x4002; // 1번
*(unsigned int *)0x70A0 = 0x4003; // 2번
*(unsigned int *)0x70A0 = 0x4004; // 3번
*(unsigned int *)0x70A0 = 0x5555; // 4번


- 위의 코드는 Optimize 옵션(코드 최적화 옵션)에 의해 0~3번은 전부 제거하고 4번 코드만 컴파일해서 어셈블리 코드를 만든다.

- 이게 문제를 일으킬 수 있다. Software인 컴파일러 입장에서는 Optimize가 당연하나, 하드웨어적인 입장에서는 C 컴파일러의 치적화는 치명적 결과를 초래할 수 있다.

- 만약 위의 0x70A0의 주소를 메모리 주소가 아닌 입출력 장치에 접근하는 MMR(Memory Mapped Register)라고 한다면

- 이 레지스터는 단순히 값을 저장하는 역할이 아니라 입출력 장치를 제어하는 역할을 한다.

- 즉, 0~4번까지 각자의 맡은 바 역할이 있는 것으로 여겨진다면 절대로 최적화는 진행되어서는 안된다는 의미이다.

- 이러한 최적화를 막아주는 키워드가 바로 volatile이다.

- 입출력 장치에 액세스할때에는 무조건 volatile을 사용하여 레지스터에 접근해야 한다.


*(volatile unsigned int *)0x70A0 = 0x4001; // 0번
*(volatile unsigned int *)0x70A0 = 0x4002; // 1번
*(volatile unsigned int *)0x70A0 = 0x4003; // 2번
*(volatile unsigned int *)0x70A0 = 0x4004; // 3번
*(volatile unsigned int *)0x70A0 = 0x5555; // 4번


- memory mapped 장치의 대표적인 것은 메모리류(FLASH, RAM, SDRAM)와 LCD DRIVER IC, 이더넷 컨트롤러, USB 컨트롤러 등 MCU의 제어를 받는 다양한 종류의 컨트롤러 들이다.


2. I/O Mapped I/O

- 메모리와 I/O가 별개의 어드레스 영역에 할당되는 것을 의미한다. 따라서 I/O를 사용하더라도 메모리의 용량은 감소하지 않는다.


- CPU 입장에서는 메모리와 I/O를 구분해야 하므로 이들을 액세스하기 위해서 RD, WR 신호 외에 추가적으로 I/O에 접근하기 위한 신호가 필요.


- 소프트웨어적으로도 메모리에 대한 데이터의 액세스와 I/O에 대한 데이터의 입출력이 서로 다른 것으로 간주된다.


- 그래서 메모리에 대한 액세스는 Load / Store에 의해 수행되고, I/O 입출력은 Input이나 Output 명령에 의해 수행된다.


- 주로 Intel 계열의 프로세서에 사용된다.(x86)


- 가장 큰 장점은 Addressing 능력이 제한된 CPU를 사용할 때 발휘된다.


- I/O 접근을 메모리 접근과 분리하기 때문에 메모리용으로 주소영역 전체를 사용할 수 있다.


- 또한, 어셈블리어상에서 소스를 볼 때 입출력 수행 루틴을 알아보기 쉽다.


- ARM Processor는 임베디드 시스템상의 프로세서로, RISC구조이며, MMIO(Memory Mapped I/O) 방식을 취하고 있다.


- ARM 어셈블러나 c로 작성할 때, 실제로 메모리 주소를 포인터로 잡아서 값에 접근하는 모습을 볼 수 있다.


- 특히, 어셈블러상에서 in/out과 같은 I/O port 접근 명령어가 따로 존재하지 않고, 일반 메모리에 대한 접근인 id/st 명령어를 사용한다.



* 여기서 잠깐, RISC가 뭔데?

- Reduced Instruction Set Computer의 약자. CPU 명령어의 갯수를 줄여 하드웨어 CISC보다 구조를 조금 더 간단하게 만드는 방식.


// 종합적으로 MMIO는 오픈소스 하드웨어를 갖다 쓰되, 내부 레지스터를 직접 건드려서 쓰는 것을 의미하는 것 같음