分享

OpenRisc CPU的Wishbone片上总线

 lynkiyee 2011-02-12

本章详细介绍了Wishbone标准,主要参考了Wishbone标准B.3版本的核心内容,感兴趣的读者可去www.下载英文原文。

目录

[隐藏]

片上总线技术综述

随着超大规模集成电路的迅速发展,半导体工业进入深亚微米时代,器件特征尺寸越来越小,芯片规模越来越大,可以在单芯片上集成上百万到数亿只晶体管。如此密集的集成度使我们现在能够在一小块芯片上把以前由CPU和外设等数块芯片实现的功能集成起来,由单片集成电路构成功能强大的、完整的系统,这就是我们通常所说的片上系统。

IP复用是片上系统时代的核心技术之一。由于IP核的设计千差万别,它们要能够直接连接,就要遵守相同的接口标准。在片上系统中,处理器核和所有外设通过共享总线互通互联,因此这些IP核必须遵守相同的总线规范。总线规范定义的是IP核之间的通用接口,因此它定义了一套标准的信号和总线周期,以连接不同的模块,而不是试图去规范IP核的功能和接口如何实现。一个片上总线规范一般需要定义各个模块之间初始化、仲裁、请求传输、响应、发送接收等过程中驱动、时序、策略等关系。

芯片与电路板的资源和环境的不同,导致片上总线与板上总线存在若干明显差异,包括:

①片上总线多采用单向信号线,而板上总线多采用三态信号。片上三态总线无论在功耗、速度、可测性上都存在很大缺陷,而且一旦出现多驱动情况便会损毁芯片(比如若应该输出"Z"的信号实际输出为"1",而另有一个信号输出为"0",就形成一个低电阻通路,导致局部电流过大,热量难以及时释放,从而增加芯片功耗和大大降低芯片寿命)。由于片上布线资源较为丰富,因此片上总线多采用单向信号线。由于电路板上布线资源较为昂贵,因此板上总线多采用三态总线,但是由于三态总线的功耗问题和速度限制,目前板上总线也在向串行和非三态方向发展,如USB和PCI Express。

②片上总线比板上总线更加简单灵活。首先片上总线结构要简单,这样可以占用较少的逻辑单元;其次时序要简单,以利于提高总线的速度;第三接口要简单,如此可减少与IP核连接的复杂度。片上系统应用广泛,不同的应用对总线的要求各异,因此片上总线具有较大的灵活性。其一,多数片上总线的数据和地址宽度都可变,其二,部分片上总线的互连结构可变,如Wishbone总线支持点到点、数据流、共享总线和交叉开关四种互连方式;其三,部分片上总线的仲裁机制灵活可变,如Wishbone总线的仲裁机制可以完全由用户定制。而板上总线则较为死板,时序也更加苛刻。

目前比较常见的片上总线规范有ARM公司的AMBA、Silicore的Wishbone、IBM公司的CoreConnect和Altera公司的Avalon。三种总线各有特点,其适用范围也不同。AMBA 总线规范因ARM处理器的广泛使用而拥有众多第三方支持,被ARM公司90%以上的合作伙伴采用,已成为广泛支持的现有互连标准之一。IBM公司的CoreConnect因为IBM的业界地位也有广泛的应用。Avalon主要用于Altera公司系列PLD中,最大的优点在于其配置的简单性,可由EDA工具快速生成。这三种片上总线虽然都是公开标准,但都不是免费的。而Wishbone则是一个真正开放和免费的规范。它最先是由Silicore公司提出的,目前由OpenCores组织维护。由于其开放性,因此OpenCores上的免费的IP核,大多数都采用Wishbone标准。Wishbone的优势除开放、免费、拥有众多免费IP核外,还有简单、灵活、轻量的特点,特别适合大型IP内部的小型IP之间的互联。在本书介绍的OpenRISC处理器设计中,各个模块间的互联接口中大量的采用了Wishbone总线规范。

需要指出,任何一个总线规范,首先至少支持点对点互联,所以严格意义上讲,国内学术界普遍讲的"总线规范"实际上指的是片上系统互联规范,只不过多个IP通过共享总线互联是人们最关心的,也是最广泛使用的,所以常笼统的将片上系统互联规范讲为总线规范。

Wishbone片上总线的基本特点

一般总线规范的共同特点为同时适用于于软核、固核和硬核设计;对开发工具和目标硬件没有特殊要求,并且几乎兼容已有的所有综合工具;可以用多种硬件描述语言来实现;支持结构化设计方法学,以提高大团队的设计效率;灵活的数据和地址总线宽度,支持大端和小端操作;支持主从设备接口,支持多主设备,这是片上共享总线通信所必须的,多个主设备同时需要进行总线操作时,由仲裁器决定当前哪个主设备可以使用总线,仲裁逻辑用户可以自行定义。此外,在作者看来,Wishbone总线规范的特点还包括:

1. 支持点到点、共享总线、十字交叉(Crossbar)和基于交换结构(Switch fabric)的互联。Wishbone总线规范是"轻量级(Lightweight)"规范,它实际上更加侧重于点对点互联以及复杂度不高的共享总线片上系统互联。因此,与其他总线规范相比,Wishbone的接口更加简单紧凑,接口需要的互联逻辑非常少(见本章后续的例子),这是Wishbone的一个重要优势。熟悉AMBA总线的读者在读完本章后就能够理Wishbone比AMBA总线简单的多,但是两者各有个的优势。AMBA将所有的外设分为高速设备和低速设备,高速设备连接到系统总线,通常通信的数据量也较大,如SDRAM控制器、NAND Flash控制器、LCD控制器,低速设备连接外设总线,通常通信数据量小,如通用串口、计时器等。外设总线与系统总线通过总线桥接器相连。在实际系统中,处理器、系统总线、外设总线间的时钟频率的典型比值之一是4:2:1,因此整个系统的功耗被有效降低。另外一个好处是通过在总线桥接器中添加DMA引擎,系统总线和外设总线可以实现速率解耦。而依照Wishbone规范,Wishbone总线上所有设备都要连接到同一总线,无论是高速设备还是低速设备。与Wishbone总线相比,AMBA的缺点是设计较为复杂。但是实际上,Wishbone总线也设计为类似AMBA总线的架构,但此时系统为双Wishbone总线架构。

2. 支持典型的数据操作,包括:单次读/写操作、块读/写操作,读改写(RMW,Read-Modify- Write,细节见后文)操作。最快情况下一个时钟周期可完成一次操作,操作的结束方式包括:成功、错误和重试(Retry)。一个总线周期完成一次操作,一次操作可以是单次读/写操作、块读/写操作或者读改写操作。操作总是在某一总线周期内完成的,因此操作结束方式也称为总线周期结束方式,两者说法在后文将视情况使用。成功是操作的正常结束方式,错误表示操作失败,造成失败的原因可能是地址或者数据校验错误,写操作或者读操作不支持等。重试表示从设备当前忙,不能及时处理该操作,该操作可以稍后重新发起。接收到操作失败或者重试后,主设备如何响应取决于主设备的设计者。

3. 允许从设备进行部分地址解码,有利于减少了冗余地址译码逻辑,提高地址译码速度。这一点本书将通过实例说明。

4. 支持用户定义的标签。这些标签可以用于为地址、数据总线提供额外的信息如奇偶校验,为总线周期提供额外的信息如中断向量、缓存控制操作的类型等。Wishbone规范只定义标签的时序,而标签的具体含义用户可自行定义。支持用户定义的标签是Wishbone规范区别与其他片上总线规范的重要特征之一。

5. 全同步化设计,包括复位方式。但是实际上复位方式采用同步还是异步并不影响IP的互联互通,因此兼容Wishbone规范的IP完全可以采用异步方式,http://www.上面的很多兼容Wishbone规范的开源IP采用的就是异步复位。

总结以上内容,Wishbone总线规范区别于其他总线规范的两个核心特点是:(1)是轻量级规范,因此接口更加简单紧凑;(2)支持支持用户定义的标签。

接口信号定义

所有的Wishbone接口信号都是高电平有效,设计成高电平有效的主要原因是由于低电平有效信号的书写问题,不同的设计者表达低电平有效信号的方式不同,拿最常见的低电平有效的复位信号来说,其表示方法就有_RST_I、N_RST_I、#RST_I和/RST_I,而高电平有效的信号其表达方式通常只有一种。

所有的Wishbone接口信号都以_I或者_O结束。_I表示输入,_O表示输出。()表示该信号为总线信号,总线的宽度可以为1,也可以为大于1的任何值。

