电子说
之前老李问过大家想看哪方面的知识,有不少同学提议老李写写总线,特别是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条评论
快来发表一下你的评论吧 !