AMBA总线那些事之APB

电子说

1.3w人已加入

描述

之前老李问过大家想看哪方面的知识,有不少同学提议老李写写总线,特别是AMBA的总线,所以老李决定从这期开始开始一个AMBA总线介绍的系列文章。AMBA总线主要包括三种最常见的协议APB, AHB, AXI,它们的复杂度、性能和设计难度都是递增的。在网上介绍三种协议的文章有不少,绝大多数都是把ARM的协议文档翻译一下,但是很少有人来系统介绍总线中的不同功能模块的设计,特别是微架构层面的设计逻辑。所以老李决定要提供给大家一些不一样的内容,在介绍协议基本内容的基础上,加入一些总线模块的设计介绍,目的是让大家有个更加直观和具体的理解。当然老李的水平也有限,有不少内容也需要学习,有不足的地方也欢迎大家批评指正。

咱们首先来介绍AMBA(Advanced Microcontroller Bus Architecture)家族中最简单的协议APB -- Advanced Peripheral Bus。其实整个AMBA家族的协议也都有些年头了,1998年的时候就发布了ABMA2, 2003年发布了ABMA3 APB 1.0版,2010年发布了AMBA 3 APB的2.0版。因为AMBA2太过久远,现在主流的SoC里绝大多数IP都支持的是AMBA3 的APB。之后老李的介绍也是基于ABMA 3的APB1.0。

废话不多说,先说APB能干啥,对于Bus,最核心的功能就是读和写,搞清楚有哪些控制信号和读写时序是最基本的。APB的协议非常简单,控制信号也很少,咱们直接上时序图理解。

写时序

AMBA

这里先插一句,AMBA把它的三个协议APB, AHB, 和AXI的信号命名都很有规范,APB的信号都是以P开头,AHB的信号都是以H开头,AXI的信号却不是以X开头,而是以A开头,这种命名方式几乎成为了在设计AMBA总线模块时候的潜规则,虽然ARM没有规定你一定要用这种方式命名你的信号,但是在大家设计不同模块和接口的时候,大家基本都遵循这种命名规则,这样IP的使用者一眼就明白信号的用途,方便你我方便大家。

PCLK是总线时钟,在上面的图中,Master驱动的是PADDR, PWRITE, PSEL, PENABLE, PWDATA,而Slave只驱动PREADY。PADDR是transaction的地址,PWRITE用来区分是write还是read,如果是write transaction就是1,是read就是0。PWDATA就是write transaction的数据。PWDATA只在write transaction起作用。

然后就是非常重要的PSEL, PENABLE以及PREADY信号,这3个信号之间的关系标志着一次transaction的开始和结束:

PSEL从0变为1,表明要开始一个新的transaction。

PSEL为1的第一个周期,PENABLE要为0,然后在下一个周期变为1。

当Slave可以确保在下一个时钟沿接受到PWDATA时,就可以把PREADY变为1。

当Master看到PREADY为1后,就可以在下一个时钟沿把PSEL,PENABLE拉为0,结束transaction。

注意:

PWDATA,PADDR, PWRITE需要在PSEL为高一直到PREADY为高期间都保持稳定。

PENABLE必须要在PSEL为高的第一个周期为0(称为setup phase),第二个周期为1,直到PREADY为1(称为access phase)。

如果Master想要在PREADY为高之后下一个沿立刻开始新的transaction,可以不拉低PSEL,而使得PSEL继续为1,但是一定要把PENABLE拉低。

然后再来看看读的时序,其中PSEL, PENABLE, PADDR, PREADY都一致,区别只在于PWRITE要为0, 以及是由Slave来返回PRDATA,注意要和PREADY在同一个周期返回。

AMBA

我们看到,APB的读和写不能同时进行,每一个transaction要么是读,要么是写,由PWRITE来控制。每一个transaction至少需要2个周期。这两点导致了APB在AMBA三兄弟中是最低性能的,但是好处是协议简单,Master和Slave的设计都只需要很少的逻辑。甚至在Spec里ARM都帮你画好了状态机(至于AHB和AXI, ARM的Spec就没有帮你画状态机了,你得自己设计)。

AMBA

APB通常是作为Peripheral IP的寄存器读写总线接口,有些IP可能年头很久,速度很慢,所以即使给了一个周期去setup,也不能够在第二个周期确保完成读或写,所以slave可以选择在没有准备好的时候不assert PREADY,插入wait cycle。咱们再放两个有wait state的时序图。

有wait state的写时序图

AMBA

有wait state的读时序图

AMBA

这和valid/ready协议有点类似,即slave不返回PREADY,那么master就要保证PSEL, PENABLE, PADDR, PWRITE,PWDATA稳定,直到PREADY为1。