Openrisc wishbone bus introduction 01.png

图1 Wishbone总线规范中使用的主要信号(一个点到点互联的例子)

在图1中,主设备具有遵守Wishbone规范的主设备接口,从设备具有遵守Wishbone规范的从设备接口,INTERCON模块将主设备和从设备的对应数据、地址和控制线连接起来,SYSCON模块提供时钟和复位信号,这两个信号被送入主设备和从设备。图1给出了Wishbone接口的常见信号,这些信号有些是必须的,有些是可选的。这些信号的基本功能描述如下:

CLK_O/CLK_I:时钟信号。时钟信号由SYSCON模块产生,并送入各个主设备和从设备。SYSCON通常内部存在一个锁相环,将来源于芯片外的晶体振荡器或者时钟输入信号整形、分频或者倍频为芯片内所需要的时钟信号。所有Wishbone信号都同步到时钟信号上,包括复位信号。

RST_O/RST_I:同步复位信号,高电平有效。复位信号由SYSCON模块产生,并送入各主设备及从设备。

DAT_O()/DAT_I():主设备和从设备的之间的数据信号,数据可以由主设备传送给从设备,也可以由从设备传送给主设备。一对主设备和从设备之间最多存在两条数据总线,一条用于主设备向从设备传输数据,另外一条用于从设备向主设备传输数据。Wishbone规定数据总线的最大宽度为64位,这一规定实际上是考虑到目前商用处理器的最大位数为64,实际上数据总线的宽度可以是任意值。就笔者看来,在64位处理器以后,处理器将向多核方向发展,总线将向高速串行方向发展。

ADR_O(n…m)/ADR_I(n…m):地址信号,主设备输出地址到从设备。n取决于IP核的地址宽度,m取决于数据总线DAT_O()/DAT_I()的宽度和粒度。数据总线的粒度指的是数据总线能够一次传送的最小比特数,很多处理器如ARM的数据总线的粒度为1个字节,但是也有一些处理器如CEVA TeakLite DSP的数据总线粒度为2个字节。一个位宽为32比特、粒度为1个字节的数据总线的地址信号应定义为ADR_O(n…2)/ADR_I(n…2)。在传送数据时,具体哪些字节有效通过SEL_O()/SEL_I()信号(见下文)控制。

TGD_O/TGD_I()、TGA_O()/TGA_I():TGD_O/TGD_I()为数据标签,具体讲是附加于在数据总线DAT_O()/DAT_I()的标签,该标签可以用于传送关于数据总线的额外信息如奇偶校验信息、时间戳信息等。TGA_O/TGA_I()为地址标签,具体讲是附加于在地址总线ADR_O()/ADR_I()的标签,该标签可以用于传送关于地址总线的额外信息如地址总线奇偶校验信息、存储器保护信息等。Wishbone只规定了TGD_O/TGD_I和TGA_O()/TGA_I()的接口时序,用户可以定义TGD_O/TGD_I的具体含义。

TGC_O/TGC_I():TGC_O/TGC_I()为总线周期标签,该标签可以用于传送关于当前总线周期所进行操作的描述如操作类型(包括单次操作、块操作、RMW操作)、中断应答类型、缓存操作类型等。类似的,Wishbone只规定了TGC_O/TGC_I()的接口时序,用户可以定义TGD_O/TGD_I的具体含义。

ACK_O/ACK_I、ERR_O/ERR_I、RTY_O/RTY_I:主从设备间的操作结束方式信号。ACK表示成功,ERR表示错误,RTY表示重试(Retry)。操作总是在某一总线周期内完成的,因此操作结束方式也称为总线周期结束方式。成功是操作的正常结束方式,错误表示操作失败,造成失败的原因可能是地址或者数据校验错误,写操作或者读操作不支持等。重试表示从设备当前忙,不能及时处理该操作,该操作可以稍后重新发起。接收到操作失败或者重试后,主设备如何响应取决于主设备的设计者。

SEL_O()/SEL_I():有效数据总线选择信号,标识当前操作中数据总线上哪些比特是有效的,以总线粒度为单位。SEL_O()/SEL_I()的宽度为数据总线宽度除以数据总线粒度。比如一个具有32位宽、粒度为1个字节的数据总线的选择信号应定义为SEL_O(3:0)/ SEL_I(3:0),SEL(4’b1001)代表当前操作中数据总线的最高和最低字节有效。

CYC_O/CYC_I、LOCK_O/LOCK_I、GNT_O()/GNT_I:总线周期信号CYC_O/CYC_I有效代表一个主设备请求总线使用权或者正在占有总线,但是不一定正在进行总线操作(是否正在进行总线操作取决于选通信号STB_O/STB_I是否有效)。只有该信号有效,Wishbone主设备和从设备接口的其它信号才有意义。CYC_O/CYC_I信号在一次总线操作过程中必须持续有效,比如一次块读操作可能需要多个时钟周期,CYC_O/CYC_I信号必须保持持续有效。实际上,该信号的实际作用等同于其他总线标准中的仲裁申请信号。当存在多个主设备时,它们可能希望同时进行总线操作,主设备通过仲裁申请信号向仲裁器申请总线占有权,仲裁器通过一定的仲裁优先级逻辑向其中一个选定的主设备发送总线允许信号GNT_O()/GNT_I,表示该主设备可以占用总线。GNT_O()是仲裁器输出的允许信号,一般有多个;而对于一个主设备,其允许信号输入GNT_I却只有一个。一次总线操作可能需要多个时钟周期,比如一次块操作。在操作过程中,仲裁器可能会提前将总线占用权收回并分配给其他主设备从而打断当前主设备的操作,LOCK_O/LOCK_I有效代表本次总线操作是不可打断的。仲裁器收到LOCK_I信号,就不会提前收回总线使用权。图1中只有一个主设备和一个从设备,因此没画出仲裁器模块,该模块可以视为是INTERCON的一部分,见本章最后给出的例子。

STB_O/STB_I:选通信号。选通有效代表主设备发起一次总线操作。只有选通信号有效(此时CYC_O/CYC_I也必须为高),ADR_O/ADR_I()、DAT_O()/DAT_I()、SEL_O()/SEL_I()才有意义。在Wishbone总线规范中,CYC_O/CYC_I是最高层的控制信号,只有该信号有效,STB_O/STB_I信号才有意义。一个信号有意义是指该信号的当前值是需要主设备或者从设备解释的,0为无效,1为有效,而一个信号没有意义是指该信号的当前值主设备和从设备不必关心,是0还是1都无效。

WE_O/WE_I:写使能信号,代表当前周期中进行的操作是写操作还是读操作。1代表写,0代表读。

Wishbone支持的互联类型

Wishbone规范支持的互联类型有四种:点到点、共享总线、十字交叉(Crossbar)和基于交换结构 (Switch fabric)的互联。这四种互联方式在Wishbone规范中有明确的定义。如图1(第4页),点到点互联是最简单的互联方式,它允许一个主设备和一个从设备相互通信。比如,主设备可能是一个处理器IP核,从设备可能是一个串行I/O端口或者存储器。

点对点互联的一个特殊应用就是当数据需要多个步骤顺序进行处理时,多个同时具有主设备接口和从设备接口的模块串接在一起,如图2所示。这种连接方式也称作数据流连接。模块A处理完数据后将处理结果送给模块B,模块B处理完模块A送来的数据后再将处理结果送给模块C处理。数据从一个模块送往另一个模块,常常将这种处理方式称作流水线。流水线在处理器设计中非常重要,其好处是多个数据可以并行处理,从而提高系统的整体数据处理能力。

Openrisc wishbone bus introduction 02.png

图2 数据流方式互联

共享总线互联方式如图3所示。共享总线互联方式适合于系统中有两个或者多个主设备需要与一个或者多个从设备通信的场合,它们通过共享的总线进行通信,其好处是结构紧凑,节省布线资源,缺点是主设备希望与特定从设备通信时可能需要等待。主设备在需要与一个从设备通信时,需要先向仲裁器(图中没有给出)申请总线占有权,获得允许后开始占用总线并与目标从设备开始通信,通信结束后释放总线。当多个主设备同时希望占有总线时,仲裁器通过一定的优先级逻辑分配总线使用机会。

共享总线互联方式在片上系统中得到了广泛的使用,几乎成了片上系统的标志技术之一。共享总线技术在板级系统如PCI总线中也得到了应用。

Openrisc wishbone bus introduction 03.png

图3 共享总线互联方式

