Local Bus总线又称为CPU总线,根据高低位地址线序的差异,又可分为Motorola CPU总线和Intel CPU总线。古老的CS51单片机就是Intel CPU总线的典型代表,而我们常用的Power PC就是Motorola CPU总线架构,它是从60X总线衍变过来的(60X总线支持64、32、16、8四种可选位宽模式)。本文以一款Power PC CPU通过Local Bus读写FPGA内部寄存器或RAM以及中断响应为例,为大家介绍开发过程中的注意事项。

FPGA Local Bus

记得在HINOC1.0的时候,FPGA样机上采用Intel XScale PXA270(ARM CPU)芯片与FPGA芯片互连,采用的接口时序就是Local Bus总线的时序,该时序与CPU与外挂异步SRAM接口时序一样,这样HINOC芯片就可以看做是ARM CPU的一个外设,可以分配可配置的寄存器或RAM等访问空间。具体时序图如下图所示,总线包括地址、片选、读/写信号,下图是简单的读时序。注意,所谓异步,就是CPU接口总线与FPGA相应接口是不同步的,也就是CPU送给FPGA的信号中不包括时钟信号,FPGA需要通过自己内部的时钟信号去采CPU接口的地址、数据等信号,同步后才能使用。如下图中的CPU读FPGA时序,CPU给出度地址和读控制信号后,等待tAA时间后才能从数据总线上取到正确的有效的数据,这个tAA时间长短是可以通过设置PXA270中的寄存器来进行调整的。

FPGA Local Bus

FPGA Local Bus

印象比较深刻的是,上面时序图在FPGA侧观察时,就能感受到CPU的效率是如何之慢了,CPU完成一次读写FPGA内部寄存器的操作,对应的FPGA常常已经过了几十个时钟周期,所以效率较低,好在一些改进的SoC总线如AXI等还有Burst等类似于DMA之类的连续读写操作模式。

FPGA Local Bus

在搭建仿真环境时,上图中的CPU行为就可以简化为简单的读、写两个task(如下图)。在FPGA侧的电路设计代码,则把CPU送过来的信号采用打两拍等方式同步后进行地址译码等再交给后续的模块使用。

FPGA Local Bus

目前,这种总线使用的已经比较少了。但在很多专用领域,如航空航天等领域,一些国产的CPU等仍然使用类似的总线时序。简单、可靠,稳定。

调试环境介绍

硬件开发板:AX7103;CPU开发板:p2020;驱动操作系统:VxWorks。

VxWorks操作系统下CPU和FPGA的硬件连接关系如下图1所示,采用Local Bus总线接口。

FPGA Local Bus

图1 CPU和FPGA的硬件连接

硬件侧没有开发板与p2020 Local Bus的50pins直接相对接,而驱动侧需要开发、测试Local Bus驱动,因此需要创造调试环境,利用AX7103开发板的68个引脚,将Local Bus总线相关的关键引脚(37pins=16pins_addr+16pins_data+3pins_csn+1pin_oen+1pin_wen)以及中断信号定义到EX_IO1和EX_IO2上,再根据p2020原理图与接插件J5、J4相匹配,调试环境如下图2所示(略丑,只做原型功能验证)。图示J5为p2020接插件(Local Bus总线相关信号);J4为p2020接插件(硬线中断、GND信号);EX_IO1为Local Bus总线地址、片选和读写使能管脚;EX_IO2为数据、中断管脚。

注意:两块开发板的电平标准要一致,否则不能通过杜邦线直连,另外建议两块开发板杜邦线共地相连。

FPGA Local Bus

图2 硬件板和CPU板实际调试环境

概述

前面介绍过FPGA与CPU进行数据交互时使用的接口是PCIe接口等接口,在TSN或TTE系统里,PCIe接口常常用在端节点上,Local Bus接口常用于交换机内CPU与FPGA进行数据交互的接口。本文只讲述Local Bus调试历程。对于硬件侧来说,Local Bus相比于PCI、PCIe开发简单些,只需要完成CPU内存地址与硬件寄存器、RAM地址的映射,读/写使能信号和片选信号的时序,此次Local Bus开发是硬件侧建立一个Local Bus测试工程配合Local Bus驱动调试寄存器读写功能以及中断功能。

2.1 数据读写操作

关于寄存器读写功能。Local Bus的简介网上有参考价值的文章很多,可自行搜索,因为局部总线简单,也没有什么可介绍的,理论部分就请在网上搜索,我这里就附上关键信号的读写时序图,如下图3,图4所示。同样的Local Bus接口在不同的CPU处理器地址和数据位宽不一致,信号也会有一些不一致,比如:BM3803处理器中地址数据线未复用,数据位宽32bit(双字操作);p2020处理器中数据线LAD复用(通过LALE信号锁存高11bit的地址,如果只用16bit的地址时可以不接LALE),数据位宽16bit(双字节操作)。

FPGA Local Bus

FPGA Local Bus

FPGA Local Bus

2.2 中断功能

中断的话采用传统的硬线中断,p2020原理图可知,除了irq3已经被占用,另外6个硬线中断(irq0/1/2/4/5/6)可供外设使用,且均在J4接插件位置处。由p2020的Datasheet可以知道p2020内部中断有64类,外部中断有12类,而我们调试使用的是外部中断中的irq1。

调试历程

3.1 数据读写操作