另外还有个信号PSLVERR (apb slave error),用于从slave向master返回transaction错误,这个错误可以由slave自己来定义,比如是非法地址访问,也可以是访问timeout了,slave无法在timeout到来之前返回有效的值,那么就可以返回PREADY和PSLVERR来通知master,并且避免总线锁死。

AMB3 APB2.0又加了2个信号,PPROT和PPROB,这其实是把AHB和AXI中关于总线transaction的保护和写操作中的按字节部分写入的功能引入APB,咱们就不过多介绍。到AHB和AXI的时候再讲。

关于APB总线协议部分就这么多,老李觉得简单到可以给刚学数字威廉希尔官方网站 设计这门课的本科生拿来当做一个小作业,然后让大家设计一个master和slave应该都是不错的尝试。

协议介绍完了,我们今天来介绍一个小模块:APB Slice。先说说Slice的作用,在SoC中,如果Master和Slave的距离比较远,那么它们之间的bus信号要满足timing就可能有点困难,比如我们上面看到的PSEL, PENABLE, PADDR, PWRITE这些信号从Master出来,要去很远的Slave,那么中间就要加很多的buffer,这引入的buffer delay就可能导致我们希望的timing不满足。这个时候就需要插入slice,给每个控制信号在中间加一级寄存器,把较长的走线缩短。当然插入的slice依然要保持bus的协议标准。简单来说,一个slice就是下面中间的那个模块,站在APB slave的角度,APB slice是它的master,从Slice出来的控制信号需要满足APB的协议要求。

AMBA

这里面我们一个信号一个信号来看,先来看最关键的PSEL

always_ff @(posedge PCLK)

if(!PRESETn)

out_PSEL <= 1'b0;

else if(out_PENABLE && out_PREADY)

out_PSEL <= 1'b0;

else if(in_PSEL && !in_PENABLE)

out_PSEL <= 1'b1;

思路很简单,当APB slave返回了PREADY,那么transaction结束,需要out_PSEL变为0。而当APB master拉起了in_PSEL且in_PENABLE为0时,标志一个新的transaction开始,需要将out_PSEL拉高。

再来看PENABLE,类似的,当out_PSEL为1的时候,还是分setup phase和access phase,当access phase并out_PREADY为1的时候,out_PENABLE要拉回0,out_PREADY为0的时候PENABLE要保持为1。在setup phase则需要把PENABLE拉为1。

always_ff @(posedge PCLK) begin

if(!PRESETn)

out_PENABLE <= 1'b0;

else if(out_PSEL)

if(out_PENABLE && out_PREADY)

  out_PENABLE <= 1'b0;

else if(!out_PENABLE)

  out_PENABLE <= 1'b1;

end

反过来,对于in_PREADY和in_PSLVERR,APB slice则作为APB Master的slave来返回。只有当APB slave返回了PREADY和PSLVERR之后,APB slice才能assert in_PREADY。

always_ff @(posedge PCLK) begin

if(!PRESETn)

in_PREADY <= 1'b0;

else if(in_PREADY)

in_PREADY <= 1'b0;

else if(out_PREADY && out_PENABLE)

in_PREADY <= 1'b1;

end

always_ff @(posedge PCLK) begin

if(!PRESETn)

in_PSLVERR <= 1'b0;

else if(in_PREADY)

in_PSLVERR <= 1'b0;

else if(out_PREADY && out_PENABLE)

in_PSLVERR <= out_PSLVERR;

end

而对于剩下的PADDR, PWRITE以及PRDATA,由于slice的setup phase比master的setup phase晚一拍,那么只需啊在master的setup phase的时候寄存它们的值就行。

always_ff @(posedge PCLK)

if(in_PSEL && !in_PENABLE) begin

out_PADDR <= in_PADDR;

out_PWRITE <= in_PWRITE;

if(in_PWRITE)

 out_PWDATA <= in_PWDATA;

end

always_ff @(posedge PCLK)

if(out_PENABLE && out_PREADY && !out_PWRITE)

in_PRDATA <= out_PRDATA;

我们引入的这个slice的握手是双向的,对于每一个从master发出的transaction,只有当slave返回了out_PREADY之后slice才会返回给master in_PREADY,也就是说,slice的引入使得从master的角度看出去的延时增加了一个周期,换句话说插入了一个气泡。这个我们之前提到过的valid/ready 的ping pong buffer可以避免气泡还是有不同的。但是由于APB通常来说是低速总线,对于transaction多一个周期少一个周期其实不太关心。真正需要关心性能和访问延迟的地方一般也不会使用APB来作为总线。所以这里引入一个周期的气泡完全可以接受。

下一篇开始老李会再带领大家看看当一个Master要访问多个slave或者多个master要同时访问一个slave时我们需要什么样的APB模块来疏导各个transaction避免冲突。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分