3.5 WISHBONE总线周期
一个总线周期由多个不可分的时钟周期构成,完成单次读/写操作或块读/写操作。总线周期也分为单次读/写周期和块读/写周期。一次块读/写总线周期完成多次数据读/写操作。一般情况下,一次操作由主设备和从设备控制信号间的一次握手,以及同时进行的地址和数据总线的一次传输,块操作表示整个操作需要完成多次数据传送。在总线周期中主设备和从设备预先设定好的共同遵守控制信号握手规则、地址和数据总线的传输规则称作协议。
3.5.1 通用操作
通用操作包括复位操作、操作发起和基本握手。
1.复位操作
复位是数字系统最基本的操作。复位后,系统进入预定的状态。当RST_I信号有效,系统开始复位。由于WISHBONE规范规定的复位是同步复位,因此在复位信号有效后接下来的时钟上升沿,所有信号和寄存器进入预定状态。因此,WISHBONE规范要求RST_I信号有效时间至少要一个时钟周期。在数字系统中,实际上更多采用的是异步复位,而且复位信号的长度一般大于系统电平稳定时间和系统时钟频率稳定时间。如果在WISHBONE接口中使用的是异步复位,需要在文档中说明,因为WISHBONE接口默认的是同步复位。
在图3-5中,只给出了STB_O和CYC_O这两个信号,而没有给出其他信号。这两个信号无效时,所有其他信号没有意义。
图3-5 复位操作
2.操作发起
操作总是由主设备发起,主设备发起的操作可以是单次读写、块读写或RMW操作。当主设备将CYC_O置为高时,一次传输操作开始。当主设备将CYC_O置为低时,主设备的所有其他信号没有意义。从设备只在CYC_I为高时才会对主设备发起的操作进行响应。CYC_O和STB_O可以同时从无效变为有效表示操作开始,CYC_O持续有效直到操作结束,CYC_O和STB_O可以同时从有效变为无效表示操作结束。
因此,在只有一个主设备时,可以将CYC_O和STB_O合并为一个信号,比如在OpenRISC1200的源代码中就广泛地使用了这种方式,信号的名字称作CYCSTB_O。
当存在多个主设备时,一个主设备完成操作后必须及时地将CYC_O信号置为低,以让出总线给其他主设备。
3.基本握手
由于在整个操作周期,CYC_O必须始终保持有效,因此,后文将在给出的图示中忽略该信号,只在必要的时候给出。
握手发生在主设备和从设备之间。握手协议是主设备和从设备在握手时所遵守的共同规则。如图3-6所示,当主设备准备好传输数据,它将STB_O信号置高。STB_O信号一直为高,直到从设备通过置高ACK_O、ERR_O、RTY_O对本次操作发起响应。在图3-6中以主设备的ACK_I信号作为示例,后文也如此。通过握手主设备和从设备不仅可以完成通信,而且可以控制它们之间的通信速率。
图3-6 基本握手协议
如果从设备保证能够在主设备发起操作时及时作出操作成功的响应,其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信号可以合并到一起成为一个信号。
在图3-6中,从STB_O到ACK_I存在一个长组合逻辑路径,在实际系统中很可能成为关键路径。因此,在设计中应尽量保证STB_O是触发器的直接输出。如果从STB_O到ACK_I存在一个长组合逻辑路径延迟不能满足设计的时序要求,可将从设备的ACK_O经过触发器寄存后再输出,从而将长组合逻辑打破,但系统的吞吐量也将因此减小。关于如何既打破长组合逻辑又不影响系统的吞吐量,后文将进行详细讨论。
3.5.2 单次读/写周期
单次读/写操作每次操作只完成一次读或写,是最基本的总线操作方式。但是,WISHBONE主设备或从设备也可以不支持单次读写操作,甚至没有地址和数据总线。
单次读操作如图3-7所示。在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将WE_O置为低表示读操作,将适当的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置,将CYC_O和TGC_O置高表示操作正在进行,将STB_O置高表示操作开始。
图3-7 单次读操作
在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将适当的数据放到主设备的DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。
在时钟上升沿1,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,并将STB_O和CYC_O置为低表示操作完成。从设备发现STB_O置低后,也将主设备的ACK_I置低。
在图3-7中,从设备可以在上升沿0和上升沿1之间插入任意多个等待周期。
单次写操作如图3-8所示。在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将数据信号DAT_O()、TGD_O()放到总线上,将WE_O置为高表示写操作,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将CYC_O和TGC_O置高表示操作正在进行,将STB_O置高表示操作开始。
图3-8 单次写操作
在时钟上升沿1到达之前,从设备检测到主设备发起的操作,将主设备的ACK_I置高作为对主设备STB_O的响应。
在时钟上升沿1,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,将STB_O和CYC_O置为低表示操作完成;从设备发现STB_O置低后,也将主设备的ACK_I置低。
在图3-8中,从设备也可以在上升沿0和上升沿1之间插入任意多个等待周期。
3.5.3 块读周期
块读/写操作每次读或写多个数据。块读/写操作实际上是由顺序的多个单次读/写操作有机地组合而成的。但同时存在多个主设备时,块读/写操作非常有用,一个块读写一般是不能打断的,比如一次DMA。主设备进行一次DMA连续传输4个或8个总线宽度的数据,然后主动释放总线,其后又试图占用总线重新进行DMA,直到所有的数据都传输完毕。这样做的好处是允许其他优先级更高的主设备在两次DMA之间插入操作以完成更加紧急的任务。
块读操作如图3-9所示。块操作能够在每个时钟周期进行一次,而图3-9中所示的是在一次块操作中,主设备和从设备都可以通过插入等待周期控制块操作的速度。一次块操作包括多次子操作。每一次子操作都是块操作的一个阶段,完成一次数据读或写。图3-9中的块操作由5次读操作完成,其过程如下:
图3-9 块读操作
在时钟上升沿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的响应。
在时钟上升沿4,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,完成第三次子操作。主设备同时发起第5次子操作,将新地址信号ADR_O()、新TGA_O()放到总线上,将新的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置。
在时钟上升沿5到达之前,从设备检测到主设备发起的第5次子操作,将适当的数据放到主设备的DAT_I()和TGD_I(),将主设备的ACK_I置高作为对主设备STB_O的响应。
在时钟上升沿5,主设备发现ACK_I信号为高,将DAT_I()和TGD_I()采样,完成第5次子操作。主设备同时发起第6次子操作,将新地址信号ADR_O()、新TGA_O()放到总线上,将新的SEL_O()信号置高通知从设备将数据放在数据总线的适当位置。
在时钟上升沿5过后,从设备检测到主设备发起的第6次子操作,但是由于数据没有准备好,它在新上升沿到达之前将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置低。
3.5.4 块写周期
块写操作的例子如图3-10所示。图3-10中的一次块写操作由5个次写操作完成,具体过程如下:在时钟上升沿0,主设备将地址信号ADR_O()、TGA_O()放到总线上,将数据信号DAT_O()、TGD_O()放到总线上,将WE_O置为高表示写操作,将适当的SEL_O()信号置高通知从设备将数据总线上哪些信号是有效的,将CYC_O和TGC_O置高表示操作正在进行,将STB_O置高表示第一次写操作开始。
图3-10 块写操作
在时钟上升沿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继续置高表示第4次写操作开始。
在时钟上升沿5到达之前,从设备检测到主设备发起的第4次写操作,将主设备的ACK_I置高作为对主设备STB_O的响应。
在时钟上升沿5,从设备将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第4次写操作完成,于是发起第5次操作,将新地址信号ADR_O()、新TGA_O()放到总线上。
在时钟上升沿5之后新的时钟上升沿到达之前,从设备发现因为某些原因暂时无法继续接收数据,因此将ACK_I信号置低,插入等待。
当从设备发现可以继续接收数据,于是在在最后一个等待周期结束且上升沿5到达之前,将DAT_I()和TGD_I()采样;主设备发现ACK_I信号为高,得知第5次写操作完成。并将STB_O和CYC_O置低表示整个块写操作完成。
3.5.5 RMW操作
在操作系统中,进程间有一种重要的同步机制称作信号量机制。信号量即当前可用资源的计数。信号量是一个用来实现同步的整型或记录型变量,除了初始化外,对它只能执行等待和释放这两种原子操作。一个进行对信号量的等待操作是获得信号量的过程,读取当前信号量的值,如果发现有可利用资源,则将信号量减1,否则进入等待状态。一个进行对信号量的释放过程即将信号量加1。一个进程对信号的读取、计算新的信号量值、更新信号量的值这三个步骤是不允许被其他进程打破的,如果被打破,则信号量的值将会发生错误,RMW操作的最大用途在于信号量操作。
一次RMW操作对于总线来说,本质上是两次子操作,一次读,一次写,只不过这两次子操作必须由同一个主设备的完成,且读数据和写数据的地址相同。“改”是不发生在总线上的,它发生在主设备内部。一个RMW操作的例子如图3-11所示,其过程如下:
图3-11 RMW操作
在时钟上升沿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置低。
3.5.6 数据组织
数据组织是指数据的传送顺序。目前常见的32位处理器的数据总线粒度为1字节,在传送时,一个32位数据的最高字节可以放在数据总线的最低8位传送,也可以放在数据总线的最高8位传送,因此出现了大端和小端两种数据组织方法。大端是指一个数据的最高位放在数据总线的最低位传送或放在地址较小的存储器位置存储;小端是指一个数据的最高位放在数据总线的最高位传送或放在地址较高的存储器位置存储。WISHBONE同时支持大端和小端两种数据组织方式。当数据总线的粒度和宽度相同时,大端和小端是一样的。
这两种数据组织方式在一般文献中都可以找到。总线标准只定义接口的通信协议,而数据的组织本质上取决于主设备和从设备的设计。因此,这里不再详细讲述大端和小端这两种数据组织方式,请读者参考其他文献。
有时需要将大端和小端的接口互联起来,实现起来并不复杂。如图3-12所示,这里将数据组织为小端的IP A和数据组织为小端的IP B相连的情形。IP A和IP B的数据总线宽度都是32位,粒度为8位。
图3-12 数据组织大端小端接口互联