AMBA APB总线协议
01 Feb 2024 5662字 19分 次 Digital IC Design打赏作者 CC BY 4.0 (除特别声明或转载文章外)
1 前言
APB(Advanced Peripheral Bus),高级外设总线,主要用于低带宽的周边外设之间的连接,例如UART、IIC等。它的总线架构如下图所示:
可以看出APB Bridge是AMBA APB中的唯一总线主机,APB Bridge也是AMBA中的一个从机。
特点:
- 低带宽;高性能;
- 不支持pipeline、Busrst、Outstanding传输,最快只能Back to back,至少需要两个时钟周期传输;
- 无需等待周期和回应信号;
- 不能读写同时传输,AHB也不行,AXI可以;
- 不支持仲裁,因为是单主多从。
版本:
- AMBA2 APB Specification(APB2)
- AMBA3 APB Protocol Specification v1.0(APB3)
- AMBA APB Protocol Specification v2.0/Issue C(APB4)
- AMBA APB Protocol Specification Issue D/E(APB5)
2 APB interface
Source | Version | Signal | Width | Attribute | Description |
---|---|---|---|---|---|
Global | APB2 | PCLK | 1 | Clock | APB的所有传输在PCLK的上升沿有效 |
PRESETn | 1 | Reset | 低电平有效,通常直接连接到系统总线的复位信号 | ||
APB bridge | APB2 | PADDR | 8/16/32 | Address | 最多32bit |
PSELx | 1 | Select | 每个APB Slave都有一个PSELx信号,由ABP Bridge产生 | ||
PENABLE | 1 | Enable | 指示APB传输的第二及后续周期 | ||
PWRITE | 1 | Direction | 1'b1: 写 1'b0: 读 |
||
PWDATA | 8/16/32 | Write data | 最多32bit,PWRITE为1时由Master产生 | ||
APB4 | PPROT | 3 | Protection type | PPROT[2]: 1=Instruction; 0=Data PPROT[1]: 1=Nonsecure; 0=Secure PPROT[0]: 1=Privileged; 0=Normal<b |
|
PSTRB | 1/2/4 | Write strobes | 指示在写传输期间,要更新哪个字节通道 写数据总线的每8bit对应1bitPSTRB 在读传输期间,PSTRB不能跳变 |
||
Slave interface | APB2 | PRDATA | 8/16/32 | Read data | 最多32bit,PWRITE为0时由Slave产生 |
APB3 | PREADY | 1 | Ready | 表示APB传输完成 | |
PSLVERR | 1 | Transfer error | 传输失败的错误信号 |
3 状态转换
APB协议总共只有三种状态,分别是IDLE,SETUP和ACCESS。
- IDLE:默认空闲状态;
- SETUP:当主机需要传输数据的时候,会把从机对应的PSEL拉高,PENABLE拉低,此时进入SETUP,且只持续一拍,下一拍必进入ACCESS;
- ACCESS:驱动PENBALE拉高,之后会采样从机的PREADY信号,如果为低则持续状态,如果为高则回到IDLE或回到SETUP,这取决于是否还有数据传输。SETUP进入ACCESS时需要以下信号保持不变:
- PADDR;
- PPROT;
- PWRITE;
- PWDATA;
- PSTRB;
- PAUSER;
- PWUSER;
从APB从机角度,状态转换如下图所示:
graph LR
IDLE -->|PSEL = 1; PENABLE = 0| SETUP;
SETUP --> ACCESS;
ACCESS -->|PSEL = 1; PENABLE = 0| SETUP;
ACCESS -->|PREADY = 0| ACCESS;
ACCESS -->|PREADY = 1| IDLE;
4 APB Timing
4.1 写传输
没有等待状态:
第一阶段为IDLE状态。
第二阶段为SETUP,此时主机把PSEL和PWRITE拉高;PADDR和PWDATA准备好地址和数据;把PENABLE拉低表示下一拍实施写入。
第三阶段为ACCESS,主机把PENABLE拉高,表示该数据有效;从机采样到PSEL拉高,会把PREADY拉高表示接收数据;
第四阶段退出ACCESS,如果PREADY为高,代表从机接收到信号,主机则拉低PSEL和PENABLE信号,进入IDLE状态。
没有等待状态的连续写时序:
具有等待状态:
ACCESS可能不止一个周期,取决于从机什么时候回复PREADY信号,因此数据写入完成要大于两个时钟周期。
4.2 读传输
没有等待状态,类似写传输:
具有等待状态,类似写传输:
5 其他信号
5.1 PSTRB
PSTRB可以理解为写入选通,1bit控制PWDATA的8bit是否能写入:
PSTRB | 3 | 2 | 1 | 0 |
---|---|---|---|---|
PWDATA | 31:24 | 23:16 | 15:8 | 7:0 |
读传输时,PSTRB必须全位拉低。
5.2 PSLVERR
PSLVERR表示从机认为写传输有错误。时序图如下:
写传输时,当从机任务可以接受数据时,把PREADY拉高。同时如果之前采集PADDR、PWDATA等信号有问题,则认为写传输有误,所以在此时把PSLVERR也拉高。主机会在下一个上升沿采集到此错误信号,得知写入错误。
读传输也可能出现错误,表示无可读数据。
6 APB SRAM RTL设计
设计一个16bit数据位宽、512字节(9bit地址位宽)的APB协议的SRAM。
6.1 Interface & Architecture
接口符合APB4协议。
Signal | Width | Direction |
---|---|---|
pclk | 1 | I |
preset_n | 1 | I |
paddr | ADDR_WIDTH | I |
psel | 1 | I |
penable | 1 | I |
pwrite | 1 | I |
pwdata | DATA_WIDTH | I |
pprot | 3 | I |
pstrb | DATA_WIDTH/8 | I |
prdata | DATA_WIDTH | O |
pready | 1 | O |
pslverr | 1 | O |
文件结构:
- APB_SRAM
- SRAM
- SRAM_IP
- SRAM
6.2 SRAM
采用ARM Artisan Physical IP来生成一个8*512的单口SRAM,然后例化:
module SPRAM #(
parameter DATA_WIDTH = 8,
ADDR_WIDTH = 9
)(
// Clock
input wire clk, // Clock
input wire cs_n, // Chip select
input wire we_n,
input wire [ADDR_WIDTH-1:0] addr,
input wire [DATA_WIDTH-1:0] din,
output logic [DATA_WIDTH-1:0] dout
);
SRAM_SP_512_8 u_SRAM_SP_512_8(
.CLK(clk),
.CEN(cs_n),
.WEN(we_n),
.A(addr),
.D(din),
.Q(dout),
.EMA(3'b000)
);
endmodule
6.3 APB_SRAM
根据APB协议,把APB读写操作转化为SRAM的读写操作。
module APB_SPRAM#(
parameter DATA_WIDTH = 8,
ADDR_WIDTH = 9
)(
// Clock and reset
input wire pclk, // Clock
input wire preset_n, // Async reset
// APB bus signal
input wire [ADDR_WIDTH-1:0] paddr, // Address
input wire psel, // Select
input wire penable, // Enable
input wire pwrite, // Write/Read
input wire [DATA_WIDTH-1:0] pwdata, // Write data
input wire [2:0] pprot, // Protection type
input wire [(DATA_WIDTH/8)-1:0] pstrb, // Write storbes
output logic [DATA_WIDTH-1:0] prdata, // Read data
output logic pready, // Read ready
output logic pslverr // Transfer error
);
//=========================================================================
// The time unit and precision of the internal declaration
timeunit 1ns;
timeprecision 1ps;
//=========================================================================
// Parameter
localparam TCO = 1.6; // Simulate the delay of the register
//=========================================================================
// Signal
logic [(DATA_WIDTH/8)-1:0] [7:0] wr_mask;
logic [DATA_WIDTH-1:0] wr_data;
logic sram_clk;
logic sram_cs_n;
logic sram_we_n;
logic [ADDR_WIDTH-1:0] sram_addr;
logic [DATA_WIDTH-1:0] sram_din;
logic [DATA_WIDTH-1:0] sram_dout;
//=========================================================================
//
enum {
IDLE,
SETUP,
ACCESS
} curr_state,next_state;
always_ff@(posedge pclk, negedge preset_n)begin
if(!preset_n)
curr_state <= #TCO IDLE;
else
curr_state <= #TCO next_state;
end
always_comb begin
case(curr_state)
IDLE: begin
if(psel && !penable)
next_state = SETUP;
else
next_state = IDLE;
end
SETUP: begin
next_state = ACCESS;
end
ACCESS: begin
if(!psel && !penable)
next_state = IDLE;
else if(psel && !penable)
next_state = SETUP;
else
next_state = ACCESS;
end
default:next_state = IDLE;
endcase
end
assign sram_clk = pclk;
assign sram_cs_n = !psel || penable;
assign sram_we_n = psel?~pwrite:sram_we_n;
assign sram_addr = (next_state == SETUP) ? paddr:sram_addr;
assign wr_data = (next_state == SETUP) ? pwdata:wr_data;
assign prdata = sram_dout;
assign pready = penable;
always_comb begin
for(int i=0;i<(DATA_WIDTH/8);i++)begin
if(pstrb[i])
wr_mask[i] = wr_data[8*i+:8];
else
wr_mask[i] = '0;
end
end
assign sram_din = wr_mask;
genvar k;
generate for(k=0;k<(DATA_WIDTH/8);k++)begin:inst
SPRAM #(
.DATA_WIDTH(8),
.ADDR_WIDTH(9)
) u_SPRAM_0(
.clk(sram_clk), //input, Clock
.cs_n(sram_cs_n), //input, Chip select
.we_n(sram_we_n), //input, 1:read;0:write
.addr(sram_addr), //input, Address
.din(sram_din[k*8+:8]), //input, Write data
.dout(sram_dout[k*8+:8]) //output, Read data
);
end
endgenerate
endmodule