AMBA APB总线协议
01 Feb 2024 7182字 24分 次 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 |
|
| PSTRB | 1/2/4 | Write strobes | 指示在写传输期间,要更新哪个字节通道 写数据总线的每8bit对应1bitPSTRB 在读传输期间,PSTRB不能跳变 |
||
| APB5 | |||||
| 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拉高。同时(RSEL&PENABLE&PREADY同时为高时)如果之前采集PADDR、PWDATA等信号有问题,则认为写传输有误,所以在此时把PSLVERR也拉高。主机会在下一个上升沿采集到此错误信号,得知写入错误。
读传输也可能出现错误,表示无可读数据。
常见的错误有:
- 非法地址:
- 读/写地址超出范围
- 地址decode错误
- 未对齐访问:
- 半字/字节访问未对齐
- 访问类型错误:
- 对只读寄存器写
- 对只写寄存器读
- 不支持的访问类型
- 只允许安全访问或supervisor write
- 权限错误(APB5)
- PPROT[0]:指令/数据属性违规
- PPROT[1]:特权级违规:User访问特权或控制寄存器
- PPROT[2]:安全域违规:Non-secure master访问Secure-only寄存器
- 内部(NVM、EEPROM、OTP)超时
- 数据范围非法
- 校验/奇偶/ECC错误(读路径)
- 功能未实现:某寄存器存在但功能未开启
- 电源/时钟不可用
- 防火墙拒绝
- 调试/测试/诊断:JTAG锁定、Debug fuse关闭
- 低功耗状态限制:retention-only、deep sleep、某些寄存器不可访问
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来生成一个4096*32的单口SRAM,然后例化:
module spram (
// Clock
input wire clk, // Clock
input wire cs_n, // Chip enable
input wire we_n, // Write enable
input wire [12-1:0] addr, // Address
input wire [32-1:0] din, // Data input
output logic [32-1:0] dout // Data output
);
//=========================================================================
// The time unit and precision of the internal declaration
timeunit 1ns;
timeprecision 1ps;
//=========================================================================
// Inst
SRAM_SP_4096x32 u_SRAM_SP_4096x32(
.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 PPROT_CLASS = 1'b0, // Normal or privileged, PPROT[0]
PPROT_SUCURE = 1'b1, // Secure or non-secure, PPROT[1]
PPROT_TYPE = 1'b0 // Instruction or data, PPROT[2]
)(
// Clock and reset
input wire pclk, // Clock
input wire presetn, // Async reset
input wire [31:0] paddr, // APB Address bus
input wire psel, // APB Select
input wire penable, // APB Enable
input wire pwrite, // APB Write control signal (1=Write
input wire [31:0] pwdata, // APB Write data
input wire [2:0] pprot, // APB Protection type (not used)
input wire [3:0] pstrb, // APB Strobe
output logic [31:0] prdata, // APB Read Data
output logic pready, // APB Ready
output logic pslverr // APB Slave Error
);
//=========================================================================
// The time unit and precision of the internal declaration
timeunit 1ns;
timeprecision 1ps;
//=========================================================================
// Parameter
localparam TCO = 0.6; // Simulate the delay of the register
//=========================================================================
// Signal
logic ram_we_n; // RAM Write Enable
logic ram_cs_n; // RAM Enable
logic [11:0] ram_addr; // RAM Address
logic [31:0] ram_wdata; // RAM Write Data
logic [31:0] ram_rdata; // RAM Read Data
logic [31:0] wdata_mask; // Write data by PTRB mask
logic pprot_err; // PPROT Error
logic slv_err;` // Slave Error
logic addr_err; // Address Error
//=========================================================================
// APB State Machine
enum {
IDLE,
SETUP,
ACCESS
} curr_state,next_state;
always_ff@(posedge pclk, negedge presetn)begin
if(!presetn)
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&&pready)
next_state = IDLE;
else if(psel&&penable&&!pready)
next_state = ACCESS;
else if(psel&&!penable)
next_state = SETUP;
else
next_state = IDLE;
end
default:next_state = IDLE;
endcase
end
//=========================================================================
// Write data mask by PSTRB
always_comb begin
for(int i=0;i<4;i++)begin
if(pstrb[i])
wdata_mask[8*i+:8] = pwdata[8*i+:8];
else
wdata_mask[8*i+:8] = '0;
end
end
//=========================================================================
// Slave Error Generation
assign addr_err = |paddr[31:12];
assign pprot_err = ~((pprot[0] == PPROT_CLASS) && (pprot[1] == PPROT_SUCURE) && (pprot[2] == PPROT_TYPE));
assign slv_err = pprot_err || addr_err;
//assign pslverr = (curr_state == ACCESS) ? slv_err : 1'b0;
always_ff@(posedge pclk, negedge presetn)begin
if(!presetn)
pslverr <= #TCO '0;
else if (curr_state == SETUP)
pslverr <= #TCO slv_err;
else
pslverr <= #TCO '0;
end
//=========================================================================
// APB Signals
assign pready = (curr_state == ACCESS) ? 1'b1 : 1'b0;
assign prdata = ((curr_state == ACCESS)&& ~pslverr) ? ram_rdata:prdata;
//=========================================================================
// SPRAM Control Signals
assign ram_cs_n = (curr_state == SETUP) ? ~psel : 1'b1;
assign ram_we_n = (curr_state == SETUP) ? ~pwrite : 1'b1;
assign ram_addr = (curr_state == SETUP) ? paddr[11:0] :'0;
assign ram_wdata = ((curr_state == SETUP)&& ~slv_err) ? wdata_mask :'0;
//=========================================================================
// Instantiation
// SPRAM Instance
spram u_spram (
.clk (pclk),
.cs_n (ram_cs_n),
.we_n (ram_we_n),
.addr (ram_addr),
.din (ram_wdata),
.dout (ram_rdata)
);
endmodule