十字交叉互联方式较共享总线互联方式略复杂。共享总线互联方式在任一时刻只允许最多有一对主设备和从设备通过共享总线进行通信,而十字交叉互联方式最多同时允许超过一对的主设备和从设备对同时进行通信,如图4所示。当然,在十字交叉互联方式中,一个从设备在任一时刻只能与一个主设备进行通信,两个主设备不能同时访问同一从设备。

Openrisc wishbone bus introduction 04.png

图4 十字交叉互联方式(图中虚线代表了一种可能的通信方式)

类似的,基于交换结构的互联比十字交叉互联方式的INTERCON模块更复杂,一般的,基于交换结构的互联比十字交叉互联允许同时通信的主从设备对更多。

随着技术的发展,已经出现了比片上总线更复杂的片上网络技术(Network on chip)。基于片上网络技术,已经出现了内含几十个32位处理器的芯片,已应用于CISCO的高端路由器中。

Wishbone总线周期

一个总线周期由多个不可分的时钟周期构成,完成单次读/写操作、块读/写操作或者读改写操作。总线周期也分为单次读/写周期、块读/写周期和读改写周期。一次块读/写总线周期完成多次数据读/写操作。一般情况下,一次操作由主设备和从设备控制信号间的一次握手,以及同时进行的地址和数据总线的一次传输构成。块操作表示整个操作需要完成多次数据传送。在总线周期中主设备和从设备预先设定好的共同遵守控制信号握手规则,以及地址和数据总线的传输规则称作总线协议。

在下文所给图例中给出的信号均为主设备的输入输出信号。因此,对操作的描述也从主设备信号的角度展开,以便于读者对照正文理解图例。比如"在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将适当的数据放到主设备的输入信号DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。"的等价描述为:"在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将适当的数据放到其输出信号DAT_O()和TGD_O(),将输出信号ACK_O置高作为对主设备STB_O的响应。"

一般操作

复位操作

复位是数字系统最基本的操作。复位后,系统进入预定的状态。在遵守Wishbone总线规范的系统中,当RST_I信号有效,系统开始复位。由于Wishbone规范规定的复位是同步复位,因此在复位信号有效后接下来的时钟上升沿,所有信号和寄存器进入预定状态。因此,Wishbone规范要求RST_I信号有效时间至少要一个时钟周期。在数字系统中,实际上更多的采用的是异步复位,而且复位信号的长度一般大于系统电平稳定时间和系统时钟频率稳定时间。如果在Wishbone接口中使用的是异步复位,需要在文档中说明,因为Wishbone接口默认的是同步复位。

Openrisc wishbone bus introduction 05.png

图5 Wishbone总线的复位操作

在图5中,我们只给出了STB_O和CYC_O这两个信号,而没有给出其它信号。当这两个信号无效时,所有其他信号没有意义。

操作发起

一个总线周期由至少一次总线操作构成。操作总是由主设备发起,主设备发起的操作可以是单次读/写、块读/写或者RMW操作。当主设备将CYC_O置为高时,一个总线周期开始。总线周期开始后,当主设备将STB_O置为高时,一次总线操作开始。当主设备将CYC_O置为低时,主设备的所有其他信号没有意义。从设备只在CYC_I为高时才会对主设备发起的操作进行响应。

CYC_O和STB_O可以同时从无效变为有效表示操作开始,CYC_O持续有效直到操作结束,CYC_O和STB_O可以同时从无效变有效表示同时发起一次总线周期的同时,也发起总线操作,也可以同时从有效变为无效表示操作结束。因此,在只有一个主设备时,可以将CYC_O和STB_O合并为一个信号,比如在OpenRISC1200的源码中就广泛的使用了这种方式,信号的名字称作CYCSTB_O。

当存在多个主设备时,一个主设备完成操作后必须及时将CYC_O信号置为低,以让出总线给其他主设备。此时,CYC_O信号等价于为总线占用请求信号。

基本握手协议

由于在整个总线周期,CYC_O必须始终保持有效,因此,此后我们将在给出的图中忽略该信号,只在必要的时候给出。

握手发生在主设备和从设备之间。握手协议是主设备和从设备在握手时所遵守的共同规则。如图6,当主设备准备好,它将STB_O信号置高。STB_O信号一直为高,直到从设备通过置高ACK_O、ERR_O、RTY_O对本次操作发起响应。在图7中我们以主设备的ACK_I信号作为示例,后文也如此。通过握手,主设备和从设备不仅可以完成通信,而且可以控制它们之间的通信速率。

Openrisc wishbone bus introduction 06.png

图6 Wishbone总线的基本握手协议

如果从设备保证能够在主设备发起操作时及时作出操作成功的响应,其ACK_O信号可以设计为STB_I和CYC_I信号的逻辑与,而ERR_O和RTY_O信号也可以不使用。因此ERR_O和RTY_O信号是可选的,而ACK_O信号是必须的。在点对点连接中,甚至可以将ACK_I信号直接置高。当存在ERR_O和RTY_O信号,主设备当发现ERR_O和RTY_O信号之一有效时如何进行响应取决于主设备的设计。

对于从设备,只有STB_I和CYC_I同时为高时,才能发起对主设备的响应。

实际上,对于主设备,其最小配置为只有ACK_I、CLK_I、CYC_O、RST_I和STB_O;而对于从设备,其最小配置为只有ACK_O、CLK_I、CYC_I、RST_I和STB_I,这里CYC 和STB信号可以合并到一起成为一个信号,通常命名为CYCSTB_O/。

在图6中,从STB_O到ACK_I存在一个长组合逻辑路径,在实际系统中很可能成为关键路径。因此,在设计中应尽量保证STB_O是触发器的直接输出。如果从STB_O到ACK_I存在一个长组合逻辑路径延迟不能满足设计的时序要求,可将从设备的ACK_O经过触发器寄存后再输出,从而将长组合逻辑打破,但系统的吞吐量也将因此减小。关于如何即打破长组合逻辑又不影响系统的吞吐量,后文将进行详细讨论。

单次读/写周期

单次读/写操作每次操作只完成一次读或者写,是最基本的总线操作方式。但是,Wishbone主设备或者从设备也可以不支持单次读/写操作,甚至没有地址和数据总线。

单次读操作如图7。在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将WE_O置为低表示读操作,将适当的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置,将CYC_O和TGC_O置高表示操作正在进行,将STB_O置高表示操作开始。

在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将适当的数据放到主设备的输入信号DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿1,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,并将STB_O和CYC_O置为低表示操作完成。从设备发现STB_O置低后,也将主设备的输入信号ACK_I置低。

在图7中,从设备可以上升沿0和上升沿1之间插入任意多个等待周期。

Openrisc wishbone bus introduction 07.png

图7 Wishbone总线的单次读操作

单次写操作如图8所示。在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将数据信号DAT_O()、TGD_O()放到总线上,将WE_O置高表示写操作,将适当的SEL_O()信号置高通知从设备数据总线DAT_O()的哪些信号是有效的,将CYC_O和TGC_O置高表示操作正在进行,将STB_O置高表示操作开始。

在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿1,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,将STB_O和CYC_O置为低表示操作完成;从设备发现STB_O置低后,也将主设备的ACK_I置低。

在图8中,从设备可以在上升沿0和上升沿1之间插入任意多个等待周期。

Openrisc wishbone bus introduction 08.png

图8 Wishbone总线的单次写操作

块读周期

块读/写操作每次读/写数据多次。块读/写操作实际上是由顺序进行的多个单次读/写操作组合而成的。在同时存在多个主设备时,块读/写操作非常有用,一个块读写一般是不能打断的,比如一次DMA,如图9所示,在一次块操作中,CRC_O信号必须保持为高。同时为了保证整个块操作不被打断,LOCK_O也可以置为高,但是LOCK_O不必须为高。典型地,主设备进行一次DMA连续传输4个或者8个总线宽度单位的数据,然后主动释放总线,其后又试图占用总线重新进行DMA,直到所有的数据都传输完毕。这样做的好处是允许其他优先级更高的主设备在两次DMA之间插入操作以完成更加紧急的任务。

Openrisc wishbone bus introduction 09.png

图9 Wishbone总线块操作中CYC_O信号的用法

块读操作如图10所示。块操作最多能够在每个时钟周期进行一次数据读或者写,但是主设备和从设备都可以通过插入等待周期控制块操作的速度。一次块操作包括多次子操作。每一次子操作都是块操作的一个阶段,完成一次数据读或者写。图10的块操作由五次读操作完成,其过程如下:

