1 简介
APB2 :AMBA 2 APB,基本APB协议;
APB3 :AMBA 3 APB,比 APB2 多两个信号(PREADY , PSLVERR);
APB4 :AMBA 4 APB,比 APB3 多两个信号(PPROT , PSTRB );
APB(Advanced Peripheral Bus),高级外设总线。APB总线协议是ARM公司提出的AMBA总线结构之一,是一种片上总线结构。APB主要用于低带宽的周边外设之间的连接,例如UART、IIC等,它的总线架构不像AHB支持多个主模块,在APB里面没有仲裁器,APB 桥是AMBA APB中的唯一总线主机,另外APB桥也是AMBA中的一个从机。其特点:低带宽;高性能;非流水作业,至少需要两个时钟周期传输,且数据均在时钟上升沿变化;无需等待周期和回应信号;控制逻辑简单,只有四个控制信号,且APB上的传输可采用状态机表示。
2. APB 协议
2.1 APB2 interface
Signal | Source | Description |
PCLK | Clock source | Clock. The rising edge of PCLK times all transfers on the APB. |
PRESETn | System bus equivalent | Reset. The APB reset signal is active LOW. This signal is normally connected directly to the system bus reset signal. |
PADDR | APB bridge | Address. This is the APB address bus. It can be up to 32 bits wide and is driven by the peripheral bus bridge unit. |
PSELx | APB bridge | Select. The APB bridge unit generates this signal to each peripheral bus slave.It indicates that the slave device is selected and that a data transfer is required.There is a PSELx signal for each slave. |
PENABLE | APB bridge | Enable. This signal indicates the second and subsequent cycles of an APB transfer. |
PWRITE | APB bridge | Direction. This signal indicates an APB write access when HIGH and an APB read access when LOW. |
PWDATA | APB bridge | Write data. This bus is driven by the peripheral bus bridge unit during write cycles when PWRITE is HIGH. This bus can be up to 32 bits wide. |
PRDATA | lave interface | Read Data. The selected slave drives this bus during read cycles when |
2.2 APB2 Timing
2.1.1 APB2 写传输
- 在T1~T2阶段,所有总线处于IDLE状态。
- 在T2~T3阶段,第一个时钟周期,处在Setup phase状态。T2时刻,PADDR、PWRITE、PWDATA变化,PSEL拉高,即Master把这些数据发送到总线上。
- 在T3~T4阶段,第二个时钟周期,处在Access phase状态。此时PENABLE有效,地址、数据和控制信号在整个ENABLE周期保持有效。T3时刻,这个时候对应的Slave接收到Master发送过来的地址和写控制命令,此时Slave得知Master要准备发数据过来了,做好准备。
- T4时刻,采到PENABLE为低电平(除非当前传输之后紧接着下一次传输),表示Master在这一时刻之前一次写操作已经完成,Master回到IDLE状态,而Slave把数据取走。
注意:为了降低功耗地址信号和写信号将在传输后保持不变,直到下一个传输发生才改变。
2.1.2 APB2 读传输
- 在T1~T2阶段,所有总线处于IDLE状态。
- 在T2~T3阶段,第一个时钟周期,处在Setup phase状态。T2时刻,PADDR、PWRITE、PWDATA变化,PSEL拉高,即Master把这些数据发送到总线上。
- 在T3~T4阶段,第二个时钟周期,处在Access phase状态。T3时刻,此时PENABLE有效,这个时候对应的Slave接收到Master发送过来的地址和读控制命令,此时Slave得知自己要将这个地址反馈给Master了。
- T4时刻,采到PENABLE为低电平,表示Master在这一时刻之前一次读操作已经完成,Master回到IDLE状态。
2.3 APB3 interface
在APB2的基础上增加:
Signal | Source | Description |
PREADY | Slave interface | Ready. The slave uses this signal to extend an APB transfer. |
PSLVERR | Slave interface | This signal indicates a transfer failure. APB peripherals are not required to support the PSLVERR pin. This is true for both existing and new APB peripheral designs. Where a peripheral does not include this pin then the appropriate input to the APB bridge is tied LOW. |
2.4 APB3 Timing
2.4.1 APB3 无等待的写传输
0. T1之前处于IDLE状态;
- T1时刻上升沿:APB把PADDR,PWDATA准备好;拉高PSEL,拉高PWRITE(写),拉低PENABLE进入SETUP状态。
- T2时刻上升沿:拉高PENABLE,开始进入ACCESSS状态。
- T3时刻上升沿:PREADY=0,传输未完成,保持ACCESS状态;PREADY=1,slave已经接收到数据,master可拉低PENABLE,PSEL,进入IDLE状态。
2.4.2 APB3 有等待的写传输
- 在T0~T1阶段,所有总线处于IDLE状态。
- 在T1~T2阶段,第一个时钟周期,处在Setup phase状态。T1时刻,PADDR、PWRITE、PWDATA变化,PSEL拉高,即Master把这些数据发送到总线上。
- 在T2~T3阶段,第二个时钟周期,处在Access phase状态。T2时刻,这个时候对应的Slave接收到Master发送过来的地址和写控制命令,此时Slave得知Master要准备发数据过来了,做好准备。
- T3时刻,采到 PENABLE 为高电平,但是 PREADY 为低电平,表示Master数据还没准备好。
- T5时刻,采到 PENABLE & PREADY 为高电平,表示Master在这一时刻之前一次写操作已经完成,Master回到IDLE状态,而Slave把数据取走。
2.4.3 无等待的读传输
- 在T0~T1阶段,所有总线处于IDLE状态。
- 在T1~T2阶段,第一个时钟周期,处在Setup phase状态。T1时刻,PADDR、PWRITE、PWDATA变化,PSEL拉高,即Master把这些数据发送到总线上。
- 在T2~T3阶段,第二个时钟周期,处在Access phase状态。T2时刻,这个时候对应的Slave接收到Master发送过来的地址和读控制命令,此时Slave得知自己要将这个地址反馈给Master了。
- T3时刻,采到PENABLE & PREADY 为高电平,表示Master这一时刻之前一次读操作已经完成,Master回到IDLE状态。
2.4.4 有等待的读传输
- 在T0~T1阶段,所有总线处于IDLE状态。
- 在T1~T2阶段,第一个时钟周期,处在Setup phase状态。T1时刻,PADDR、PWRITE、PWDATA变化,PSEL拉高,即Master把这些数据发送到总线上。
- 在T2~T3阶段,第二个时钟周期,处在Access phase状态。T2时刻,这个时候对应的Slave接收到Master发送过来的地址和读控制命令,此时Slave得知自己要将这个地址反馈给Master了。
- T3时刻,采到 PENABLE 为高电平,但是 PREADY 为低电平,表示Slave数据还没准备好。
- T5时刻,采到 PENABLE & PREADY 为高电平,表示Master这一时刻之前一次读操作已经完成,Master回到IDLE状态。
2.4.5 有错误的写传输
2.4.6 有错误的读传输
2.5 状态转化
从MASTER角度理解:
从SLAVE角度理解:
3. APB_SRAM RTL实现
首先进行模块划分:apb_sram.v;sram.v;tb.v。
3.1 sram.v 模块RTL实现
Signal | Source | Description |
clk | input | 系统时钟 |
rst_n | input | 复位信号,低电平复位 |
en | input | 使能SRAM信号 |
we | input | 读写使能;1:写;0:读 |
addr | input | 输入地址 |
din | input | 输入写数据 |
dout | output | 读数据 |
//thise module define a ram ,depth = 32,width = 32bit
module sram
#(
parameter DEPTH = 32,
parameter WIDTH = 32,
parameter ADDR_WIDTH = $clog2(32*32)
)
(
input wire clk ,//clock
input wire rst_n ,//reset
input wire en ,//enable sram
input wire we ,//1:write;0:read
input wire [ADDR_WIDTH-1:0]addr ,//address
input wire [WIDTH-1:0] din ,//write data
output reg [WIDTH-1:0] dout //read data
);
integer i;
reg [WIDTH-1:0] mem [0:DEPTH-1];
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin //initialize sram
for(i=0;i<(DEPTH-1);i=i+1)begin
mem[i] <= 'd0;
end
end else if(en)begin //enable sram
if(we)
mem[addr] <= din; //write data
else
dout <= mem[addr]; //read data
end
end
endmodule
3.2 apb_sram.v 模块RTL实现
Signal | Source | Description |
pclk | input | 系统时钟 |
prstn | input | 复位信号,低电平复位 |
psel | input | 片选信号 |
penable | input | 使能信号 |
pwrite | input | 1:写;0:读 |
paddr | input | 地址 |
pwdata | input | 写数据 |
pready | output | 响应 |
prdata | output | 读数据 |
//spb_sram slave module;a control module between bridge and sram
module apb_sram
(
//from bridge
input wire pclk ,
input wire prstn ,
input wire psel ,
input wire penable ,
input wire pwrite ,
input wire [9:0] paddr ,
input wire [31:0] pwdata ,
//to bridge
output wire pready ,
output wire [31:0] prdata
);
//state define
parameter IDLE = 3'b001;
parameter SETUP = 3'b010;
parameter ACCESS = 3'b100;
reg [2:0] curr_state;
reg [2:0] next_state;
//---- FSM_1:state transfer ----/
always@(posedge pclk or negedge prstn)begin
if(!prstn)begin
curr_state <= IDLE ;
end else begin
curr_state <= next_state;
end
end
//---- FSM_2:state transfer condition ----//
always@(*)begin
if(!prstn)begin
next_state <= IDLE;
end else begin
case(curr_state)
IDLE:begin
if(psel && !penable)begin
next_state = SETUP;
end else begin
next_state = IDLE;
end
end
SETUP:begin
next_state = ACCESS;
end
ACCESS:begin
if(!psel && !penable)begin
next_state = IDLE;
end else if(psel && !penable)begin
next_state = SETUP;
end else begin
next_state = ACCESS;
end
end
default:next_state = IDLE;
endcase
end
end
//---- FSM3:output logic
wire mem_en ;
wire mem_we ;
wire [9:0] apb_addr;
wire [31:0] apb_wdata;
assign mem_en = psel && !penable;
assign mem_we = psel ? pwrite:mem_we;
assign apb_addr = (next_state == SETUP) ? paddr[9:2] : apb_addr;
assign apb_wdata= (next_state == SETUP) ? pwdata : apb_wdata;
assign pready = penable;
sram
#(
.depth (32),
.width (32)
)
U0_sram
(
.clk (pclk ),//clock
.rst_n (prstn ),//reset
.en (mem_en ),//enable sram
.we (mem_we ),//1:write;0:read
.addr (apb_addr ),//address
.din (apb_wdata ),//write data
.dout (prdata )//read data
);
endmodule
3.3 Testbench
`timescale 1ns/1ps
module tb();
reg pclk ;
reg prstn ;
reg psel ;
reg penable ;
reg pwrite ;
reg [9:0] paddr ;
reg [31:0] pwdata ;
wire pready ;
wire[31:0] prdata ;
//---- apb_write_task ----//
task apb_write;
input [31:0] addr;
input [31:0] data;
begin
@(posedge pclk);
paddr <= #1 addr;
psel <= #1 1'b1;
pwrite <= #1 1'b1;
pwdata <= #1 data;
@(posedge pclk);
penable <= #1 1'b1;
@(posedge pclk);
psel <= #1 1'b0;
penable <= #1 1'b0;
end
endtask
//---- apb_read_task ----//
task apb_read;
input [31:0] addr;
begin
@(posedge pclk);
paddr <= #1 addr;
psel <= #1 1'b1;
pwrite <= #1 1'b0;
@(posedge pclk);
penable <= #1 1'b1;
@(posedge pclk);
psel <= #1 1'b0;
penable <= #1 1'b0;
end
endtask
//---- initial signle ----//
integer i,j,k;
reg [31:0] wdata_random;
initial begin
pclk = 1'b0;
prstn = 1'b1;
psel = 1'b0;
penable = 1'b0;
pwrite = 1'b0;
paddr = 32'b0;
pwdata = 32'b0;
repeat(3) @(posedge pclk);
prstn = 1'b0;
repeat(5) @(posedge pclk);
prstn = 1'b1;
$display("------------------------------------");
$display("-------------- write ---------------");
for(i=0;i<=31;i=i+1)begin
wdata_random = $random;
apb_write(i*4,wdata_random);
$display("addr = %h, wdata = %h",i*4,wdata_random);
end
$display("------------------------------------\n");
$display("------------------------------------");
$display("--------------- read ---------------");
for(j=0;j<=31;j=j+1)begin
apb_read(j*4);
$display("addr = %h, rdata = %h",j*4,prdata);
end
$display("-------------------------------------\n");
$display("-------------------------------------");
$display("-------- writing and reading --------");
for(k=0;k<=31;k=k+1)begin
wdata_random = $random;
apb_write(k*4,wdata_random);
apb_read(k*4);
$display("write data is %h, read data is %h",wdata_random,prdata);
end
$display("-------------------------------------");
repeat(10)@(posedge pclk);
$finish();
end
//---- define clock -----//
always begin
#10 pclk = ~pclk; //pclk cycle is 20ns
end
//---- generate wave -----//
initial begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0);
$fsdbDumpMDA();//add to see memory
end
apb_sram U0_apb_sram
(
.pclk (pclk ),
.prstn (prstn ),
.psel (psel ),
.penable (penable),
.pwrite (pwrite ),
.paddr (paddr ),
.pwdata (pwdata ),
.pready (pready ),
.prdata (prdata )
);
endmodule
3.4 Makefile
4. 仿真结果
写sram部分:
读sram部分:
边写边读: