pci总线使用inta#、intb#、intc#和intd#信号向处理器发出中断请求。这些中断请求信号为低电平有效,并与处理器的中断控制器连接。在pci体系结构中,这些中断信号属于边带信号(sideband signals),pci总线规范并没有明确规定在一个处理器系统中如何使用这些信号,因为这些信号对于pci总线是可选信号。pci设备还可以使用msi机制向处理器提交中断请求,而不使用这组中断信号。有关msi机制的详细说明见第8章。
1.1 中断信号与中断控制器的连接关系
不同的处理器使用的中断控制器不同,如x86处理器使用apic(advanced programmable interrupt controller)中断控制器,而powerpc处理器使用mpic(multiprocessor interrupt controller)中断控制器。这些中断控制器都提供了一些外部中断请求引脚irq_pinx#。外部设备,包括pci设备可以使用这些引脚向处理器提交中断请求。
但是pci总线规范没有规定pci设备的intx信号如何与中断控制器的irq_pinx#信号相连,这为系统软件的设计带来了一定的困难,为此系统软件使用中断路由表存放pci设备的intx信号与中断控制器的连接关系。在x86处理器系统中,bios可以提供这个中断路由表,而在powerpc处理器中firmware也可以提供这个中断路由表。
在一些简单的嵌入式处理器系统中,firmware并没有提供中断路由表,此时系统软件开发者需要事先了解pci设备的intx信号与中断控制器的连接关系。此时外部设备与中断控制器的连接关系由硬件设计人员指定。
我们假设在一个处理器系统中,共有3个pci插槽(分别为pci插槽a、b和c),这些pci插槽与中断控制器的irq_pinx引脚(分别为irqw#、irqx#、irqy#和irqz#)可以按照图15所示的拓扑结构进行连接。
采用图15所示的拓扑结构时,pci插槽a、b、c的inta#、intb#和intc#信号将分散连接到中断控制器的irqw#、irqx#和irqy#信号,而所有intd#信号将共享一个irqz#信号。采用这种连接方式时,整个处理器系统使用的中断请求信号,其负载较为均衡。而且这种连接方式保证了每一个插槽的inta#信号都与一根独立的irqx#信号对应,从而提高了pci插槽中断请求的效率。
在一个处理器系统中,多数pci设备仅使用inta#信号,很少使用intb#和intc#信号,而intd#信号更是极少使用。在pci总线中,pci设备配置空间的interrupt pin寄存器记录该设备究竟使用哪个intx信号,该寄存器的详细介绍见第2.3.2节。
1.2 中断信号与pci总线的连接关系
在pci总线中,intx信号属于边带信号。所谓边带信号是指这些信号在pci总线中是可选信号,而且只能在一个处理器系统的内部使用,并不能离开这个处理器环境。pci桥也不会处理这些边带信号。这给pci设备将中断请求发向处理器带来了一些困难,特别是给挂接在pci桥之下的pci设备进行中断请求带来了一些麻烦。
在一些嵌入式处理器系统中,这个问题较易解决。因为嵌入式处理器系统很清楚在当前系统中存在多少个pci设备,这些pci设备使用了哪些中断资源。在多数嵌入式处理器系统中,pci设备的数量小于中断控制器提供的外部中断请求引脚数,而且在嵌入式系统中,多数pci设备仅使用inta#信号提交中断请求。
在这类处理器系统中,可能并不含有pci桥,因而pci设备的中断请求信号与中断控制器的连接关系较易确定。而在这类处理器系统中,即便存在pci桥,来自pci桥之下的pci设备的中断请求也较易处理。
在多数情况下,嵌入式处理器系统使用的pci设备仅使用inta#信号进行中断请求,所以只要将这些inta#信号挂接到中断控制器的独立irq_pin#引脚上即可。这样每一个pci设备都可以独占一个单独的中断引脚。
而在x86处理器系统中,这个问题需要bios参与来解决。在x86处理器系统中,有许多pci插槽,处理器系统并不知道在这些插槽上将要挂接哪些pci设备,而且也并不知道这些pci设备到底需不需要使用所有的intx#信号线。因此x86处理器系统必须要对各种情况进行处理。
x86处理器系统还经常使用pci桥进行pci总线扩展,扩展出来的pci总线还可能挂接一些pci插槽,这些插槽上intx#信号仍然需要处理。pci桥规范并没有要求桥片传递其下pci设备的中断请求。事实上多数pci桥也没有为下游pci总线提供中断引脚intx#,管理其下游总线的pci设备。但是pci桥规范推荐使用表13建立下游pci设备的intx信号与上游pci总线intx信号之间的映射关系。
表13 pci设备intx#信号与pci总线intx#信号的映射关系
设备号 pci设备的intx#信号 pci总线的intx#信号
0, 4, 8, 12, 16, 20, 24, 28 inta# inta#
intb# intb#
intc# intc#
intd# intd#
1, 5, 9, 13, 17, 21, 25, 29 inta# intb#
intb# intc#
intc# intd#
intd# inta#
2, 6, 10, 14, 18, 22, 26, 30 inta# intc#
intb# intd#
intc# inta#
intd# intb#
3, 7, 11, 15, 19, 23, 27, 31 inta# intd#
intb# inta#
intc# intb#
intd# intc#
我们举例说明该表的含义。在pci桥下游总线上的pci设备,如果其设备号为0,那么这个设备的inta#引脚将和pci总线的inta#引脚相连;如果其设备号为1,其inta#引脚将和pci总线的intb#引脚相连;如果其设备号为2,其inta#引脚将和pci总线的intc#引脚相连;如果其设备号为3,其inta#引脚将和pci总线的intd#引脚相连。
在x86处理器系统中,由bios或者apci表记录pci总线的inta~d#信号与中断控制器之间的映射关系,保存这个映射关系的数据结构也被称为中断路由表。大多数bios使用表13中的映射关系,这也是绝大多数bios支持的方式。如果在一个x86处理器系统中,pci桥下游总线的pci设备使用的中断映射关系与此不同,那么系统软件程序员需要改动bios中的中断路由表。
bios初始化代码根据中断路由表中的信息,可以将pci设备使用的中断向量号写入到该pci设备配置空间的interrupt line register寄存器中,该寄存器将在第2.3.2节中介绍。
1.3 中断请求的同步
在pci总线中,intx信号是一个异步信号。所谓异步是指intx信号的传递并不与pci总线的数据传送同步,即intx信号的传递与pci设备使用的clk#信号无关。这个“异步”信号给系统软件的设计带来了一定的麻烦。
系统软件程序员需要注意“异步”这种事件,因为几乎所有“异步”事件都会带来系统的“同步”问题。以图11为例,当pci设备11使用dma写方式,将一组数据写入存储器时,该设备在最后一个数据离开pci设备11的发送fifo时,会认为dma写操作已经完成。此时这个设备将通过intx信号,通知处理器dma写操作完成。
此时处理器(驱动程序的中断服务例程)需要注意,因为intx信号是一个异步信号,当处理器收到intx信号时,并不意味着pci设备11已经将数据写入存储器中,因为pci设备11的数据传递需要通过pci桥1和host主桥,最终才能到达存储器控制器。
而intx信号是“异步”发送给处理器的,pci总线并不知道这个“异步”事件何时被处理。很有可能处理器已经接收到intx信号,开始执行中断处理程序时,该pci设备还没有完全将数据写入存储器。
因为“pci设备向处理器提交中断请求”与“将数据写入存储器”分别使用了两个不同的路径,处理器系统无法保证哪个信息率先到达。从而在处理器系统中存在“中断同步”的问题,pci总线提供了以下两种方法解决这个同步问题。
(1) pci设备保证在数据到达目的地之后,再提交中断请求。
显然这种方法不仅加大了硬件的开销,而且也不容易实现。如果pci设备采用posted写总线事务,pci设备无法单纯通过硬件逻辑判断数据什么时候写入到存储器。此时为了保证数据到达目的地后,pci设备才能提交中断请求,pci设备需要使用“读刷新”的方法保证数据可以到达目的地,其方法如下。
pci设备在提交中断请求之前,向dma写的数据区域发出一个读请求,这个读请求总线事务将被pci设备转换为读完成总线事务,当pci设备收到这个读完成总线事务后,再向处理器提交中断请求。pci总线的“序”机制保证这个存储器读请求,会将dma数据最终写入存储器,有关pci序的详细说明见第9.3节。
pci总线规范要求host主桥和pci桥必须保证这种读操作可以刷新写操作。但问题是,没有多少芯片设计者愿意提供这种机制,因为这将极大地增加他们的设计难度。除此之外,使用这种方法也将增加中断请求的延时。
(2) 中断服务例程使用“读刷新”方法。
中断服务例程在使用“pci设备写入存储器”的这些数据之前,需要对这个pci设备进行读操作。这个读操作也可以强制将数据最终写入存储器,实际上是将数据写到存储器控制器中。这种方法利用了pci总线的传送序规则,这种方法与第1种方法基本相同,只是使用这种方法使用软件方式,而第1种方式使用硬件方式。第9.3节将详细介绍这个读操作如何将数据刷新到存储器中。
第2种方法也是绝大多数处理器系统采用的方法。程序员在书写中断服务例程时,往往都是先读取pci设备的中断状态寄存器,判断中断产生原因之后,才对pci设备写入的数据进行操作。这个读取中断状态寄存器的过程,一方面可以获得设备的中断状态,另一方面是保证dma写的数据最终到达存储器。如果驱动程序不这样做,就可能产生数据完整性问题。产生这种数据完整性问题的原因是intx这个异步信号。
这里也再次提醒系统程序员注意pci总线的“异步”中断所带来的数据完整性问题。在一个操作系统中,即便中断处理程序没有首先读取pci设备的寄存器,也多半不会出现问题,因为在操作系统中,一个pci设备从提交中断到处理器开始执行设备的中断服务例程,所需要的时间较长,处理器系统基本上可以保证此时数据已经写入存储器。
但是如果系统程序员不这样做,这个驱动程序依然有bug存在,尽管这个bug因为各种机缘巧合,始终不能够暴露出来,而一旦这些bug被暴露出来将难以定位。为此系统程序员务必要重视设计中出现的每一个实现细节,当然仅凭谨慎小心是远远不够的,因为重视细节的前提是充分理解这些细节。
pci总线v2.2规范还定义了一种新的中断机制,即msi中断机制。msi中断机制采用存储器写总线事务向处理器系统提交中断请求,其实现机制是向host处理器指定的一个存储器地址写指定的数据。这个存储器地址一般是中断控制器规定的某段存储器地址范围,而且数据也是事先安排好的数据,通常含有中断向量号。
host主桥会将msi这个特殊的存储器写总线事务进一步翻译为中断请求,提交给处理器。目前pcie和pci-x设备必须支持msi中断机制,但是pci设备并不一定都支持msi中断机制。
目前msi中断机制虽然在pcie总线上已经成为主流,但是在pci设备中并不常用。即便是支持msi中断机制的pci设备,在设备驱动程序的实现中也很少使用这种机制。首先pci设备具有intx#信号可以传递中断,而且这种中断传送方式在pci总线中根深蒂固。其次pci总线是一个共享总线,传递msi中断需要占用pci总线的带宽,需要进行总线仲裁等一系列过程,远没有使用intx#信号线直接。
但是使用msi中断机制可以取消pci总线这个intx#边带信号,可以解决使用intx中断机制所带来的数据完整性问题。而更为重要的是,pci设备使用msi中断机制,向处理器系统提交中断请求的同时,还可以通知处理器系统产生该中断的原因,即通过不同中断向量号表示中断请求的来源。当处理器系统执行中断服务例程时,不需要读取pci设备的中断状态寄存器,获得中断请求的来源,从而在一定程度上提高了中断处理的效率。本书将在第8章详细介绍msi中断机制。
三星Galaxy S8真机上手:这屏幕看呆我了!
首届百度Apollo生态大会召开 长沙已准备好接受自动驾驶行业的检阅了
【瞭望者】如何使用黑客技术反制无人机
通过机器学习将光学传感器灵敏度提高到单分子水平
智能垃圾回收设备需要什么样的连接器呢?高可靠高防水才是关键
PCI总线如何与中断控制器的信号相连?
iPhone8什么时候上市最新消息:iPhone8如期发布,再爆智能黑科技,双2.5D玻璃+超大屏幕+无线充电
世界上最小的自主飞行竞速无人机是怎样的
儿童机器人有多火?为何会火?
智慧城市的物联网技术将会怎样发展
锐龙7 3700X对比i9-9900K 买哪个最好
74ls76引脚图及功能表
变频器调整几个主要参数
使用FPGA实现高效并行实时上采样
动能定理公式的理解与推导
PCM将成为2010年闪存市场的技术亮点
科学家利用模拟超材料来实现弯曲微波和禁止频率
高通宣布推出首款基于5G新空中介面的新款小型基地台产品
"元宇宙”龙头股中青宝二收关注函
小米8屏幕指纹版将升级小米9同款GameTurbo技术