APB的全称为Advanced Peripheral Bus,即先进外设接口,该总线主要用于和低速以及低功耗的外设通信,该总线中唯一的主设备为APB Bridge,而其它的外设均为从设备。其中,APB Bridge可以锁存总线所有的地址、数据和控制信号,并通过二级译码来产生APB从设备选择信号,通常APB Bridge本身挂在AHB等高速总线上,用于桥接低速的APB总线。

APB规定所有的信号必须在时钟上升沿触发时进行传递

APB总线最大支持32bit的数据位宽,拥有两个独立的数据通道,分别为读通道和写通道。由于APB的两个通道没有独立的握手信号,因此两个通道不会被同时使用。

1、APB信号定义如下:

大体可以分为以下三组:

  1. 系统信号:PCLK(系统时钟)、PRESETn(系统复位,低有效);
  2. master信号:PADDR(地址信号,确定读写的地址)、PSELx(片选信号,拉出来接给搭载APB总线的slave,选中slave时,PSELx信号拉高)、PNEABLE(使能信号,在PSELx拉高一个周期后,必定拉高)、PWRITE(写使能信号,PWRITE为高时写有效,为低时读有效)、PWDATA(写数据);
  3. slave信号:PREADY(ready为高时,代表着一次APB数据传输的结束)、PRDATA(读数据)、PSLVERR(错误数据,由slave发出,具体逻辑由slave内部决定,当slave发现内部逻辑出现故障,譬如状态机状态出错、计数器数字异常等,slave都可以使用内部逻辑把该信号拉高,使得master接收到PSLVERR为高时,哪怕ready拉高表示APB结束了,也可以使master放弃该次传输或做出其他应对策略)。

2、APB写传输

如文档所示,APB的写分为两种情况:没有等待状态的写;有等待状态的写。

  • 没有等待状态的写

        对于APB协议来说,最快的写入或者读出一个数据的周期是两周期,先给地址,再写数据;或者先给地址,再读数据。APB 协议文档中,将上述这种传输方式分为两个阶段(phase),给地址的阶段称为Set up phase;紧接着下一周期PENABLE信号拉高,标志着进入写/读数据的阶段,该阶段称为Access phase。

        一次没有等待状态的写传输如上图所示,计划写数据时,第一周期PSEL拉高,表示选中某个slave,同时给出地址信息Addr1和写入数据信息Data1,紧接着下一周期,PENABLE信号拉高,PREADY信号也拉高,这时数据写入完成。

  • 有等待状态的写

在文档中,对有等待周期的APB写传输描述如上,即:

第一个周期的setup phase和write with no wait没有区别,psel拉高,penable为低;第二周期,penable拉高之后,进入access phase,进入access phase之后,penable不会拉低,直到pready为高标志着一次传输结束时,penable才会随着pready一起拉低。

3、APB读传输

APB的读传输也分为两种情况:没有等待状态的读;有等待状态的读。

  • 没有等待状态的读

        

        一次没有等待状态的读传输如上图所示,读状态和写状态不同,写数据时PWRITE=1,读数据时应该令PWRITE=0计划读数据时,第一周期PSEL拉高,表示选中某个slave,同时给出地址信息Addr1,紧接着下一周期,PENABLE信号拉高,PREADY信号也拉高,这时数据被读出,master接受到读出数据PRDATA。

  • 有等待状态的读

        在文档中,对有等待周期的APB读传输描述如上,即:第一个周期的setup phase和read with no wait没有区别,psel拉高,penable为低;第二周期,penable拉高之后,进入access phase,进入access phase之后,penable不会拉低,直到pready为高,这时数据被读出,master接受到读出数据PRDATA,标志着一次传输结束,penable才会随着pready一起拉低。

4、示例代码:

module apbMBFM #(

    parameter                   ADDR_BITS = 24,

    parameter                   DATA_BITS = 32

) (

    output  reg                     apb_psel                    ,

    output  reg                     apb_pwrite                  ,

    output  reg                     apb_penable                 ,

    output  reg[ADDR_BITS-1:0]      apb_paddr                   ,

    output  reg[DATA_BITS-1:0]      apb_pwdata                  ,

    input   wire[DATA_BITS-1:0]     apb_prdata                  ,

    input   wire                    apb_pready                  ,

    input   wire                    apb_pslverr                 ,

    input   wire                    clk                         ,

    input   wire                    rst_n                        

);

    always @(posedge clk or negedge rst_n) begin

        if (rst_n == 1'b0) begin

            apb_psel <=  1'h0;

            apb_pwrite <=  1'h0;

            apb_penable <=  1'h0;

            apb_paddr <=  24'h0;

            apb_pwdata <=  32'h0;

        end        

    end

    task apb_write;

        input [ADDR_BITS-1:0]  addr;

        input [DATA_BITS-1:0]  wdata;

        begin

            @(posedge clk);  //align clock delay

            apb_penable = 1'h0;

            apb_psel   = 1'h1;  

            apb_pwrite = 1'h1;

            apb_paddr  = addr;

            apb_pwdata = wdata;

            @(posedge clk);

            apb_penable = 1'h1;

            @(posedge clk);

            while (!apb_pready) begin

                @(posedge clk);

            end

            apb_pwrite = 1'h0;

            apb_psel   = 1'h0;

            apb_penable = 1'h0;

            apb_paddr  = 24'h0;

            apb_pwdata = 32'h0;

            //$display("task apb_write addr=%0h, wdata=%0h\n", addr, wdata);

        end

    endtask

    task apb_read;

        input [ADDR_BITS-1:0] addr;

        output [DATA_BITS-1:0] rdata;

        begin

            @(posedge clk);  //align clock delay

            apb_penable = 1'h0;

            apb_psel   = 1'h1;

            apb_pwrite = 1'h0;

            apb_paddr  = addr;

            @(posedge clk);      

            apb_penable = 1'h1;

            @(posedge clk);

            while (!apb_pready) begin

                @(posedge clk);

            end

            rdata = apb_prdata;    

            apb_psel = 1'h0;

            apb_penable = 1'h0;

            apb_paddr = 24'h0;

            //$display("task pb_read addr=%0h, rdata=%0h\n", addr, rdata);

        end

    endtask

5、波形图:

 

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