原创 I/O 端口和 I/O 内存

2014-9-4 09:42 895 7 7 分类: MCU/ 嵌入式

 

1I/O 端口和 I/O 内存

每个外设都是通过读写其寄存器来控制的。通常一个设备有几个寄存器,它们位于内存地址空间或者I/O地址空间,并且地址是连续的。

在硬件层上,内存区和I/O区域没有概念上的区别它们都是通过在地址总线和控制总线上发出电信号来存取(即,读写信号),并且通过数据总线来读写数据。

在一些CPU制造商在其芯片上实现了一个单地址空间(统一编址)的同时,其它的CPU制造商认为外设不同于内存,应该有一个独立的地址空间给外设(单独编址),其生产处理器(特别是x86家族)I/O端口有自己的读写信号线和特殊的CPU指令来存取端口。因为外设要与外设总线相匹配,并且大部分流行的I/O总线都是以个人计算机(主要是x86家族)作为模型,所以即便那些没有单独地址空间给I/O端口的处理器,也必须在访问外设时模拟成读写端口。这通常通过外部芯片组(PC中的南北桥)或者在CPU核中附加额外电路来实现(基于嵌入式应用的处理器)。关于编址方式、I/O 端口和 I/O 内存更详细内容见http://blog.chinaunix.net/u3/96613/showart_1926286.html

由于同样的理由,Linux在所有计算机平台上都实现了I/O端口,甚至在那些单地址空间的CPU平台上(模拟I/O端口)。并不是所有的设备都会将其寄存器映射到I/O端口。虽然ISA设备普遍使用I/O端口,但大部分PCI设备将寄存器映射到某个内存地址区。这种I/O内存方法通常是首选的,因为它无需使用特殊的处理器指令,CPU存取内存也更有效率,并且编译器在存取内存时在寄存器分配和寻址模式的选择上有更多自由。

 

1.1I/O 寄存器和常规内存

I/O寄存器和RAM的主要不同是I/O操作有边际效应side effect而内存操作没有:访问内存只是在内存某一位置存储数值。因为内存存取速度严重影响CPU的性能,编译器可能会对源码进行优化,主要是:使用高速缓存和重排读/写指令的顺序。对于传统内存(至少在单处理器系统)这些优化是透明有益的,但是对于I/O 寄存器,这可能是致命错误,因为它们干扰了那些"边际效应"(驱动程序存取I/O 寄存器就是为了获取边际效应)。因此,驱动程序必须确保在存取寄存器时,不能使用高速缓存并且不能重新编排读写指令的顺序。

 

side effect 是指:访问I/O寄存器时,不仅仅会像访问普通内存一样影响存储单元的值,更重要的是它可能改变CPU的I/O端口电平、输出时序或CPU对I/O端口电平的反应等等,从而实现CPU的控制功能。CPU在电路中的意义就是实现其side effect 。举个例子,有些设备的中断状态寄存器只要一读取,便自动清零。

 

硬件缓冲的问题是最易解决的:只要将底层硬件配置(或者自动地或者通过Linux 初始化代码)为当存取I/O区时,禁止任何硬件缓冲(不管是I/O 内存还是I/O 端口)。

编译器优化和硬件重编排读写指令顺序的解决方法是:在硬件或处理器必须以一个特定顺序执行的操作之间安放一个内存屏障(memory barrierLinux 提供以下几个宏来实现这个功能:

#include <linux/kernel.h>
/* barrier(软件内存屏障)告知编译器插入一个内存屏障但是对硬件没有影响。编译后的代码将当前CPU寄存器所有修改过的值保存到内存,并且在需要时重新读取它们。barrier可阻止在屏障前后的编译器优化,但硬件能完成自己的重新排序 */
void barrier(void)

#include <asm/system.h>
/* rmb(read memory barrier)保证任何出现于屏障前的读在执行任何后续读之前完成 */
void rmb(void);
/* read_barrier_depends是一种特殊的、弱些的读屏障形式。rmb 阻止屏障前后的所有读指令的重新排序,read_barrier_depends 只阻止依赖于其他读指令返回的数据的读指令的重新排序。区别微小, 并且不是所有体系都支持。除非你确切地理解它们的差别, 并确信完整的读屏障会增加系统开销,否则应当始终使用 rmb。*/
void read_barrier_depends(void);
/* wmb(write memory barrier)保证任何出现于屏障前的写在执行任何后续的写之前完成 */
void wmb(void);
/* rmb(memory barrier)保证任何出现于屏障前的读写操作在执行任何后续的读写操作之前完成 */
void mb(void);
/* 以上这些宏都是是barrier的超集,它们插入硬件内存屏障在编译的指令流中,并且它们的实际实现是平台相关的 */


/* 以下这些屏障宏仅当SMP系统时插入硬件屏障;否则它们只是简单被替换成barrier宏 */
void smp_rmb(void);
void smp_read_barrier_depends(void);
void smp_wmb(void);
void smp_mb(void);

 

文章评论0条评论)

登录后参与讨论
我要评论
0
7
关闭 站长推荐上一条 /2 下一条