CPU访问外部设备

整个计算机由CPU、内存和外部设备组成。当CPU要对内存中的内容进行读写操作时,地址总线负责传输地址,控制总线指明是读取操作还是写入操作,数据总线则用来传输写入内存或从内存读出的信息。而CPU与外部设备的交互,则是通过CPU读写外部设备上的寄存器实现的,外设寄存器也称为“I/O端口”。
我们知道,内存中的每个存储单元都有一个地址与其对应,CPU根据这个地址就可以指定自己想要访问的存储单元。那外部设备呢?外部设备中的寄存器也需要有一个地址与之相对应,才能让CPU根据寄存器的地址来区分不同设备之间的寄存器和同一个设备中的不同寄存器。既然,外部设备中的寄存器也需要有一个地址,那么现在的问题是外部设备中的寄存器地址与内存地址的关系是什么呢。
1、独立编址(ISOLATED I/O): 也称为“I/O端口”方式,外部设备中的寄存器(I/O端口)地址与内存地址是相互独立、互不影响的,它们分别位于不同的地址空间内。即I/O端口地址位于I/O地址空间内,而内存地址则位于内存地址空间内。不同的地址空间,这个如何理解呢。举个例子,I/O地址空间内存在0xff这样的地址,内存地址空间也可以存在0xff这样的地址,它们两个互不影响。那当地址总线上出现0xff这个地址时,CPU到底是想访问内存还是想访问I/O端口呢,这个是由I/O读控制线、I/O写控制线、内存读控制线、内存写控制线来决定的。如果CPU访问的是I/O端口,那么I/O读控制线或者I/O写控制线被激活;如果CPU访问的是内存,那么内存读控制线或者内存写控制线被激活。所以,采用独立编址方式,对于某个地址(例如0xff)而言,它既可以存在于I/O地址空间内,也可以位于内存地址空间内。在采用独立编址的体系架构下,CPU通过MOV指令来访问内存,而对I/O端口的访问则是通过IN和OUT指令来完成的。IN指令和OUT指令的具体例子如下:

IN AL, 21H  // 从21H端口读取一字节数据到AL寄存器
OUT 21H, AL // 将寄存器AL的值写入21H端口

/*
汇编语言中的数字:
二进制 11111111B 00001111B // 数字后加B
十进制 255 16
十六进制 0FFH 0FH // 数字后加HS
另外,当十六进制数的第一个字符是字母时,必须在第一个字符之前添加一个0
*/

32位处理器的内存地址空间大小是4G,而独立编址的I/O地址空间的大小为64K,即它由65536个8bit的I/O端口组成。I/O端口的编号从 0x0000到0xFFFF,有16位,80x86架构用低16位地址线A0-A15来寻址。连续两个8bit的端口可以组成一个16bit的端口,连续4个组成一个 32bit的端口。采用独立编址的典型例子是intel 80x86。

2、统一编址(Memory-Mapped I/O):也称为“I/O内存”方式。外部设备中的寄存器和内存中的存储单元被同等看待,每个I/O端口占用一个内存存储单元的地址,将内存地址的一部分划出来用作IO地址空间。I/O端口占用了内存的地址空间,使内存的存储量容量减小。对于一个32位系统来说,它的4G的地址空间被内存和外部设备中的寄存器共同瓜分。采用统一编址方式,CPU访问外部设备IO端口时,不再需要额外的IN和OUT指令,CPU只需要使用同一套指令就可以访问内存或者IO端口。arm,powerpc在这一类的嵌入式处理器中采用统一编址方式。