电子说
之前老李问过大家想看哪方面的知识,有不少同学提议老李写写总线,特别是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把它的三个协议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在同一个周期返回。
我们看到,APB的读和写不能同时进行,每一个transaction要么是读,要么是写,由PWRITE来控制。每一个transaction至少需要2个周期。这两点导致了APB在AMBA三兄弟中是最低性能的,但是好处是协议简单,Master和Slave的设计都只需要很少的逻辑。甚至在Spec里ARM都帮你画好了状态机(至于AHB和AXI, ARM的Spec就没有帮你画状态机了,你得自己设计)。
APB通常是作为Peripheral IP的寄存器读写总线接口,有些IP可能年头很久,速度很慢,所以即使给了一个周期去setup,也不能够在第二个周期确保完成读或写,所以slave可以选择在没有准备好的时候不assert PREADY,插入wait cycle。咱们再放两个有wait state的时序图。
有wait state的写时序图
有wait state的读时序图
这和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的协议要求。
这里面我们一个信号一个信号来看,先来看最关键的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避免冲突。
全部0条评论
快来发表一下你的评论吧 !