在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将WE_O置为低表示读操作,将适当的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置,将CYC_O和TGC_O()置高表示操作正在进行,将STB_O置高表示一次子操作开始。CYC_O和TGC_O()从无效变为有效可以发生在上升沿0以前的任何时刻。

在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将适当的数据放到主设备的DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿1,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,完成第一次子操作。主设备将新地址信号ADR_O()、新TGA_O()放到总线上,将新的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置。

在时钟上升沿2到达之前,从设备检测到主设备发起的第二次操作,将适当的数据放到主设备的DAT_I()和TGD_I(),继续将主设备的ACK_I置高。

在时钟上升沿2,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,完成第二次子操作。主设备将STB_O信号置低表示插入等待周期。

在时钟上升沿3到达之前,从设备检测到STB_O信号为低,将ACK_I置低。

在时钟上升沿3,主设备发起第三次操作,将新的地址信号ADR_O()、新的TGA_O()放到总线上,将WE_O置为低表示读操作,将适当的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置,将STB_O置高表示第三次子操作开始。

在时钟上升沿4到达之前,从设备检测到主设备发起的第三次子操作,将适当的数据放到主设备的DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。

Openrisc wishbone bus introduction 10.png

图10 Wishbone总线的块读操作

在时钟上升沿4,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,完成第三次子操作。主设备同时发起第五次子操作,将新地址信号ADR_O()、新TGA_O()放到总线上,将新的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置。

在时钟上升沿5到达之前,从设备检测到主设备发起的第五次子操作,将适当的数据放到主设备的DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿5,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,完成第五次子操作。主设备同时发起第六次子操作,将新地址信号ADR_O()、新TGA_O()放到总线上,将新的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置。

在时钟上升沿5过后,从设备检测到主设备发起的第六次子操作,但是由于数据没有准备好,它在新上升沿到达之前将ACK_I信号置低表示插入等待周期。
上升沿5和6之间被插入了多个等待周期。当从设备准备好数据,在时钟上升沿6到达之前,将适当的数据放到主设备的DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿6,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,并将STB_O和CYC_O置为低表示整个块读操作完成。 从设备发现STB_O置低后,也将主设备的ACK_I置低。

块写周期

块写操作的例子如图11所示。图11的一次块写操作由五个相互关联的单次写操作完成。

在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将数据信号DAT_O()、TGD_O()放到总线上,将WE_O置为高表示写操作,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将CYC_O和TGC_O置高表示操作正在进行,将STB_O置高表示第一次写操作开始。

在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿1,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第一次写操作完成,于是将新地址信号ADR_O()、新TGA_O()放到总线上,将新数据信号DAT_O()、新TGD_O()放到总线上,将WE_O置为高表示写操作,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将CYC_O和TGC_O置高表示操作继续在进行,将STB_O置高表示第二次写操作开始。

在时钟上升沿2到达之前,从设备检测到主设备发起的第二次写操作,将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿2,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第二次写操作完成,发现自己的数据还没有准备好,于是将STB_O置低表示插入等待周期。

在时钟上升沿3到达之前,从设备检测到STB_O为低,也将ACK_I置低。

在时钟上升沿3,主设备发起第三次操作,将新地址信号ADR_O()、新TGA_O()放到总线上,将新数据信号DAT_O()、新TGD_O()放到总线上,将WE_O置为高表示写操作,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将CYC_O和TGC_O置高表示操作继续进行,将STB_O置高表示第三次写操作开始。

在时钟上升沿4到达之前,从设备检测到主设备发起的第三次写操作,将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿4,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第三次写操作完成,于是将新地址信号ADR_O()、新TGA_O()放到总线上,将新数据信号DAT_O()、新TGD_O()放到总线上,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将STB_O继续置高表示第四次写操作开始。

在时钟上升沿5到达之前,从设备检测到主设备发起的第四次写操作,将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿5,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第四次写操作完成,于是发起第五次操作,将新地址信号ADR_O()、新TGA_O()放到总线上。

在时钟上升沿5之后新的时钟上升沿到达之前,从设备发现因为某些原因暂时无法继续接收数据,因此将ACK_I信号置低,插入等待。

当从设备发现可以继续接收数据,于是在在最后一个等待周期结束且上升沿5到达之前,将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第五次写操作完成。并将STB_O和CYC_O置低表示整个块写操作完成。

Openrisc wishbone bus introduction 11.png

图11 Wishbone总线的块写操作

RMW操作

在操作系统中,有一种重要的进程间的同步机制称作信号量机制。信号量即当前可用资源的计数。信号量是一个用来实现同步的整型或记录型(Record)变量,除了初始化外,对它只能执行等待和释放这两种原子操作。一次对信号量的等待操作是获得信号量的过程,读取当前信号量的值,如果发现有可利用资源,则将信号量减1,否则进入等待状态。一次对信号量的释放过程即将信号量加1。一个进程对信号的读取、计算新的信号量值、更新信号量的值这三个步骤是不允许被其他进程打破的,如果被打破,则信号量的值将会发生错误,RMW操作的最大用途在于信号量操作。

一次RMW操作对于总线来说,本质上是两次子操作,一次读,一次写,只不过这两次子操作必须由同一个主设备的完成,且读数据和写数据的地址相同。"改"是不发生在总线上的,它发生在主设备内部。一个RMW操作的例子如图13所示,其过程如下:

在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将WE_O置为低表示读操作,将适当的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置,将CYC_O和TGC_O()置高表示操作正在进行,将STB_O置高表示第一次子操作开始。CYC_O和TGC_O()可以发生在上升沿0以前的任何时刻。

在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将适当的数据放到主设备的DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿1,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,完成第一次子操作。主设备将STB_O置低表示插入等待。

在时钟上升沿1之后新的上升沿到达之前,从设备检测到主设备将STB_O置低,于是将ACK_I置低。

经过若干等待周期,在上升沿2,主设备将地址信号ADR_O()、TGA_O()放到总线上,将数据信号DAT_O()、TGD_O()放到总线上,将WE_O置为高表示写操作,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将STB_O置高表示第二次子操作开始。

在时钟上升沿3到达之前,从设备检测到主设备发起的操作,将主设备的ACK_I置高作为对主设备STB_O的响应。

在时钟上升沿3,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第二次子操作完成,于是将STB_O和CYC_O置低表示整个RMW操作完成。

在时钟上升沿之后,从设备发现STB_O为低,于是将ACK_I置低。

Openrisc wishbone bus introduction 12.png

图13 Wishbone总线的RMW操作

数据组织

数据组织是指数据的传送顺序。目前常见的32为处理器的数据总线粒度为1字节,在传送时,一个32位数据的最高字节可以放在数据总线的最低8位传送,也可以放在数据总线的最高8位传送,因此出现了大端和小端两种数据组织方法。大端是指一个数据的最高位放在数据总线的最低位传送或者放在地址较小的存储器位置存储;小端是指一个数据的最高位放在数据总线的最高位传送或者放在地址较高的存储器位置存储。Wishbone同时支持大端和小端两者数据组织方式。当数据总线的粒度和宽度相同时,大端和小端是一样的。

这两者数据组织方式在一般文献中都可以找到。总线标准只定义接口的通信协议,而数据的组织本质上取决于主设备和从设备的设计。有时需要将大端和小端的接口互联起来,下图给出了将数据组织为大端的IP A和数据组织为大端的IP B相连的情形。IP A和IP B的数据总线宽度都是32为,粒度为8位。

Openrisc wishbone bus introduction 13.png

图14 大端和小端的接口互联

Wishbone寄存反馈总线周期

周期的同步与异步结束方式之对比

为了实现在给定时钟频率下的最大可能吞吐量,Wishbone采用了周期异步结束方式。这样做的结果是从主设备的STB_O到从设备的ACK_O/ERR_O/RTY_O再到主设备的ACK_I/ERR_I/RTY_I输入形成了一个异步回路,如图15。在大型SoC设计中,该回路往往成为整个设计的关键路径,限制系统时钟频率的进一步提高。在深亚微米时代,由于线延迟往往比门延迟更大,这一异步回路更加可能成为系统性能的瓶颈。

Openrisc wishbone bus introduction 14.png

图15 Wishbone总线的异步周期结束路径

