之前老李問過大家想看哪方面的知識,有不少同學提議老李寫寫總線,特別是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避免沖突。
-
AMBA
+關注
關注
0文章
69瀏覽量
15042 -
AHB
+關注
關注
0文章
23瀏覽量
9837 -
AXI總線
+關注
關注
0文章
66瀏覽量
14313
發布評論請先 登錄
相關推薦
評論