-
Port-Mapped IO공학/컴퓨터구조 2023. 8. 11. 04:08
글의 참고
- https://stackoverflow.com/questions/3215878/what-are-in-out-instructions-in-x86-used-for
- https://wiki.osdev.org/Port_IO
- https://en.wikipedia.org/wiki/Input/output_base_address
- https://en.wikipedia.org/wiki/Memory-mapped_I/O_and_port-mapped_I/O
- https://en.wikipedia.org/wiki/I/O_Controller_Hub
- https://en.wikipedia.org/wiki/Programmed_input%E2%80%93output
글의 전제
- 내가 글을 쓰다가 궁금한 점은 파란색 볼드체로 표현했다. 나도 모르기 때문에 나중에 알아봐야 할 내용이라는 뜻이다.
- 밑줄로 작성된 글은 좀 더 긴 설명이 필요해서 친 것이다. 그러므로, 밑 줄 처친 글이 이해가 안간다면 링크를 따라서 관련 내용을 공부하자.
글의 내용
: PIO는 2가지 의미로 해석될 수 있다. `Programmed Input/Output(Programmable Input/Output)`와 `Port-Mapped I/O`가 있다. 먼저 `Programmed Input/Output(Programmable Input/Output)`에 대해 알아보자. PIO는 CPU가 I/O 장치들과 데이터를 주고 받는 방법을 의미한다. 여기서 I/O 장치란 프로세서 외부에 존재하는 모든 장치를 의미한다(램도 외부이지만, 예외로 놓는다). 이 PIO에는 2가지 방법이 존재한다. 바로 MMIO와 PMIO가 있다. 이 2가지 방법 모두 CPU가 직접 명령어를 실행한다. 즉, 데이터 전송 작업에 CPU가 직접적으로 참여한다는 소리다. MMIO 같은 경우, CPU는 메모리를 통해서 외부 장치와 데이터 통신을 하게 된다(`x86` 같은 경우 mov 명령어`). PMIO는 별도의 I/O 영역을 통해서 CPU가 외부 디바이스와 통신하는 것이다(`x86` 같은 경우, in & out 명령어). 중요한 포인트는 PIO는 CPU가 외부 디바이스와 데이터를 주고 받기 위해 직접 명령어를 수행한다는 것을 꼭 기억하자.
: 그런데, 외부 장치는 속도가 상당히 느리다. 즉, CPU가 MMIO를 하든, PMIO를 하든 외부 장치의 응답 속도가 상대적으로 너무 느리다 보니, CPU 파이프라인에서 많은 양의 `버블`이 발생할 우려가 있다. 그래서 등장한 것이 `DMA`다. DMA는 RAM에 직접 액세스할 수 있기 때문에, CPU가 DMA에게 요청을 하면 DMA가 외부 디바이스와 데이터 통신을 주고 받은 뒤, 해당 결과물을 특정 영역에 쓴다. 그리고, 인터럽트를 발생시켜 CPU가 RAM에서 결과물을 읽어가도록 하게 한다.
: PMIO는 PIO의 종류 중 하나로, `Port-Mapped I/O`라고도 불리고, `Port I/O`라고도 불린다. x86 아키텍처는 주소 공간을 2가지로 분류한다.
1" 메모리
2" 포트: 옛날부터 메모리는 데이터를 읽고/쓰는 저장소의 역할을 맡고 있었으며, 포트는 외부 하드웨어 디바이스들과 통신하는 데 주로 사용을 했다. 그런데, 왜 CPU는 메모리(RAM, I/O 메모리 포함)를 통해서 I/O 디바이스와 통신할까? CPU와 I/O 디바이스가 직접 통신하면 안되나? I/O 디바이스와 직접 통신할 경우, 외부 디바이스가 추가될 때마다 CPU에 추가 핀이 필요하다.
: 주변 장치와의 통신 속도는 메모리와의 접근 속도보다 훨씬 느리다. 그래서, I/O 디바이스의 속도를 맞추다 보면 병목 현상이 발생할 여지가 크다. 그래서 포트-맵 I/O 통신은 MMIO보다 느리다. x86을 제외한 다른 많은 아키텍처들에서는 주소 공간을 메모리 하나로 통합하여, 메모리 영역에서 일부는 데이터 영역으로 나머지 일부는 외부 디바이스를 설정할 수 있는 영역(MMIO)으로 할당하고 있다. AMD는 x86_64 만들 때, 64비트에서는 포트-맵 I/O를 지원하지 않도록 설계했다.
: x86에서는 포트-맵 I/O 를 사용하기 위해 별도의 명령어를 제공한다.
1" out
2" in: 위의 명령어는 여러 버전들이 존재한다. 예를 들어, `outb`, `outw`, `outl` 는 각각 1B, 2B, 4B를 의미한다. 그리고 이 명령어는 EAX와 레지스터 혹 EAX의 하위 종속 레지스터들인 AX, AH|AL 등을 사용한다. 아래 그림에서 볼 수 있다시피, I/O 어드레스는 CPU가 주소 지정 가능한 모드에 따라서 조금씩 달라졌다. IBM PC/[XT, AT] 모두 ISA 버스를 사용한다. 메모리와 I/O 주소 영역의 버스를 별개로 둔 이유는 `병목 현상`이 발생할 수 있기 때문이다. I/O가 속도가 상대적으로 느리다 보니, 메모리와 분리해서 처리해야 시스템 전체적인 속도를 높일 수 있다.
: x86 칩셋의 구조는 아래와 같다. I/O 포트는 `MCH`에 의해서 컨트롤되는 것이 아닌, `ICH`에 의해 컨트롤된다. 즉, CPU가 I/O 영역에 쓰기/읽기를 요청하면 MCH는 해당 요청을 ICH로 단지 포워딩한다. 결국, I/O 포트 관련 작업은 ICH에서 한다는 것을 알아야 한다. 그리고 아래 그림에서 알 수 있다시피, MCH가 컨트롤하는 영역에는 I/O 메모리가 보이지 않는다. 즉, 우리가 아는 RAM과 I/O 어드레스는 완전히 별개로 존재하는 영역이라는 것도 알아두자.
- I/O 베이스 어드레스
: IBM PC 호환 기종에서는 MMIO가 아닌, 별도의 주소 공간을 할당해서 I/O 디바이스와 통신을 했다. 이 공간을 I/O 주소 공간이라 부르며, 각 I/O 디바이스들은 자신만의 고유한 I/O 주소를 할당받아야 CPU와 통신이 가능했다. 각 I/O 디바이스가 할당받은 주소 공간에서 제일 첫 번째 주소를 베이스 어드레스라고 한다. I/O 어드레스를 표현할 때는, `I/O 베이스 어드레스 + 오프셋`을 통해 I/O 디바이스의 특정 레지스터에 접근한다. 참고로 OS 개발 시에, IBM PC 호환 기종이라고 하면 대개 x86 아키텍처를 생각하면 된다.
: 각 디바이스들에게 할당되는 I/O 어드레스는 다음과 같다. 대개는 고정이지만, 점퍼 및 DIP 스위치를 통해서 값을 바꿀 수 도 있다. 그리고 ISA 버스에서 `0x0000 ~ 0x03FF`까지는 예약되어 있다. 즉, ISA 버스를 사용하고 있다면 앞에 언급한 영역은 고정이므로 바꿀 수 가 없다. 그 이후인 `0x0400 ~ 0xFFFF`까지는 원하는대로 사용할 수 있다.
I/O 어드레스 범위 디바이스 00 – 1F First DMA controller 8237 A-5 20 – 3F First Programmable interrupt controller, 8259A, 40 – 5F Programmable interval timer (System Timer), 8254 60 – 6F Keyboard, 8042 70 – 7F Real-time clock, NMI mask 80 – 9F DMA Page Register, 74LS612 87 DMA Channel 0 83 DMA Channel 1 81 DMA Channel 2 82 DMA Channel 3 8B DMA Channel 5 89 DMA Channel 6 8A DMA Channel 7 8F Refresh A0 – BF Second Programmable interrupt controller, 8259A, Slave C0 – DF Second DMA controller 8237 A-5 F0 Clear 80287 Busy F1 Reset 80287 F8 – FF Math coprocessor, 80287 F0 – F5 PCjr Disk Controller F8 – FF Reserved for future microprocessor extensions 100 – 10F POS Programmable Option Select (PS/2) 110 – 1EF System I/O channel 140 – 15F Secondary SCSI host adapter 170 – 177 Secondary Parallel ATA Disk Controller 1F0 – 1F7 Primary Parallel ATA Hard Disk Controller 200 – 20F Game port 210 – 217 Expansion Unit 220 – 233 Sound Blaster and most other sound cards 278 – 27F Parallel port 3 280 – 29F LCD on Wyse 2108 PC SMC Elite default factory setting 2B0 – 2DF Alternate Enhanced Graphics Adapter (EGA) display control 2E8 – 2EF Serial port 4 2E1 GPIB/IEEE-488 Adapter 0 2E2 – 2E3 Data acquisition 2F8 – 2FF Serial port 2 300 – 31F Prototype Card 300 – 31F Novell NE1000 compatible Ethernet network interfaces 300 – 31F AMD Am7990 Ethernet network interface, IRQ=5. 320 – 323 ST-506 and compatible hard disk drive interface 330 – 331 MPU-401 MIDI processing unit on most sound cards 340 – 35F Primary SCSI host adapter 370 – 377 Secondary floppy disk drive controller 378 – 37F Parallel port 2 380 – 38C Secondary Binary Synchronous Data Link Control (SDLC) adapter 388 – 389 AdLib Music Synthesizer Card 3A0 – 3A9 Primary Binary Synchronous Data Link Control (SDLC) adapter 3B0 – 3BB Monochrome Display Adapter (MDA) display control 3BC – 3BF Parallel port 1 on MDA card 3C0 – 3CF Enhanced Graphics Adapter (EGA) display control 3D0 – 3DF Color Graphics Adapter (CGA) 3E8 – 3EF Serial port 3 3F0 – 3F7 Primary floppy disk drive controller. Primary IDE controller (second I/O range) (3F6–3F7h) 3F8 – 3FF Serial port 1 CF8 – CFC PCI configuration space - I/O 함수 구현
: 기본적으로 2가지 함수를 구현해야 한다.
" outX
" inX
: 하드웨어 초기화를 위해서는 대개 딜레이 함수가 필요하다. 그러나, 아직 준비가 되어있지 않으니, 임시 방편으로 `I/O 포트`를 이용해서 딜레이 함수를 구현하도록 한다.
" io_wait
= 모든 I/O 포트에 액세스하는 데는 최소 적어도 1 마이크로초가 소요된다.
- 현재 상황
: 32비트 및 64비트 프로세서가 도래하면서, I/O 디바이스와 통신하기 위해 MMIO를 사용하는 것이 더 이상 문제가 되지 않게되었다. 왜냐면, 32비트 프로세서만 보더라도, 4GB까지 주소 지정(`Memory address space`)이 가능했다. 16비트 초창기 시절에는 램도 16비트가 나올 수 있었기 때문에, 16비트 CPU가 1MB 램을 주시 지정 하게 되면, 더 이상 CPU가 주소 지정 가능한 영역이 나오지 않았다. 그래서 I/O 주소를 별도로 분리해서 접근했던 것이었다. 그런데, 32비트 CPU가 도래하면서 4GB까지 주소 지정이 가능했고, 32비트 초기시절에는 2GB 램이면 충분한 경우가 많았다. 나머지 2GB 남게되면서 별도의 I/O 주소를 둘 필요가 없어졌다. 예를 들어, 16비트에서는 1MB 램을 사용하면, I/O 주소는 주소 지정이 불가능하므로, 램에 접근할 때와 I/O 영역에 접근할 때, 다른 명령어를 사용해서 접근해야 했다. 그러나, 32비트가 되면 4GB 주소 지정이 가능하므로, CPU 주소 지정 범위안에 I/O 영역까지 할당하면, 별도의 명령어가 필요없게 되는 것이다. 80286에서는 총 68개의 핀이 존재했으며, 그 중에서 `M/IO`에 대한 내용은 다음과 같다.
https://pdf1.alldatasheet.co.kr/datasheet-pdf/download/1284116/AMD/80286.html
https://pdf1.alldatasheet.co.kr/datasheet-pdf/download/1284116/AMD/80286.html: `x86` 에서 `mov` 명령어를 사용하면, `M/IO`라인에 1을, `out/in` 명령어를 사용하면, `M/IO` 라인에 0을 쓰는 것이다.
: `Port I/O`를 만든 `x86` 기준에서도 `Port-based I/O` 보다는 `Memory-mapped I/O`를 더 선호한다. 왜냐면, port I/O는 데이터 전송에 사용할 수 있는 레지스터를 하나로 제한한다(EAX, AX, AL). 그러나, MMIO는 모든 범용 레지스터를 사용할 수 있고, 뿐만 아니라, 더 적은 명령어를 사용해서 퍼포먼스 또한 더 좋다. 참고로, 당연한 얘기겠지만, 메모리에 접근하는 것이 속도도 더 빠르기 때문인 것도 있다. 그래서, AMD는 `x86_64`를 내놓으면서, 64비트 관련 port I/O를 더 이상 지원하지 않겠다고 밝혔다.
'공학 > 컴퓨터구조' 카테고리의 다른 글
[컴퓨터 구조] Cache (0) 2023.08.12 [컴퓨터 구조] MMIO (0) 2023.08.11 [컴퓨터 구조] AHCI (0) 2023.08.10 [컴퓨터 구조] ISA 버스 (0) 2023.08.09 [컴퓨터 구조] PCI (1) 2023.08.09