这一问题的最简单解决方法是插入寄存器将回路断开,但这样做的缺点是在每一次总线操作中都需要插入一个等待周期,从而制约了总线吞吐量。如图16所示,在上升沿1主设备发起了一次操作,在上升沿2从设备发起响应将ACK_O置高,在上升沿3主设备检测到ACK_I为高完成第一次操作并发起第二次操作,但是在上升沿3从设备并不知道主设备会发起第二次操作,因此只能将ACK_O信号置低。在上升沿4从设备才能对第二次操作发起响应将ACK_O置高,在上升沿4主设备检测到ACK_I为高完成第二次操作。

Openrisc wishbone bus introduction 15.png

图16 Wishbone总线的传统同步周期结束方式

在图16中,每一次传输都需要两个时钟周期,一半的带宽被浪费。如果从设备在上升沿3知道主设备将发起新的操作,它将能够在上升沿3就对主设备的操作发起响应,从而节省时钟周期提高了系统的吞吐量,利用该思想改进后的同步时序如图17所示。

Openrisc wishbone bus introduction 16.png

图17 Wishbone总线改进的同步周期结束方式

在图17的上升沿1,主设备发起操作,在上升沿2,从设备将ACK_O置高,在上升沿3,从设备知道主设备将发起新的操作,于是将ACK_O继续置高。因此, 3个时钟周期就完成了2次操作,而不是原来大的4个时钟周期,于是吞吐量提高了25%。一般的,改进后,N次操作需要N+1个时钟周期,而不是2N个时钟周期,吞吐量改善为(N-1)/N%。

改进后的同步周期结束方式具备异步周期结束方式的吞吐量优势,同时具备传统同步结束方式的延迟优势。改进后的同步周期结束方式称作Wishbone寄存反馈周期结束方式。

Wishbone寄存反馈周期结束方式

在Wishbone寄存反馈周期结束方式中,主设备需要事先通知从设备操作下一时钟周期是否将发起新的总线操作,这是通过周期类型识别地址标签CTI_O()/CTI_I()和突发类型扩展地址标签BTE_O()/BTE_I()完成的。主设备和从设备必须同时支持CTI_O()/CTI_I(),Wishbone接口才能以寄存反馈周期结束方式工作,主设备和从设备如果支持地址增加突发模式,它们必须同时支持BTE_O()/BTE_I()。如果主设备或者从设备之一不支持这种周期结束方式,它们只能以传统周期结束方式工作。

周期类型识别地址标签CTI_O()/CTI_I()提供描述当前操作突发模式的信息,从设备可以根据该信息确定在下一时钟周期如何进行响应。CTI_O()/CTI_I()的具体定义如表2。

表2 周期类型标识符
CTI_O(2:0) 描述
3’b000 传统总线周期
3’b001 恒定地址突发总线周期
3’b010 递增突发总线周期
3’b011 预留
3’b100 预留
3’b101 预留
3’b110 预留
3’b111 突发结束

不支持CTI_O()的主设备必须将该信号置为3’b000或者3’b111,这两个是等价的。主设备和从设备可以支持这些突发模式中的一部分,对于不支持的突发模式,主设备必须将CTI_O()置为3’b000,从设备必须将CTI_I()理解为3’b000。

为了减少在同步模式下的等待时间,从设备必须对主设备的操作尽快的做出响应。从设备可利用CTI_I()信号确定在下一周期对主设备的响应,但是由于此时还不知道STB_I信号的状态,因此从设备在确定在下一周期对主设备的响应时只能忽略STB_I信号,因此即使在STB_I信号为低的情况下,从设备仍然可能将ACK_O/ERR_O/RTY_O置高。因此,主设备应保证CTI_O()信息的有效性。

突发类型扩展地址标签BTE_O()/BTE_I()是对CTI_O()/CTI_I()=3’b010情况下的突发类型信息的扩展,描述的是地址值是如何变化的。其含义如表3所示。

表3 递增和递减突发类型扩展
BTE_IO(1:0) 描述
2’b00 线性突发
2’b01 叠4突发
2’b10 叠8突发
2’b11 叠16突发

突发结束

突发结束(CTI_O=3’b111)表示当前操作是当前突发的最后一次操作,主设备在当前操作结束后紧接着的时钟周期不再发起操作。

图18给出了CTI_O=3’b111的用法。图中共有3次操作。第一次操作和第二次操作是一个突发操作的一部分。第一次操作发生在上升沿0,这是一次寄存反馈读操作,第二次操作是该突发操作的最后一个操作,主设备将CTI_O置为3’b111表示整个突发操作结束。第三次操作为单次写操作。

Openrisc wishbone bus introduction 17.png

图18 Wishbone总线的突发结束

图18的操作过程如下:

在时钟上升沿0到达之前,一个突发寄存反馈读操作正在进行。从设备将数据放到数据总线DAT_I()。

在时钟上升沿0,主设备将DAT_I()采样,并更新地址总线ADR_O(),同时将CTI_O()置为3’b111表示本次突发读操作即将结束。从设备根据时钟上升沿0采样到的CTI_O()的值(不等于3’b111)知道操作还在进行,因此仍然将ACK_I置高,并在数据总线DAT_I()放上新的数据。

在时钟上升沿1,主设备将DAT_I()采样完成整个突发读操作,同时主设备将新地址信号放到地址总线ADR_O()上,将新数据信号放到数据总线DAT_O()上,将WE_O置为高表示写操作,CTI_O()置为3’b111表示本次写操作只进行一次。由于从设备根据CTI_O()为3’b111知道整个突发读操作结束,而不了解主设备将发起写操作,因此将ACK_I置低。

在时钟上升沿2,从设备检测到主设备的写操作,并将数据DAT_O()采样,同时将ACK_I置高。

在时钟上升沿3,主设备将CYC_O和STB_O置低表示操作结束,从设备将ACK_I置低。

地址不变突发

地址不变突发是一个总线周期,在这个总线周期中完成多次操作,但是地址不变。地址不变突发的典型应用是以DMA方式读/写FIFO。主设备在时钟的某一个上升沿将CTI()置为3’b001后,下一个周期的操作必须与本周期,包括SEL_O()信号也不能改变,即在地址不变突发中的每一次操作除传输的数据可能发生变化外其他都是相同的。

Openrisc wishbone bus introduction 18.png

图19 Wishbone总线的地址不变突发示例

图19给出了一个地址不变突发写总线周期示例。图中主设备和从设备一共进行了4次操作。

在时钟上升沿0,主设备将地址信号ADR_O()放到总线上,将数据信号DAT_O()、TGD_O()放到总线上,将WE_O置为高表示写操作,将CTI_O()置为3’b001表示地址不变突发总线周期,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将CYC_O和TGC_O置高表示操作正在进行,将STB_O置高表示第一次写操作开始。

在时钟上升沿1,从设备检测到主设备的写操作,将DAT_I()采样,并置高ACK_O。

在时钟上升沿2,主设备检测到ACK_I为高,知道第一次传输顺利完成,由于数据没有准备好,于是将STB_O置低表示插入等待周期。

在等待周期,从设备知道地址不变突发写操作仍然在进行,因此一直将ACK_O置高。但是此时主设备和从设备知道没有真正的传输发生。

在时钟上升沿3,主设备已经将新的数据准备好,于是将STB_O置高表示第二次写操作开始,并更新DAT_O()。

在时钟上升沿4,从设备检测到主设备的写操作,将DAT_I()采样。同时从设备发现没有足够的空间存储数据或者由于其他原因不能及时接收新的数据,于是将ACK_O置低插入等待周期。主设备检测到ACK_I为高,于是发起第三次操作,更新DAT_O()。

在时钟上升沿5,从设备发现可以继续接收数据,于是将DAT_I()采样并将ACK_O置高。

在时钟上升沿6,主设备检测到ACK_I为高,于是发起第四次操作,更新DAT_O(),同时将CTI_O置为3’b111表示本次操作为最后一次操作。

在时钟上升沿7,从设备将DAT_I()采样,并根据CTI_O为3’b111知道本次地址不变突发写操作完成,于是将ACK_O置低。

地址增加突发

地址不变突发是一个总线周期,在这个总线周期中完成多次操作,但是地址递增的。地址的单位增加值取决于数据总线的宽度和粒度。对于粒度为8比特的数据总线,当数据总线宽度为8,地址每次增加1;当数据总线宽度为16,地址每次增加2;当数据总线宽度为32,地址每次增加4。地址增加的方式又分为线性增加和折叠增加。在线性增加中,新的地址总是比旧的地址大一个单位增加值。在折叠增加中,新的地址的获得方式为:在旧的地址加一个单位增加值,其后再将地址与折叠尺寸异或。常见的折叠尺寸为4和8,因此Wishbone标准中明确定义了支持叠4和叠8操作,示例如表4所示。折叠操作常见于处理器缓存的读写。典型的SDRAM都支持折叠操作。

