地址寄存器和数据寄存器成对出现,每一对寄存器对应一条I2C总线。当处理器需要读取SFP模块内的一个寄存器时,可以将寄存器地址设置到地址寄存器,再设置指令寄存器为读操作。如果操作正常,则操作完成后,FPGA会把读取的数据存放在对应的数据寄存器中;如果操作出现异常,则FPGA会设置中断状态寄存器的相关比特位反映异常情况;当处理器需要写SFP模块内的一个寄存器时,先把待写数据写人数据寄存器,并将寄存器地址写人地址寄存器,再设置指令寄存器为写操作。如果写操作异常,FPGA也会设置中断状态寄存器的相关比特位反映异常情况。
中断状态寄存器反映了每条I2C总线的工作状态以及模块本身的状态,例如:I2C总线工作是否正常、读写操作是否成功完成、模块被拔出或者插人等。中断屏蔽寄存器定义了各个状态变化是否要触发中断上报机制。
指令寄存器在不同比特位针对每条I2C总线设置了读、写、轮询和重启4种指令(读写和重启指令在前文中已有描述)。轮询指令是指:如果SFP模块存在且使能,则FPGA会不停地读取SFP模块中的寄存器(寄存器地址由地址寄存器指定),并将读回的数据存放在数据寄存器中,供处理器随时调用;同时可以设置中断,如果本次读的数值不同于上次读的数值,则产生中断并通知处理器。轮询功能为系统软件监测后板对外的以太网接口的链路状态提供了强大的支持。因为在系统运行过程中,以太网链路可能被断开或连接,链接状态随时可能变化。例如,速率从百兆变为十兆,或从千兆变为百兆;模式从全双工变为半双工。
许多SFP模块的链路状态可以从其中的一个物理层状态寄存器获得。假设处理器通过直接轮询这个寄存器来获得链路信息。如果I2C工作频率为100 kHz,则读取一个寄存器耗时约0.4 ms(8个SFP模块共耗费约3.2 ms) ;在一个主频为1 GHz的处理器中,轮询一次占用的CPU资源高达3 200 000个CPU时钟周期,处理器为了及时获得模块和链路状态,不得不频繁地(如每隔0.1 s)去轮询一次,明显降低了处理器的工作效率。然而,模块和链路状态不是一直在变化的,可能数小时或者几周有一次变化,但是处理器如果不能及时捕获这一次变化,就可能给系统运行造成致命的后果。例如链路断开了没有及时发现,仍然误认为连接,将导致实际业务的中断。而为了偶然出现的一次状态变化,而频繁地去轮询查看低速设备的状态,耗费大量的CPU资源是不值得的。
因此,在新的软件设计中,由FPGA接管处理器的轮询任务,以中断方式上报给处理器,使处理器只要在系统初始化时去获得SFP模块和链路状态,运行过程中几乎不需要在I2C总线上浪费任何CPU资源。
另外,对某些中断类型,例如模块的插拔、链路状态等,FPGA设计了防止“中断风暴”的功能。“中断风暴”是指短时间内有大量的中断产生,直接影响处理器的效率甚至业务。例如有时SFP模块或网线没有插紧或者对端设备的网线虚插,可能引发状态的频率变化,触发大量中断产生。
因为在系统软件中,中断的优先级大于任何线程或任务,当中断过多时,处理器就忙于处理中断而无暇顾及正常的数据处理和业务。而FPGA会根据“中断风暴寄存器”规定的时间间隔,统计相同类型的中断数量,上报第一个发生的中断和最后一个发生的中断,隐瞒中间过程产生的中断,从而提高了处理器效率。时间间隔的设置需要根据应用仔细权衡,一般设置为50 ms左右。此外,运行在处理器上的软件发现某类中断频繁上报时,还可以通过设置中断屏蔽寄存器禁止这类中断,并输出消息提醒用户。
图4是针对一个SFP模块的FPGA的软件流程。首先,FPGA读人模块的存在寄存器和使能寄存器,然后判断模块是否存在和使能,如果模块不存在或者不使能则跳过;否则,根据指令寄存器中的指令进行相关操作。如果没有设置指令,则跳过;如果是写指令,则根据地址寄存器和数据寄存器的值进行写操作;如果是读指令,则根据地址寄存器中的数值进行读操作,并将读取的数值存放在数据寄存器中;如果设置了轮询,则不断地读取设定的寄存器,更新数据寄存器中的数值。每种操作完成后都会调用中断子程序,例如读取存在寄存器后,FPGA会把当前数值和前一次的值进行比较,如果不同,则根据中断屏蔽寄存器的设置触发中断;轮询操作时数据寄存器的数值发生更新也可触发中断。