(一)AHB 总线的架构
总线周期:就是总线时钟的频率,对于AMBA AHB 或者APB 协议总线周期定义为从一个上升沿到临界的上升沿的变化区间。
总线传输:AHB 总线传输是数据目标的读写操作,可能会持续一个或者多个总线周期,而总线的传输在收到从机的ready信号终止,总线位宽为8、16、32、64、128bits。
猝发传输:定义了一个或多个数据传输,由主线总机发起,在地址空间增加时,每次传输增加的步长,由总线传输的大小决定。
(二)AHB总线特点
- 高速总线,高性能
- 流水线操作
- 支持多个总线主设备(最多16个)
- 支持single、burst传输
- 总线位宽8、16、32、64、128bits
- 上升沿触发
(三)AHB总线结构
(1)AHB主设备(master)
- 初始化一次读/写操作
- 某一时刻只允许一个主设备使用总线
- uP、DMA、DSP、LCDC
(2)AHB从设备(slave) - 响应一次读/写操作
- 外部存储器控制器EMI、APB bridge
(3)AHB仲裁器(arbiter)
允许某一个主设备控制总线(没有定义仲裁算法)
(4)AHB译码器
通过地址译码来决定选择哪一个从设备
(四)AHB总线信号
- HCLK/HRESETn(时钟与复位信号)
- HSEL(指明当前被访问的从设备,来自译码器)
- HADDR[31:0] (32位系统地址总线)
- HWDATA[31:0] (写数据总线,从主设备写到从设备)
- HRDATA[31:0] (读数据总线,从从设备读到主设备)
- HTRANS (传输的状态有:NONSEQ、SEQ、IDLE、BUSY)
- HSIZE(传输的数据宽度 8 、16 、32)
- HBURST(传输的burst类型,single,incr4/8/16,wrap4/8/16)
- HRESP(从设备发给主设备的总线传输状态:OKAY、ERROR、RETRY、SPLIT)
- HREADY(高-从设备传输结束;低-从设备需延迟传输周期)
(五)AHB基本传输
1、地址周期(地址与控制信号),只有一个cycle
2、数据周期(读周期/写周期),由HREADY信号决定需要几个cycle
(1)没有等待的single transfer
第一个周期的上升沿,主机将地址信息和控制信息发送到总线上
第二个周期的上升沿,从机采样地址和控制信号,并将HREADY拉高;如果是写操作,主机会在第二个周期的上升沿后写入数据;如果是读操作,从机会在HREADY信号拉高后将读取的数据写入总线。
第三个周期的上升沿,主机获取HREADY高信号,如果是写操作,表明从机已经接收数据;如果是读操作,表明读数据有效并接收。(HREADY在数据有效器件必须为高,并且保存至第三个周期的上升沿之后)
(2)两个等待周期的single transfer
从机有时候存在太慢不能不处理的情况,需要插入等待状态,HREADY信号早第二和第三周期拉低,需要主机等待两个周期。如果是写操作,主机需要在等待期间保持写数据不变,直到本次传输完成;如果是读操作,从机只需在READY信号拉高后,给出数据即可。
(3)流水线AHB传输
由于pipeline操作,尽管进行B的读写操作时,插入了一个等待状态,但是传递三个数据总共用了5个周期。
由于AHB只能将Address 与 Data 分开,所以在AHB 中流水线的深度是2,即总线上最多存在两个未处理完成的transfer。
(4)burst传输
突发信息通过使用 HBURST[2:0]并且 8 种可能的类型在中定义如下:
(1)single transfer
(2)INCR 4-beat/8-beat/16-beat
(3)WRAP 4-beat/8-beat/16-beat
对于增量猝发,每一次传输地址都是前一次地址的增量
对于回环(wrap)猝发,如果传输的起始地址并未和字节总数对齐,那么传输地址将在边界处回环。例如,一个4拍回环猝发的字(4字节)访问将在16字节边界回环。因此地址将在16字节处回环,如果起始地址为0x34,即0x3c之后的地址为0x30,那么地址传输为0x34、0x38、0x3c、0x30;对于wrap8而言,地址将在32字节边界处回环,因此0x3c之后的地址如果起始地址为0x34,地址0x3c之后的地址是0x20,那么地址传输为0x34,0x38、0x3c、0x20、0x24、0x28、0x2c、0x30.
注意:
burst传输不能穿越KB边界,比如由0x000传输至0x400时,穿越KB边界必须将猝发模式由SEQ转为NSEQ,并继续传输。这是由于slave的地址一般以KB分界,避免数据传输时发生错误
HTRANS[1:0] :
00:IDLE(主设备占用总线,但没有进行传输;两次burst传输中间主设备发送IDLE)
01:BUSY(主设备占用总线,但是在burst传输过程中还没有准备好进行下一次传输;一次burst传输中间主设备发BUSY)
10:NONSEQ (表明一个单个数据的传输或者一个burst传输的第一个数据;其地址与控制信号与上一次传输无关)
11:SEQ(表明burst传输接下来的数据;地址与上一次传输的地址是相关的)
其他控制信号:
HWRITE:高电平:写;低电平:读
HSIZE[2:0]:
000:8bits
001:16bits
010:32bits
011:64bits
100:128bits
101:256bits
110:512bits
111:1024bits
注意:猝发大小表示猝发的节拍数量,并不是一次猝发传输的实际字节数量。一次猝发数据总量可以用节拍数乘以每拍数据的字节数来计算,每拍字节数由HSIZE[2:0]确定,并且猝发传输必须将地址边界与传输大小对齐。
由于猝发传输的大小受限->
下面是一个软件(汇编)与硬件对应的图:
HRESP[1:0] 的编码、传输响应信号和每个响应的描述如下:
(五)仲裁信号
- HBUSREQ(总线请求 ,master发出)
- HLOCKx(高电平:主设备请求锁定总线,master发出)
- HGRANTx(指出主设备x可访问总线,arbiter 发出;主设备x控制总线,当HGRANTx=1且HREADY=1)
- HMASTER[3:0](正在传输的主设备)
- HMASTLOCK(主设备正在进行一次锁定传输)
- HSPLITx[15:0](从设备用这个信号告诉仲裁器哪个主设备允许重新尝试一次split传输)
(1)无等待仲裁
(2)有等待仲裁
AHB主设备端口
AHB从设备端口
下面是AHB slave的RTL代码:
module ahb_slave(
input hclk,
input hreset_n,
input hselx,
input [31:0] haddr,
input hwrite,
input [1:0] htrans,
input [2:0] hsize,
input [2:0] hburst,
input [31:0] hwdata,
output reg hready,
output [1:0] hresp,
output reg [31:0] hrdata);
reg hready1;
reg hready2;
reg hready3;
reg hready4;
wire [7:0] addr1= haddr[7:0];
reg [7:0] hrdata1;
reg [7:0] hrdata2;
reg [7:0] hrdata3;
reg [7:0] hrdata4;
parameter bits8 = 3'b000;
parameter bits16 = 3'b001;
parameter bits32 = 3'b010;
parameter bits64 = 3'b011;
parameter bits128 = 3'b100;
parameter bits256 = 3'b101;
parameter bits512 = 3'b110;
parameter bits1024 = 3'b111;
parameter single=3'b000;
parameter incr=3'b001;
parameter wrap4=3'b010;
parameter incr4=3'b011;
parameter wrap8=3'b100;
parameter incr8=3'b101;
parameter wrap16=3'b110;
parameter incr16=3'b111;
parameter okay=2'b00;
parameter error=2'b01;
parameter retry=2'b10;
parameter split=2'b11;
reg [31:0] data [256-1:0];
integer i;
//read and write
always@(posedge hclk or negedge hreset_n)
begin
if(!hreset_n)
begin
for(i=0;i<=256-1;i=i+1)
begin
data[i]<=0;
end
hready<=1'b0;
hrdata<=32'b0;
hrdata1<=8'h0;
hrdata2<=8'h0;
hrdata3<=8'h0;
hrdata4<=8'h0;
end
else
begin
case(hburst)
single:
begin
if(!hwrite&&hselx&&(htrans[1:0]==2'b10))
begin
hready<=1'b1;
hready1<=hready;
if(hready1==1)
begin
hrdata<={hrdata4,hrdata3,hrdata2,hrdata1};
case(hsize)
bits8:
begin
hrdata1<=data[addr1];
end
bits16:
begin
hrdata1<=data[addr1];
hrdata2<=data[addr1+1];
end
bits32:
begin
hrdata1<=data[addr1];
hrdata2<=data[addr1+1];
hrdata3<=data[addr1+2];
hrdata4<=data[addr1+3];
end
endcase
end
end
else if(hwrite&&hselx&&(htrans[1:0]==2'b10))
begin
hready<=1'b1;
hready1<=hready;
if(hready1==1)
begin
case(hsize)
bits8:
begin
data[addr1]<=hwdata[7:0];
end
bits16:
begin
data[addr1]<=hwdata[7:0];
data[addr1+1]<=hwdata[15:8];
end
bits32:
begin
data[addr1]<=hwdata[7:0];
data[addr1+1]<=hwdata[15:8];
data[addr1+2]<=hwdata[23:16];
data[addr1+3]<=hwdata[31:24];
end
endcase
end
end
end
incr4:
begin
if(!hwrite&&hselx&&(htrans[1:0]==2'b10))
begin
hready<=1;
hready1<=hready;
hready2<=hready1;
hready3<=hready2;
hready4<=hready3;
case(hsize)
bits32:
begin
if(hready1==1)
hrdata<=data[addr1];
else if(hready2==1)
hrdata<=data[addr1+4];
else if(hready3==1)
hrdata<=data[addr1+8];
else if(hready4==1)
hrdata<=data[addr1+12];
end
endcase
end
else if(hwrite&&hselx&&(htrans[1:0]==2'b10))
begin
hready<=1;
hready1<=hready;
hready2<=hready1;
hready3<=hready2;
hready4<=hready3;
case(hsize)
bits32:
begin
if(hready1==1)
data[addr1]<=hwdata;
else if(hready2==1)
data[addr1+4]<=hwdata;
else if(hready3==1)
data[addr1+8]<=hwdata;
else if(hready4==1)
data[addr1+12]<=hwdata;
end
endcase
end
end
wrap4:
begin
if(!hwrite&&hselx&&(htrans[1:0]==2'b10))
begin
hready<=1;
hready1<=hready;
hready2<=hready1;
hready3<=hready2;
hready4<=hready3;
case(hsize)
bits32:
begin
if(hready1==1)
begin
case(addr1[3:0])
4'h0:
begin
if(hready1==1)
hrdata<=data[addr1];
else if(hready2==1)
hrdata<=data[addr1+4];
else if(hready3==1)
hrdata<=data[addr1+8];
else if(hready4==1)
hrdata<=data[addr1+12];
end
4'h4:
begin
if(hready1==1)
hrdata<=data[addr1];
else if(hready2==1)
hrdata<=data[addr1+4];
else if(hready3==1)
hrdata<=data[addr1+8];
else if(hready4==1)
hrdata<=data[addr1-4];
end
4'h8:
begin
if(hready1==1)
hrdata<=data[addr1];
else if(hready2==1)
hrdata<=data[addr1+4];
else if(hready3==1)
hrdata<=data[addr1-8];
else if(hready4==1)
hrdata<=data[addr1-4];
end
4'hc:
begin
if(hready1==1)
hrdata<=data[addr1];
else if(hready2==1)
hrdata<=data[addr1-12];
else if(hready3==1)
hrdata<=data[addr1-8];
else if(hready4==1)
hrdata<=data[addr1-4];
end
endcase
end
end
endcase
end
else if(hwrite&&hselx&&(htrans[1:0]==2'b10))
begin
hready<=1;
hready1<=hready;
hready2<=hready1;
hready3<=hready2;
hready4<=hready3;
case(hsize)
bits32:
begin
if(hready1==1)
begin
case(addr1[3:0])
4'h0:
begin
if(hready1==1)
data[addr1]<=hwdata;
else if(hready2==1)
data[addr1+4]<=hwdata;
else if(hready3==1)
data[addr1+8]<=hwdata;
else if(hready4==1)
data[addr1+12]<=hwdata;
end
4'h4:
begin
if(hready1==1)
data[addr1]<=hwdata;
else if(hready2==1)
data[addr1+4]<=hwdata;
else if(hready3==1)
data[addr1+8]<=hwdata;
else if(hready4==1)
data[addr1-4]<=hwdata;
end
4'h8:
begin
if(hready1==1)
data[addr1]<=hwdata;
else if(hready2==1)
data[addr1+4]<=hwdata;
else if(hready3==1)
data[addr1-8]<=hwdata;
else if(hready4==1)
data[addr1-4]<=hwdata;
end
4'hc:
begin
if(hready1==1)
data[addr1]<=hwdata;
else if(hready2==1)
data[addr1-12]<=hwdata;
else if(hready3==1)
data[addr1-8]<=hwdata;
else if(hready4==1)
data[addr1-4]<=hwdata;
end
endcase
end
end
endcase
end
end
endcase
end
end
endmodule
module tb();
reg hclk;
reg hreset_n;
reg hselx;
reg [31:0] haddr;
reg hwrite;
reg [1:0] htrans;
reg [2:0] hsize;
reg [2:0] hburst;
reg [31:0] hwdata;
wire hready;
wire [31:0] hrdata;
ahb_slave u1(
.hclk(hclk),
.hreset_n(hreset_n),
.hselx(hselx),
.haddr(haddr),
.hwrite(hwrite),
.htrans(htrans),
.hsize(hsize),
.hburst(hburst),
.hwdata(hwdata),
.hready(hready),
.hresp(),
.hrdata(hrdata));
initial
begin
hclk=0;
forever #(20/2) hclk=~hclk;
end
initial
begin
hreset_n=0;
#(60) hreset_n=1;
hwrite=1;
hselx=1;
haddr=32'h45684120;
hsize=3'b010;
hburst=3'b000;
htrans=2'b10;
#(20);
hwdata=32'h123045;
#(40);
hwrite=1;
hselx=1;
haddr=32'h45684124;
hsize=3'b010;
hburst=3'b010;
htrans=2'b10;
#(20);
hwdata=32'h546123;
#(20);
hwdata=32'h123456;
#(20);
hwdata=32'h11111111;
#(20);
hwdata=32'h22222222;
#(100);
hwrite=0;
hselx=1;
haddr=32'h45684120;
hsize=3'b010;
hburst=3'b000;
htrans=2'b10;
#(40);
hwrite=0;
hselx=1;
haddr=32'h45684124;
hsize=3'b010;
hburst=3'b010;
htrans=2'b10;
end
endmodule
来源:CSDN
作者:人无再少年97
链接:https://blog.csdn.net/buzhiquxiang/article/details/103641344