表4 地址增加方式
起始地址得最低若干比特 线性 叠4 叠8
000 0-1-2-3-4-5-6-7 0-1-2-3-4-5-6-7 0-1-2-3-4-5-6-7
001 1-2-3-4-5-6-7-8 1-2-3-0-5-6-7-4 1-2-3-4-5-6-7-0
010 2-3-4-5-6-7-8-9 2-3-0-1-6-7-4-5 2-3-4-5-6-7-0-1
011 3-4-5-6-7-8-9-A 3-0-1-2-7-4-5-6 3-4-5-6-7-0-1-2
100 4-5-6-7-8-9-A-B 4-5-6-7-8-9-A-B 4-5-6-7-0-1-2-3
101 5-6-7-8-9-A-B-C 5-6-7-4-9-A-B-8 5-6-7-0-1-2-3-4
110 6-7-8-9-A-B-C-D 6-7-4-5-A-B-8-9 6-7-0-1-2-3-4-5
111 7-8-9-A-B-C-D-E 7-4-5-6-B-8-9-A 7-0-1-2-3-4-5-6

主设备将CTI()置为3’b010表示地址增加突发操作,若主设备在时钟的某一个上升沿将CTI()置为3’b010,必须同时将BTE_O()置为合适的值,以通知从设备地址的增加方式,且下一个周期的传输必须与本周期相同,SEL_O()信号也不能改变。

图20给出了一个数据总线为32位粒度为8的叠4地址增加突发读操作示例。主设备和从设备之间一共进行了4次传输。

在时钟上升沿0,主设备将地址信号ADR_O()放到总线上,将WE_O置为低表示读操作,将CTI()置为3’b010表示地址增加突发操作,将BTE_O()置为2’b01表示叠4操作,将适当的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置,将CYC_O置高表示操作正在进行,将STB_O置高表示第一次传输开始。从设备根据ADI_O()准备所需数据。

在时钟上升沿1,从设备检测到主设备的读操作,将数据放到DAT_O(),同时置高ACK_O。从设备此时根据CTI()和BTE_O()知道下次主设备操作的地址,并前根据该地址准备第二次操作数据。

在时钟上升沿2,主设备检测到ACK_I为高,将DAT_I()采样完成第一次操作,并发起第二次操作。从设备获得第二次操作的数据并将其放到DAT_O()并开始准备第三次操作的数据。

在时钟上升沿3,主设备检测到ACK_I为高,将DAT_I()采样完成第二次操作,并发起第三次操作。从设备获得第三次操作的数据并将其放到DAT_O()并开始准备第四次操作的数据。

在时钟上升沿4,主设备检测到ACK_I为高,将DAT_I()采样完成第三次操作,并发起第四次操作,并将CTI()置为3’b111表示本次地址增加突发操作即将结束。从设备获得第四次操作的数据并将其放到DAT_O()并开始准备第五次操作的数据。

在时钟上升沿5,主设备检测到ACK_I为高,将DAT_I()采样完成第四次操作。从设备检测到CTI()为3’b111,刚刚获取的为第五次操作准备的数据被抛弃。

Openrisc wishbone bus introduction 19.png

图20 数据总线为32位粒度为8的叠4地址增加突发操作示例

Wishbone规范对IP文档的要求

为了简化设计复用,Wishbone规范要求遵守Wishbone规范的IP必须同时给出Wishbone规范要求的文档。文档的作用是帮助用户理解该IP的操作以及如何将该IP核与其他IP互联。

遵守Wishbone规范的IP的文档中必须写明:

  • 该IP所遵守的Wishbone规范的版本。
  • 接口的类型:主设备接口还是从设备接口。
  • 接口的信号名。如果信号名与规范中定义的信号名不同,必须给出对照表。
  • 如果主设备支持RTY_I/ERR_I,必须给出当RTY_I/ERR_I有效时,主设备是如何响应的;如果从设备支持RTY_O/ERR_O,必须给出从设备在什么条件下才能置高RTY_O/ERR_O。
  • 如果接口支持标签,必须给出标签名字、类型和标签的使用方法。
  • 接口的数据总线粒度、宽度,地址总线宽度。
  • 数据组织方式是大端还是小端。
  • 数据的传输顺序,如果不规定接口的数据传输顺序,该项为未定义。
  • 对CLK_I的要求,包括对频率的要求、对边沿的要求,对门控时钟的要求。

Wishbone从设备接口示例

一个8比特从设备

前文曾经指出,Wishbone总线规范是"轻量级(Lightweight)"规范,它实现起来非常简单紧凑,接口需要的互联逻辑非常少。这里给出一个Wishbone从设备的一个例子,如图21所示。该从设备由一个与门和8个D触发器构成。在写周期,当STB_I和WE_I同时有效,数据DAT_I[7:0]在时钟CLK_I的上升沿被写到触发器中。

Openrisc wishbone bus introduction 20.png

图21 一个简单的Wishbone从设备

该从设备的RTL代码如下:

module slave8bit(input CLK_I, //这里使用了Verilog 2001语法
input RST_I,
input [7:0] DAT_I,
input STB_I,input WE_I,
output reg[7:0] DAT_O,output ACK_O);
always @(posedge CLK_I or posedge RST_I ) begin
if(RST_I) begin
DAT_O<=8'h00;
end
else if(STB_I&WE_I)begin
DAT_O<=DAT_I;
end
end
assign ACK_O=STB_I;
endmodule

该从设备的Wishbone文档如表5。

表5 从设备的Wishbone文档
描述 规范
功能 8比特从设备
支持的总线周期类型 从设备读写
从设备块读写
从设备RMW操作
数据端口宽度
数据端口粒度
数据端口最大操作数尺寸
数据传输顺序
8
8
8
大端或者小端
支持的信号及其对应WISHBONE信号列表 align = "center"
信号名 WISHBONE等效
ACK_O ACK_O
CLK_I CLK_I
DAT_I[7:0] DAT_I()
DAT_O[7:0] DAT_O()
RST_I RST_I
STB_I STB_I
WE_I WE_I

一个32比特RTL级随机数生成器从设备

下面我们举一个实用一点的例子,一个随机数生成器。随机数生成理论和随机数生成器模块的RTL代码见附录2。该模块端口定义如下:

module rng(clk,reset,loadseed_i,seed_i,number_o);

number_o为随机数输出,当loadseed_I有效时,种子seed_I被送入number_o成为随机数的第一个值。

我们的目的是将其包装成WISHBONE兼容的从模块,其Verilog RTL代码如下:

//Rng_wbc.v
module Rng_wbc(input CLK_I,
input RST_I,
input [31:0] DAT_I,
input STB_I,input WE_I,
input [3:0] SEL_I,
output [31:0] DAT_O,output ACK_O);
assign ACK_O=STB_I;
wire loadseed=WE_I&STB_I&(|SEL_I);
wire [31:0] seed;
 
assign seed[7:0]  =SEL_I[0]?DAT_I[7:0]  :8'h00;
assign seed[15:8] =SEL_I[1]?DAT_I[15:8] :8'h00;
assign seed[23:16]=SEL_I[2]?DAT_I[23:16]:8'h00;
assign seed[31:24]=SEL_I[3]?DAT_I[31:24]:8'h00;
 
wire [31:0]  rand_number;
 
assign DAT_O[7:0]  =SEL_I[0]?rand_number[7:0]  :8'h00;
assign DAT_O[15:8] =SEL_I[1]?rand_number[15:8] :8'h00;
assign DAT_O[23:16]=SEL_I[2]?rand_number[23:16]:8'h00;
assign DAT_O[31:24]=SEL_I[3]?rand_number[31:24]:8'h00;
//随机数生成器的莉化
rng unit_rng(.clk(CLK_I),.reset(RST_I),.loadseed_i(loadseed),.seed_i(seed),.number_o(rand_number));
endmodule

该从设备的Wishbone文档如表6。

表6 从设备的Wishbone文档
描述 规范
功能 随机数生成器,32比特从设备
支持的总线周期类型 从设备读写
从设备块读写
从设备RMW操作
数据端口宽度
数据端口粒度
数据端口最大操作数尺寸
数据传输顺序
32
8
32
大端或者小端
支持的信号及其对应WISHBONE信号列表
align = "center"
信号名 WISHBONE等效
ACK_O ACK_O
CLK_I CLK_I
DAT_I[7:0] DAT_I()
DAT_O[7:0] DAT_O()
RST_I RST_I
STB_I STB_I
WE_I WE_I
SEL_I[3:0] SEL_I()

