数字IC设计总线系列之APB实例解析

APB是AMBA中相对比较简单的接口协议。

采用这种简单的协议,你可以轻松地将自定义外设挂在AMBA总线上。

许多APB外设都是慢速器件,例如UART。一般SoC都是通过它们的寄存器进行访问。

下面是一个APB slave 的verilog实例,大家可以在此基础上,设计自己APB slave接口的自定义模块控制器.

// Sample APB register code
// Standard read/write registers
// Adress offset from psel:
//  0x00 : 32 bit read of status32 port
//  0x04 : 32 bit read & write control32 port
//  0x08 : 16 bit status
//         and 16 bit read & write control16 port
//  0x0C : 8 bit status8
//          and 8 bit read & write control8port
module apb_regs1 (
   //system
   //APB
   //Interface
);
...
endmodule


其中,

// system
  input             reset_n,
  input             enable,  // clock gating

是系统复位和使能信号

// APB
  input             pclk,           
  input      [ 3:0] paddr,   // ls 2 bits are unused 
  input             pwrite,
   input             psel,
  input             penable,
  input      [31:0] pwdata,
  output reg [31:0] prdata,
  output            pready,
  output            pslverr,


是APB slave的接口信号

// Interface
  input      [31:0] status32,
  input      [15:0] status16,
  input      [ 7:0] status8,
  output reg [31:0] control32,
  output reg [15:0] control16,
  output reg [ 7:0] control8

是一个自定义模块的寄存器,其中status32是read_only

wire apb_write = psel & penable &pwrite;
wire apb_read  = psel & ~pwrite;


apb_write和apb_read是为了满足APB协议做的读写控制。apb_read信号产生和apb_write不同,具体原因可以查阅APB协议官方文档

assign pready = 1'b1;
assign pslverr = 1'b0;

该APB slave模块只是对一些控制和状态寄存器进行读写,是无等待传输,同时不生成传输错误信号。

always @(posedge pclk or negedge reset_n)
  begin
     if (!reset_n)
     begin
        control32 <= 32'h0;
        control16 <= 16'h1234; // reset/initial value
        control8  <=  8'h0;
        prdata    <= 32'h0;
     end // reset
     else if (enable)
     begin
        if (apb_write)
        begin
           case (paddr)
         //4'h0 :  status32 read only 
           4'h4 : control32 <= pwdata;
           4'h8 : control16 <= pwdata[15:0];
           4'hC : control8  <=pwdata[7:0];
           endcase
        end // write
        if (apb_read)
        begin
           case (paddr)
           4'h0 : prdata <= status32;
           4'h4 : prdata <= control32; 
           4'h8 : prdata <= {status16,control16};
           4'hC : prdata <={8'h0,status8,8'h0,control8};
           endcase
        end // read
        else
           prdata <= 32'h0; // so we can OR all busses
     end // clocked
  end // always

对不同的寄存器做了地址分配,其中status32寄存器只读

然后我们在Testbench里例化APB slave和一个APB master 模型,对该APB slave模块进行验证。

apb_bus0.read(16'h00,32'h9c4e9a31);      
apb_bus0.write(16'h04,32'h11223344);
apb_bus0.write(16'h08,32'hAABB);
apb_bus0.write(16'h0C,32'hDD);
apb_bus0.read(16'h04,32'h11223344);
apb_bus0.read(16'h08,32'h7832AABB);
apb_bus0.read(16'h0C,32'h002a00DD);

发布于 2019-09-28

文章被以下专栏收录