在,是为了实现AHB主设备到APB从设备的连接(比如本项目中的CU和GPIO),
Bridge需要完成AHB协议和APB协议之间的转换,以及异步数据通信,
由于SoC系统的时钟多样性,AMBA总线并没有规范AHB和APB总线之间的时钟关系,
因此AHB2APB Bridge根据系统总线的时钟分布又可以分为两类:同步Bridge和异步Bridge
异步Bridge
在很多场景中,为了充分利用AHB/APB协议各自特点,对于AHB总线系统、APB总线系统的时钟采用不同频率/不同相位,
对于这种情况,AHB2APB Bridge为异步Bridge,
异步Bridge可以在保证AHB设备的性能的前提下,尽可能降低APB上外设的功耗,
而代价则是跨时钟设计带来的额外面积和设计的复杂程度
同步Bridge
在本项目中,则采用同步Bridge,即AHB与APB时钟同频,同步Bridge具有以下优点:
- 整个AMBA系统由单个时钟控制,有利于时序分析
- 不涉及跨时钟域的信号同步处理,设计简单且综合后面积小
- 系统运行稳定可靠
2.2 AHB2APB Bridge有限状态机
那么如何实现一个可以完成上述功能的Bridge模块呢?
答案是通过一个有限状态机(FSM,Finite State Machine)来进行控制,
看看一次典型的AHB到APB信号转换时序图(以Read为例):
在T1-T2期间,Bridge作为AHB总线上的Slave被HADDR选中,
在T2-T4两个时钟周期内,Bridge将AHB的HADDR上的地址Addr1放在APB总线的PADDR上,
其中T2-T3,PENABLE尚未被拉高,且HREADY会被Bridge拉低,因为APB的传输速度是低于AHB的,需要AHB总线上的Master进行等待,
而T3-T4,PENABLE被Bridge使能,数据信息被放上PRDATA并且由Bridge转交给HRDATA,
我们将T1-T2,T2-T3,T3-T4这个周期分别称为IDLE,SETUP和ENABLE,
于是我们得到AHB2APB Bridge的状态机转换示意图:
总结一下:
-
对于SETUP,下一个周期必定跳往ENABLE,
由于本项目用的是APB-Lite,没有PREADY信号,所有APB传输必须在两个周期完成
-
对于IDLE或ENABLE,若总线上有传输,则下一个周期为SETUP;若无,则下一个周期为IDLE
(“有无传输”,意思是AHB端的HADDR是否选中APB上的设备)
注意:在T2-T4传输Data1后,APB总线又在T4-T6两个周期内完成了Data2的传输,
这两次传输之间并没有经历IDLE状态,这表明当AHB总线上仍然有传输任务时,ENABLE状态直接跳往SETUP状态以准备下一个数据的传输,
由此我们得到AHB2APB Bridge的状态机:
reg [1:0] bridge_state;
always @(posedge iHCLK ) begin
if (!iHRESETn) begin
bridge_state <= BRIDGE_IDLE;
end else begin
case ( bridge_state )
// IDLE
BRIDGE_IDLE: begin
if(iHSEL) begin
bridge_state <= BRIDGE_SETUP;
end
else begin
bridge_state <= BRIDGE_IDLE;
end
end
// SETUP
BRIDGE_SETUP: begin
bridge_state <= BRIDGE_ENABLE;
end
// ENABLE
BRIDGE_ENABLE: begin
if(iHSEL) begin
bridge_state <= BRIDGE_SETUP;
end
else begin
bridge_state <= BRIDGE_IDLE;
end
end
//DEFAULT
default: bridge_state <= BRIDGE_IDLE;
endcase
end
end
2.3 AHB与APB信号转换
完成上述状态机设计后,剩下的工作就非常简单了,我们需要把AHB总线上的信号放到APB总线上:
-
APB上的控制信号(PWRITE,PADDR)与数据信号(PWDATA,PRDATA)都是直接从AHB那边的信号拿过来的,
-
PENABLE只在ENABLE状态下拉高,
-
PSELx取决于HADDR落在APB总线上哪个外设的地址空间范围内,在本设计中,APB Slave为APB GPIO模块
于是可以得到AHB与APB信号之间的对应关系:
//AHB -> APB
assign oPSEL0 = ( iHADDR_r == ADDR_GPIO_0) ? 1'b1 : 1'b0;
assign oPSEL1 = ( iHADDR_r == ADDR_GPIO_1) ? 1'b1 : 1'b0;
assign oPWRITE = iHWRITE_r ;
assign oPENABLE = ( bridge_state == BRIDGE_ENABLE ) ? 1'b1 : 1'b0;
assign oPADDR = iHADDR_r;
assign oPWDATA = iHWDATA_r;
//Bridge -> AHB
assign oHREADY = ( bridge_state == BRIDGE_SETUP ) ? 1'b0 : 1'b1;
assign oHRESP = OKAY;
assign oHRDATA = iPRDATA;
最后,
附上AHB2APB Bridge模块的完整RTL:
module AHB2APB_bridge #(
//HRANS Parameters
parameter IDLE = 2'b00 ,
parameter BUSY = 2'b01 ,
parameter SEQ = 2'b10 ,
parameter NONSEQ = 2'b11 ,
//HRSP Parameters
parameter OKAY = 2'b00 ,
parameter ERROR = 2'b01 ,
parameter SPLIT = 2'b10 ,
parameter RETRY = 2'b11 ,
//bridge_state Parameters
parameter BRIDGE_IDLE = 2'b00,
parameter BRIDGE_SETUP = 2'b01,
parameter BRIDGE_ENABLE = 2'b10,
//ADDR Parameters
parameter ADDR_GPIO_0 = 32'h0000_0000,
parameter ADDR_GPIO_1 = 32'h0000_8000
) (
//------------ AHB ------------
//input
input iHCLK,
input iHRESETn,
input iHSEL,
input [1:0] iHTRANS,
input [3:0] iHSIZE ,
input [2:0] iHBURST,
input iHWRITE,
input [31:0] iHWDATA,
input [31:0] iHADDR ,
//output
output oHREADY,
output [1:0] oHRESP ,
output [31:0] oHRD