Wishbone对RAM/ROM的支持

WISHBONE与RAM和ROM的互联

遵守Wishbone规范的IP可以与任何类型的RAM和ROM互联,只是对于不同的RAM和ROM,接口的复杂度不同,工作效率也不同。Wishbone规范在设计时更多的考虑了对典型RAM和ROM的高效支持,而这些RAM/ROM在FPGA和ASIC片内是普遍使用的。

Wishbone主设备接口与RAM的连接示例如图22。典型的同步RAM具有一个时钟输入,一个写使能信号WE,地址信号以及数据输入DIN、数据输出信号DOUT,有时还包括片选信号CE。 图22的左图表示RAM写操作。在时钟上升沿0,主设备将ADR_O()、DAT_O()置为合适的值,将WE_O置高表示写操作;在时钟上升沿1,DAT_O()被写到ADR_O()对应的RAM位置。图22的右图表示RAM读操作。在时钟上升沿0,主设备将ADR_O()置为合适的值,将WE_O置低表示读操作;在时钟上升沿1,DAT_O()被主设备采样。

Openrisc wishbone bus introduction 21.png

  • 同步写周期

Openrisc wishbone bus introduction 22.png

(b) 异步读周期
图22 Wishbone主设备接口与同步RAM的连接

Wishbone主设备接口与ROM的连接和时序更加简单,只需要地址输入ADR()和数据输出DOUT(),时钟信号都不需要,如图23所示。

Openrisc wishbone bus introduction 23 392.png

图23 Wishbone主设备接口与ROM的连接

WISHBONE兼容的RAM和Flash仿真模型

在Or1200的验证中我们将使用到WISHBONE兼容的RAM和Flash模型,因此,我们在这里给出所使用的RAM和Flash的源码。

RAM的大小为2M字节,是实际FPGA或者ASIC RAM如idt71256sa15 SRAM的等效行为模型,其源码如下:

module sram_top (
wb_clk_i, wb_rst_i,
wb_dat_i, wb_dat_o, wb_adr_i, wb_sel_i, wb_we_i, wb_cyc_i,
wb_stb_i, wb_ack_o, wb_err_o,
);
……
parameter		aw = 19;
……
reg     [7:0]           mem [2097151:0];//RAM为2MB,因此地址宽度aw = 19
//地址不能大于2M。
assign wb_err_o = wb_cyc_i & wb_stb_i & (|wb_adr_i[23:21]);
assign adr = {8'h00, wb_adr_i[23:2], 2'b00};
 
//SRAM读逻辑
assign wb_dat_o[7:0] = mem[adr+3];
assign wb_dat_o[15:8] = mem[adr+2];
assign wb_dat_o[23:16] = mem[adr+1];
assign wb_dat_o[31:24] = mem[adr+0];
 
//写逻辑
always @(posedge wb_rst_i or posedge wb_clk_i)
if (wb_cyc_i & wb_stb_i & wb_we_i) begin
if (wb_sel_i[0])
mem[adr+3] <= #1 wb_dat_i[7:0];
if (wb_sel_i[1])
mem[adr+2] <= #1 wb_dat_i[15:8];
if (wb_sel_i[2])
mem[adr+1] <= #1 wb_dat_i[23:16];
if (wb_sel_i[3])
mem[adr+0] <= #1 wb_dat_i[31:24];
end
 
assign wb_ack_o = wb_cyc_i & wb_stb_i & ~wb_err_o;
endmodule

所使用的Flash实际上是一个只读的Flash,模型仿真了典型的Nor Flash如INTEL 28f016s3 FLASH的读时序,而忽略了写时序。主设备每读32比特的数据需要4个时钟周期。其源代码列出如下:

module flash_top (
wb_clk_i, wb_rst_i,
 
wb_dat_i, wb_dat_o, wb_adr_i, wb_sel_i, wb_we_i, wb_cyc_i,
wb_stb_i, wb_ack_o, wb_err_o
);
//内部数据线和寄存器
reg	[7:0]		mem [2097151:0]; //2M字节
wire	[31:0]		adr;
reg			wb_err_o;
reg	[31:0]		prev_adr;
reg	[1:0]		delay;
wire			wb_err;
 
//
assign adr = {8'h00, wb_adr_i[23:2], 2'b00};
 
//FLASH的初始化
initial $readmemh("../src/flash.in", mem, 0);
 
//读模型,数据被一次赋值
assign wb_dat_o[7:0] = wb_adr_i[23:0] < 65535 ? mem[adr+3] : 8'h00;
assign wb_dat_o[15:8] = wb_adr_i[23:0] < 65535 ? mem[adr+2] : 8'h00;
assign wb_dat_o[23:16] = wb_adr_i[23:0] < 65535 ? mem[adr+1] : 8'h00;
assign wb_dat_o[31:24] = wb_adr_i[23:0] < 65535 ? mem[adr+0] : 8'h00;
// delay逻辑,该逻辑的目的是每次发生读操作,都在4个周期后产生应答信号wb_ack_o
always @(posedge wb_clk_i or posedge wb_rst_i)
if (wb_rst_i) begin
delay <= #1 2'd3;
prev_adr <= #1 32'h0000_0000;
end
else if (delay && (wb_adr_i == prev_adr) && wb_cyc_i && wb_stb_i)
delay <= #1 delay - 2'd1;
else if (wb_ack_o || wb_err_o || (wb_adr_i != prev_adr) || ~wb_stb_i) begin
delay <= #1 2'd0;	// delay ... can range from 3 to 0
prev_adr <= #1 wb_adr_i;
end
assign wb_ack_o = wb_cyc_i & wb_stb_i & ~wb_err & (delay == 2'd0)	& (wb_adr_i == prev_adr);
//总线操作错误信号,错误产生的原因是输入地址值过大
assign wb_err = wb_cyc_i & wb_stb_i & (delay == 2'd0) & (|wb_adr_i[23:21]);
always @(posedge wb_clk_i or posedge wb_rst_i)
if (wb_rst_i)
wb_err_o <= #1 1'b0;
else
wb_err_o <= #1 wb_err & !wb_err_o;
endmodule

Wishbone点到点连接示例

Wishbone点到点连接在ASIC和FPGA设计中非常常见。如图24,这是一个典型的点到点连接示意图。在图中,数据的粒度是32比特,每次操作中数据总是同时全部有效,因此可以省略SEL_O()信号。点到点连接中CYC_O信号也是没有用的,因为该信号用于多主设备的仲裁请求。

Openrisc wishbone bus introduction 24.png

图24 Wishbone点到点连接示例

Wishbone共享总线连接示例

Wishbone共享总线连接与点到点连接同样重要。在本例中,两个主设备和两个从设备通过SYSCON实现了共享总线连接,在后续章节中,我们将使用该例子对Or1200进行基本RTL验证。系统复位后,一个或者多个主设备通过置高CYC_O请求使用总线,仲裁器(后文将讨论)选择其中一个主设备#X(X在0和N之间),只将该主设备对应的GNTX信号置高,而将其它GNT信号置低以通知INTERCON哪一个主设备可以使用总线。

一旦主设备选定,其输出信号将通过选择器送到共享总线进而送到各个从设备。例如,如果主设备#0被中可以使用总线,则器ADR()、DAT_O()、SEL_O()、WE_O和STB_O信号将被送到各个从设备。与此同时请求使用总线的其他设备,将永远接收不到总线周期结束信号ACK_I/ERR_I/RTY_I。

从设备是如何被选择的呢 '若系统中有M个从设备,则地址线被地址译码器分为M个部分,别代表每一个从设备的地址空间。从设备#Y对应的译码输出信号与共享总线上的STB_I信号相与,如果STB_I信号和从设备#Y对应的译码输出信号同时为高,从设备#Y才发起对主设备操作的响应。

各个从设备输出的总线周期结束ACK_O/ERR_O/RTY_O被通过或门送到各个主设备的ACK_I/ERR_I/RTY_I。而从设备的DAT_O()输出则通过选择器送到各个主设备的输入端,这个选择器的输入为共享地址总线。

Openrisc wishbone bus introduction 25.png

图25 基于选择器的Wishbone共享总线连接示例

intercon模块的源码如下:

module intercon2M2S(clk_i, rst_i,	wb0s_data_i, wb0s_data_o, wb0_addr_i, wb0_sel_i, wb0_we_i, wb0_cyc_i,
wb0_stb_i, wb0_ack_o, wb0_err_o, wb0_rty_o,
wb0m_data_i, wb0m_data_o, wb0_addr_o, wb0_sel_o, wb0_we_o, wb0_cyc_o,
wb0_stb_o, wb0_ack_i, wb0_err_i, wb0_rty_i,
 
wb1s_data_i, wb1s_data_o, wb1_addr_i, wb1_sel_i, wb1_we_i, wb1_cyc_i,
wb1_stb_i, wb1_ack_o, wb1_err_o, wb1_rty_o,
wb1m_data_i, wb1m_data_o, wb1_addr_o, wb1_sel_o, wb1_we_o, wb1_cyc_o,
wb1_stb_o, wb1_ack_i, wb1_err_i, wb1_rty_i
);
……//这里忽略了端口定义
 
//仲裁器,两个主设备,两个从设备
wire GNT0,GNT1,CYC;
arbiter2M2S arbiter2M2S(.CLK(clk_i),.RST(rst_i),.CYC0(wb0_cyc_i),.CYC1(wb1_cyc_i),
.GNT0(GNT0),.GNT1(GNT1),.CYC(CYC));
 
//地址译码器
wire ACMP0,ACMP1;
wire [31:0] shared_address;
address_decoder2S address_decoder2S(.addr_i(shared_address),.ACMP0(ACMP0),.ACMP1(ACMP1));
//共享总线周期有效信号CYC,从各主设备到各从设备
assign wb0_cyc_o=CYC;
assign wb1_cyc_o=CYC;
 
//共享地址信号
assign shared_address=GNT1?wb1_addr_i:wb0_addr_i;
assign wb0_addr_o=shared_address;
assign wb1_addr_o=shared_address;
 
//从各主设备到各从设备的共享数据总线
wire [31:0] shared_data_m2s=GNT1?wb1m_data_i:wb0m_data_i;
assign wb0s_data_o=shared_data_m2s;
assign wb1s_data_o=shared_data_m2s;
 
//从各从设备到各主设备的共享数据总线
wire [31:0] shared_data_s2m=ACMP1?wb1s_data_i:wb0s_data_i;
assign wb0m_data_o=shared_data_s2m;
assign wb1m_data_o=shared_data_s2m;
 
//从各主设备到各从设备的共享选择信号
wire [3:0] shared_sel=GNT1?wb1_sel_i:wb0_sel_i;
assign wb0_sel_o=shared_sel;
assign wb1_sel_o=shared_sel;
 
//从各主设备到各从设备的共享写使能信号
wire shared_we=GNT1?wb1_we_i:wb0_we_i;
assign wb0_we_o=shared_we;
assign wb1_we_o=shared_we;
 
//从各主设备到各从设备的共享选通信号
wire shared_stb=GNT1?wb1_stb_i:wb0_stb_i;
assign wb0_stb_o=ACMP0?shared_stb:1'b0;
assign wb1_stb_o=ACMP1?shared_stb:1'b1;
 
//从各从设备到各主设备的应答信号
wire shared_ack=ACMP0?wb0_ack_i:wb1_ack_i;
assign wb0_ack_o=shared_ack;
assign wb1_ack_o=shared_ack;
wire shared_err=ACMP0?wb0_err_i:wb1_err_i;
assign wb0_err_o=shared_err;
assign wb1_err_o=shared_err;
wire shared_rty=ACMP0?wb0_rty_i:wb1_rty_i;
assign wb0_rty_o=shared_rty;
assign wb1_rty_o=shared_rty;

地址译码

在很多总线标准中,从设备必须译码所有的地址,这称为全地址译码。比如在PCI总线中,每一个从设备都必须具有32位的地址输入,并对这32位的地址进行译码以确定如何响应主设备的请求。

片上总线规范比如Wishbone规范支持部分地址译码。在部分地址译码总线规范中,从设备只需要译码自己所使用的地址空间。比如如果一个从设备只有4个地址,它就只需译码2比特地址而不是32位地址。这样做的好处是:地址译码速度快,地址译码逻辑少,从设备可自由定义地址总线宽度。

Openrisc wishbone bus introduction 26.png

图26 Wishbone部分地址译码的实现

Wishbone部分地址译码的实现如图26所示。对于图中所示IP核,我们假设其只有4个地址,对应4组寄存器。地址译码器首先译码出其地址并给出选择信号,该选择信号与共享总线的STB_O相与输入到IP核的STB_I。而地址总线的最低2位被直接连接到IP核,IP核再根据这两位译码出当前操作选中的是具体哪个寄存器。

Wishbone共享总线示例中的仲裁器可以设计如下:

//the ACMP0 is for slave0: Flash
//the ACMP1 is for slave1: SRAM
module address_decoder2S(addr_i,ACMP0,ACMP1);
input [31:0]addr_i;
output ACMP0,ACMP1;
parameter ADDR_LOW_S0=32'b0;
parameter ADDR_HIGH_S0=32'h1FFFFF;//2M space for Slave 0;
//the rest of the space for Slave 1,so we do not need a dummuy slave;
assign ACMP0=addr_i<=ADDR_HIGH_S0;
assign ACMP1=~ACMP0;
endmodule

仲裁器的设计

在存在多个主设备的共享总线片上系统中,仲裁器是必不可少的。它决定哪个主设备可以使用总线。所有的主设备通过置高CYC_O信号向仲裁器请求使用总线,仲裁器则根据用户自定义的优先级算法确定哪个主设备可以使用总线。仲裁器的输出信号GNT0~GNTN(或者同时输出更加简洁的GNT()信号作为选择器的输入,如图24中的例子)对应N个不同的主设备。GNT[N-1…0]用于选择器的选择输入端,以选择各主设备和从设备对应的输入信号。需要指出,二输入与门是最简单的选择器,它或者选择一个信号,或者一个信号也不选。

优先级算法最常用的是循环优先级(Round robin)和固定优先级相结合。所谓循环优先级是指各个主设备的优先级安排按照FIFO的方式组织,排到队头的主设备先使用总线,使用完总线后该主设备被插入到FIFO的队尾。

常见的片上系统的主设备包括一个处理器,一个液晶显示屏控制器,若干DMA引擎。一般地,液晶显示屏控制器的优先级固定是最高的,DMA引擎的优先级次之,而处理器的优先级最低。与此同时,处理器是缺省的主设备,也就是说,当没有其他主设备申请使用总线时,总线的使用权归处理器。DMA引擎存在多个,它们之间采用循环优先级,表示它们在系统中具有同等的地位。高优先级的主设备使用一次总线只完成一次单次操作或者块操作,然后释放总线,并试图再次获得总线。这样做的目的是让出总线给其他主设备插入更加重要的操作。一次块操作的子操作次数的典型值为4或者8,只在特殊情况下才使用16或者更大。

因而,图25中的Wishbone共享总线示例中的仲裁器可以设计如下:

//CYC0 is the default master, but it has lower priority.
module arbiter2M2S(input CLK,input RST, input CYC0,input CYC1,
output reg GNT0,output reg GNT1,output CYC);
always @(posedge CLK or posedge RST) begin
if(RST) {GNT0,GNT1}<=2'b10;
if(CYC1 & !CYC0) {GNT0,GNT1}<=2'b01;
else if(!CYC1) {GNT0,GNT1}<=2'b10;
end
assign CYC=GNT1?CYC1:CYC0;
endmodule

本章小节

本章详细讲述了Wishbone总线标准。该标准为"轻量级"片上总线标准,既适合做复杂的片上总线,又适合做IP内部的模块互联。与ARM公司的AMBA总线规范的AHB总线相比,一个明显的区别是写操作时Wishbone总线同时给出地址和数据,而AHB则是在连续的两个周期内前一个周期给出地址,下一个周期给出数据,因此写操作需要至少两个周期完成。对于读操作,两者的区别是Wishbone总线可以在一个周期内完成读操作,而AHB则最快允许在地址周期的下一周期给出应答和数据。但实际上,AHB支持流水线操作,而Wishbone总线在支持寄存反馈总线周期以前则不能,从这个角度,对于随机的总线读写,两个总线的效率是一样的。AHB总线是完全的同步时序,更加复杂,在突发操作时效率与Wishbone寄存反馈总线周期一样。AHB总线更加严谨且功能丰富,Wishbone则更加灵活,更加简单,满足了开源处理器所定义的应用的需求。

目前,主流的IP都采用AMBA总线标准,但是实际上,ARM公司从未声明该标准是免费的,而Wishbone总线标准则是真正的免费标准。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多