调试伊始,通过p2020的原理图可以看出CS0和CS1分别给了内部的nand_flash以及nor_flash使用,另外输出3位的片选信号(CS2,CS3和CS4),说明最多可以挂8片局部总线外设,,这些外设(含*_flash)共用数据线、地址线以及读写使能。第一步就得先知道驱动使用了哪个片选信号,驱动也是摸索,驱动代码写着只使用了CS4,屏蔽了原来例程中的CS2,通过硬件侧抓cpu_csn[2:0]发现,cpu_csn[0]也会存在低片选状态,后经驱动修改,先保留使用CS2,这样驱动每次读写操作时都能触发“cpu_csn[2:0]==3’b110”。CPU板将对地址区间0xf1000_0000~0xf1000_ffff(128KB)进行读写操作时会拉低CS2,映射到硬件侧就是对寄存器/RAM的读写了。

测试写功能,在CPU启动后,驱动会給内存地址0xf100_0118处写16‘h5555,如下图5所示,先给出写地址,然后拉低片选信号,几乎同时拉低了写使能信号,这样数据就从CPU写入到FPGA了。

FPGA Local Bus

图5 板级写操作

与写操作的简单相比,问题都出在了读操作,我们也同样地将AX7103和p2020的读使能线接好(地址、数据和片选信号先接好了),发现此时CPU不能启动了,但是将此信号接到未使用的EX_IO上时CPU可以正常启动,说明读使能信号干扰了CPU的启动,可是,cpu_oen和cpu_wen属性是一样的,input到FPGA内部,不存在输出到CPU导致不能启动,查看p2020的datasheet发现p2020上的LGPL2信号有两重定义:

1、Local Bus的读使能cpu_oen
2、配合LBCTL、LALE信号配置e500核pll时钟占空比。

怀疑是系统启动后短时间内FPGA侧的cpu_oen电平影响到CPU侧的LGPL2,为此,我们将读使能改为inout信号,在CPU启动后的10s内为高阻态,起着隔离作用,而10s后p2020的bootrom也加载差不多可以bootup了,然而实际测试下来的结果是CPU依旧不能正常启动。

我们把所有的杜邦线完全拔掉,只保留读使能线的连接,发现CPU可以正常启动,此时说明FPGA侧的读使能电平并没有影响到CPU侧的启动,为了具体定位到哪一个信号,我们再次基础上,把线一点一点接上去,最后接完了读使能线、写使能线,片选线以及地址线后,CPU板可以正常bootup而且FPGA能抓到正常的读写(只是看不到读写数据)时序,插上了数据线后发现CPU就不能启动了,至此,总算定位到问题出在哪了,为此,FPGA侧第一次修改,做简单的延迟处理,数据线一直处于20s的高阻态(此时CPU可以进行Local Bus写操作),等到bootup后,才使能数据的读出,这样处理其实很笨拙,但是确实能解决CPU不能正常启动的问题,可治标不治本/允悲/,毕竟刻意的延迟不靠谱。


/**********第一次修改********/
//rst_done只有20s后才为1,在此之前cpu_data处于高阻
assign cpu_data  =((cpu_oen==1'b0)&&(rst_done==1'b1)) ? cpu_rdata : 16'bz;
assign cpu_wdata = cpu_data ;
/*******************************/

灵感一现。FPGA侧在“assign cpu_data =(cpu_oen==1'b0) ? cpu_rdata : 16'bz;”只是多加了一条约束条件“(rst_done==1'b1)”就能让CPU启动,说明在CPU启动之初,cpu_oen满足低使能导致cpu_data有输出干扰到CPU侧的数据线,,,本文之初提及p2020内部还有nand_flash和nor_flash,在p2020启动的时候要从这些flash里读写数据,也拉低了cpu_oen,此时Local Bus读出的是默认值16’h0,而本应该从*_flash里读出一些有用的值被16’h0覆盖。为了验证这种猜想,我将数据信号的代码做第二次修改。


/**********第二次修改********/
//CPU只有片选CS_4了localbus后FPGA才会将cpu_rdata输出,否则高阻.
assign cpu_data  =((cpu_oen==1'b0)&&(cpu_csn[2]==1'b0)) ? cpu_rdata : 16'bz;
assign cpu_wdata = cpu_data ;
/*******************************/

这样修改后CPU正常启动,因为p2020启动之初,会读*_flash,虽然拉低了cpu_oen,但是选通并非CS2,故此时FPGA的cpu_data保持高阻,直到片选成功且读使能低有效才将读数据输出。最终调试正确的板级读操作时序如下图6所示。

FPGA Local Bus

图6 板级读操作

3.2 硬中断

p2020处理器的中断分为外部中断和内部中断,我们使用的是外部中断1。在Vxworks下,处理器外部中断向量从0开始编号,故分配给外部中断1的中断向量为1。首先,在BSP中将中断向量与设备绑定,并注册到hcfDevice设备列表中;然后,在驱动中初始化并使能中断,即可收到来自外部中断1引脚的中断信号。

需要注意的是,下图7是p2020datasheet关于中断irq的描述:为高时置中断,为低时不产生中断,然而实际板级测试发现,irq为低时有效,产生硬线中断。

FPGA Local Bus

图7 p2020中断irq描述

当驱动收到中断后,需要对硬线中断进行复位,参考PCIe总线中INTa中断操作(干货!实测VxWorks响应PCIe中断的最小时间间隔),我们定义0x110寄存器为中断寄存器,驱动往0x110第[15]位写1再写0时硬件将硬线中断信号重新拉回高电平复位状态,中断时序如下图8所示。

FPGA Local Bus

图8 中断时序