AHB
一、什么是AHB总线?
AHB是一种适用于高性能可综合设计的总线接口。
最常见的AHB从属设备包括内部存储器、外部存储器接口及高带宽外设。低带宽外设也可作为AHB从属器接入,但出于系统性能考量,往往通过作为AHB从属器的APB桥接器,连接低带宽外设。
主控器通过驱动地址与控制信号来启动一次传输。这些信号提供了关于传输地址、方向、数据位宽的信息,并指示该传输是否属于突发传输的一部分。
传输类型可分为:
- 单次传输;
- 地址递增突发传输(不进行地址边界回绕);
- 地址回环突发传输(在特定地址边界处回绕)。
每次传输包含两个阶段:
- 地址阶段:一个地址与控制信号周期;
- 数据阶段:一个或多个数据周期。
从属器无法请求延长地址阶段,但是可通过HREADY信号请求主控器延长数据阶段。HREADY为低电平时,传输中会插入等待状态,使得从属器获得额外时间来提供或采样数据。
从属器使用HRESP信号来指示传输的成功或失败状态。
二、信号描述
2.1 全局信号
| 名称 | 源头 | 位宽 | 说明 |
|---|---|---|---|
| HCLK | 时钟源 | 1 | 上升沿有效 |
| HRESETn | 复位控制器 | 1 | 唯一一个低电平有效的信号 |
2.2 主机信号
| 名称 | 目的地 | 位宽 | 描述 |
|---|---|---|---|
| HADDR | 从机和译码器 | 建议10~64 | 字节地址,文档中固定32bit |
| HBURST | 从机 | 0或3 | burst传输次数,以及地址增加方式 |
| HMASTLOCK | 从机 | 1 | 表示当前传输属于锁定序列。其地址和控制信号同步。 |
| HPROT | 从机 | 0,4,7 | 保护控制信号,用于指示当前传输的访问类型属性 |
| HSIZE | 从机 | 3 | 数据总线的有效位宽 |
| HNONSEC | 从机和译码器 | 1 | 表示当前传输是否安全 |
| HEXCL | 独占访问监控器 | 1 | 表示传输是否是独占访问传输的一部分 |
| HMASTER | 从机和独占访问监控器 | 0~8 | 主控器标识符,若主控器支持多个可执行独占访问的线程,则由其生成该信号 |
| HTRANS | 从机 | 2 | 表示传输类型,IDLE、BUSY、NONSEQUENTIAL、SEQUENTIAL |
| HWDATA | 从机 | 8, 16, 32, 64, 128, 256, 512, 1024 | 写数据,建议32~256 |
| HWSTRB | 从机 | DATA_WIDTH/8 | 写数据选通信号,表示写数据的每个字节是否有效 |
| HWRITE | 从机 | 1 | 表示传输方向,高写低读,传输期间保持不变 |
2.3 从机信号
| 名称 | 目的地 | 位宽 | 描述 |
|---|---|---|---|
| HRDATA | 多路选择器 | 8, 16, 32, 64, 128, 256, 512, 1024 | 读数据,从机传->多路选择器->主机,建议32~256 |
| HREADYOUT | 多路选择器 | 1 | 高说明传输完成,低可以延长传输周期 |
| HRESP | 多路选择器 | 1 | 传输响应信号,拉高表示传输正常,拉低表示传输错误,与HREADY结合 |
| HEXOKAY | 多路选择器 | 1 | 独占传输响应信号 |
2.4 译码器信号
| 名称 | 目的地 | 位宽 | 描述 |
|---|---|---|---|
| HSELx | 从机 | 1 | 从设备选通信号 |
一般译码器也会把HSELx传给多路选择器,方便将被选中的从机的数据传回给主机,需要保证HSELx能对上时序。
2.5 多路选择器信号
| 名称 | 目的地 | 位宽 | 描述 |
|---|---|---|---|
| HRDATA | 主机 | 8, 16, 32, 64, 128, 256, 512, 1024 | 读数据总线,建议32~256 |
| HREADY | 主机和从机 | 1 | 拉高时,告诉主机和从机上次传输完成了 |
| HRESP | 主机 | 1 | 传输响应信号 |
| HEXOKAY | 主机 | 1 | 独占传输响应信号 |
三、信号传输
3.1 基本传输
一次传输分成两个阶段:
- 地址阶段:一个时钟周期,但是如果上一次传输的数据阶段有多周期,那么这次的地址阶段就有多周期;
- 数据阶段:多个时钟周期,取决于HREADY。

第二次传输的地址阶段和第一次传输的数据阶段重合,实现高性能流水线传输。
3.2 传输类型
传输类型由HTRANS[1:0]决定,
| HTRANS[1:0] | 类型 | 说明 |
|---|---|---|
| 00 | IDLE | 表示无需进行数据传输。主控器不打算执行数据传输时,会发起空闲传输。从属器必须始终对空闲传输提供零等待状态的OKAY响应(HRESP),且必须忽略该传输。空闲传输用于维持总线流水线操作完整性 |
| 01 | BUSY | 使主控器能够在突发传输过程中插入空闲周期,表明主控器将继续进行突发传输,但下一个数据传输无法立即执行。当主控器使用BUSY传输类型时,地址与控制信号必须指向突发序列中的下一个传输。仅未定义长度的突发传输允许将BUSY传输作为突发序列的最后一个周期 用于维持突发流程不中断的同时插入延迟,适用于主控器需临时处理高优先级任务等场景 从属器必须始终对BUSY传输提供零等待状态的OKAY响应,且必须忽略该传输 |
| 10 | NONSEQ | 当前传输为单次传输或突发传输的首个传输,标志着新传输序列的启动,地址总线需提供完整目标地址,控制信号需完全重新定义 |
| 11 | SEQ | 突发传输中剩余的传输均为连续传输,其地址与前一传输相关联,控制信息与前一传输完全相同。地址值的增加方式由HBURST决定 |

T0~T1:连续传输的第一个阶段NONSEQ,准备好地址和控制信息;
T1~T2:主机此时无法第二次传输,因此插入BUSY,地址和控制信息需要准备好;从机把数据输出到HRDATA总线;
T2~T3:主机沿用第二次传输的地址和控制信息;主机不理睬从机输出到HRDATA总线的数据;
T3~T4:主机准备好第三次传输的地址和控制信号;从机把数据输出到HRDATA总线;
T4~T5:主机准备好第四次传输的地址和控制信号;从机没准备好;
T5~T6:主机依然准备好第四次传输的地址和控制信号;从机准备好了,把数据输出到HRDATA总线;
T6~T7:没有下一次传输,从机把第四次的数据输出到HRDATA总线。
3.3 传输位宽
HSIZE[2:0]表示传输数据位宽,HWDATA的位宽表示写数据总线位宽
| HSIZE[2:0] | 位宽 | 说明 |
|---|---|---|
| 000 | 8 | 字节 |
| 001 | 16 | 半字 |
| 010 | 32 | 字 |
| 011 | 64 | 双字 |
| 100 | 128 | 四字 |
| 101 | 256 | 八字 |
| 110 | 512 | |
| 111 | 1024 |
HSIZE决定的位宽必须小于等于HWDATA的位宽。在地址阶段准备好,并在整个传输期间保持不变。HSIZE与 HBURST信号共同决定了回环突发传输的地址边界。
3.4 写选通
写数据选通是一项可选功能,使主控器能够执行仅更新寄存器部分有效字节的写传输。
AHB协议中写数据选通信号的适用规则如下:
- 对于数据位宽小于总线位宽的传输,HSIZE和HADDR共同决定哪些字节通道为活跃通道;
就是说对应有效字节的选通信号才有效; - 允许所有写选通信号均取消置位的传输,在此情况下,该传输不会写入任何字节;
- 写选通信号与写数据通道的映射关系不依赖于系统的字节序;
- 对于读传输,建议将写选通信号全部取消置位;
- 允许在写突发传输的各个数据节拍之间改变写选通信号的设置。
3.5 突发操作
本协议定义了4拍、8拍及16拍的突发传输、未定义长度的突发传输以及单次传输。协议支持以下两种突发模式:
- 递增突发:访问连续的存储位置,突发中的每次传输地址均为前一次地址的递增;
- 回环突发:在跨越特定地址边界时进行地址回绕。该地址边界由突发传输的拍数(由
HBURST控制)与传输大小(由HSIZE控制)的乘积计算得出。
例如,一个4拍的字(4字节)回环突发传输,其地址边界为 4拍 × 4字节/拍 = 16字节。因此,若该突发的起始地址为0x34,则其四次传输的访问地址依次为:0x34, 0x38, 0x3C, 和 0x30。
| HBURST[2:0] | 类型 | 说明 |
|---|---|---|
| 000 | 单次传输 | 顾名思义 |
| 001 | 未定义长度 | burst长度不定,所以也没法回环,只能地址自增 |
| 010 | 4拍回环传输 | 4次传输,地址回环 |
| 011 | 4拍自增传输 | 4次传输,地址自增 |
| 100 | 8拍回环传输 | 8次传输,地址回环 |
| 101 | 8拍自增传输 | 8次传输,地址自增 |
| 110 | 16拍回环传输 | 16次传输,地址回环 |
| 111 | 16拍自增传输 | 16次传输,地址自增 |
主控器不得发起任何会跨越1KB地址边界的递增式突发传输。
3.5.1 BUSY传输后突发终止
在突发传输中,可以插入BUSY暂停突发传输。
未定义长度的突发可以先插入BUSY,再决定是否结束,通过执行NONSEQ(非连续)或IDLE(空闲)传输来有效终止该未定义长度突发。
其余固定长度的突发传输只能以SEQ(连续)传输作为结束。
不允许主控器在SINGLE(单次)突发后立即执行BUSY传输,只能跟随IDLE传输或NONSEQ传输。
3.5.2 burst示例



3.6 等待传输
从属器在需要更多时间提供或采样数据时,通过驱动 HREADYOUT信号插入等待状态。
在传输被等待状态延长期间,主控器对传输类型与地址的修改权限将受到限制:
- 等待状态期间的传输类型变更规则;
- 等待状态期间的地址变更规则。
3.6.1 等待状态期间传输类型改变
从机请求等待时,主机符合下列情况,才能改变传输类型:
- 主机的传输类型时IDLE;
- 主机的传输类型是固定长度burst期间的BUSY;
- 主机的传输类型是不定长度burst期间的BUSY。
3.6.1.1 IDLE传输

如果在等待期间,传输类型正好是IDLE,主机可以把传输类型改为NONSEQ,改了之后就必须保持NONSEQ了。
3.6.1.2 固定长度burst期间的BUSY

允许从BUSY改为SEQ。
3.6.1.3 不定长度burst期间的BUSY

允许从BUSY改为NONSEQ。
3.6.2 等待状态期间地址改变
3.6.2.1 IDLE传输期间

等待状态发生在IDLE传输期间可以修改地址,发生在NONSEQ传输期间不能修改地址。
3.6.2.2 错误响应

在处于等待状态的传输过程中,若从属器返回ERROR响应,则允许主控器在HREADY为低电平时改变地址。
四、总线互连
4.1 互连
互连组件为系统中的主控器与从属器提供连接支持。
4.2 地址译码

地址译码器为总线上的每个从属器生成一个选通信号 HSELx。
从属器仅可在 HREADY为高电平(表明当前传输即将完成)时对 HSELx、地址及控制信号进行采样。在某些情况下,主机选择从机1,但是从机1没有ready,等从机1ready,主机已经改选从机2,见3.6.2.1。
每个从机最小地址空间为1KB(即0x0000_0000~0x0000_03ff,0x0000_0400~0x0000_07ff),且该地址区域的起始与结束地址必须位于1KB边界(即地址空间必须是1KB的整数倍,而且起始地址要能被0x0000_0400整除)。
在burst传输期间,地址不能超出某个从机的地址空间,如果跨越了从机,就错了。
4.2.1 默认从机
若系统设计中的存储器映射未完全填满(即存在未分配的地址空间),则必须实现一个额外的默认从属设备,用于在访问任何不存在的地址时提供响应。
当尝试对不存在的地址执行NONSEQUENTIAL(非连续)或SEQUENTIAL(连续)传输时,默认从属设备将返回ERROR(错误)响应。
而对不存在地址的IDLE(空闲)或BUSY(忙碌)传输则会得到默认从属设备零等待状态的OKAY(正常)响应。
4.2.2 多从机选通
如果只有一个从机,这个从机包括了多个不连续的地址,那么每个地址都需要一个HSELx映射,即一个从机可以有多个HSELx信号。该从机根据不同的HSELx,选择访问不同的地址空间。
4.3 读数据与响应的多路选择器

AHB协议采用读数据多路选择器互联方案。主控器将地址与控制信号广播至所有从属器,由译码器在传输的数据相位期间选通相应的从属器。被选从属器返回的响应数据通过读数据多路选择器传送至主控器。
五、从设备响应信令
当主控器启动传输后,从属器将控制传输的进度。
从属器在被访问时必须提供表明传输状态的响应,该传输状态通过HRESP信号传递,
| HRESP | 响应 | 说明 |
|---|---|---|
| 0 | OKAY | 该传输可能已成功完成,是否完成取决于HREADYOUT信号。HREADYOUT=0,传输未完成;=1,传输完成且成功。 |
| 1 | ERROR | 传输过程中发生了错误。错误状态需要采用两周期响应机制实现:第一周期驱动HRESP信号为ERROR状态,第二周期在保持HRESP为ERROR状态的同时断言HREADYOUT信号。即先拉高HRESP,再拉高HREADYOUT,同时保持HRESP不变。 |
六、数据总线
6.1 数据总线
6.1.1 HWDATA总线
主控器在执行写传输时驱动写数据总线。若传输被延长(插入等待状态),主控器必须保持数据有效直至传输完成(以HREADY信号变为高电平为标志)。
对于位宽小于总线位宽的传输(例如在32位总线上执行16位传输),主控器仅需驱动有效的字节通道。从属器从正确的字节通道中选择写数据。
6.1.2 HRDATA总线
在执行读传输时,相应的从属器驱动读数据总线。若从属器通过保持HREADY为低电平来延长读传输,则其仅需在传输的最终周期(即HREADY变为高电平时)提供有效数据。
对于位宽小于总线位宽的传输,从属器仅需在活跃字节通道上提供有效数据。主控器从正确的字节通道中选择数据。
从属器仅在传输以OKAY响应完成时需要提供有效数据。ERROR响应不需要提供有效的读数据。
七、代码与仿真
7.1 完整代码
目前完成了Single传输和Incr传输,没有加入busy逻辑。
由于两个从机都只定义了8个寄存器,因此Incr读写都会在传最后一个地址的周期内发送stop信号,结束传输。
7.1.1 AHB
`timescale 1ns/1ps
module ahb #(
parameter DATA_WIDTH = 32 ,
parameter ADDR_WIDTH = 32
)(
//global
input wire HCLK ,
input wire HRESETn ,
//master
output wire [ADDR_WIDTH-1:0] HADDR ,
output wire [2:0] HBURST ,
output wire [2:0] HSIZE ,
output wire [1:0] HTRANS ,
output wire [DATA_WIDTH-1:0] HWDATA ,
output wire HWRITE ,
`ifdef RARELY_USED_SIGNAL
output wire HMASTLOCK ,
output wire [3:0] HPROT ,
output wire HNONSEC ,
output wire HEXCL ,
output wire [3:0] HMASTER ,
output wire [DATA_WIDTH/8-1:0] HWSTRB ,
`endif
//multiplexor
input wire [DATA_WIDTH-1:0] HRDATA ,
input wire HREADY ,
input wire HRESP ,
`ifdef RARELY_USED_SIGNAL
input wire HEXOKAY ,
`endif
//for simulation
input wire cmd_start ,
input wire [DATA_WIDTH-1:0] cmd_wdata ,
input wire [ADDR_WIDTH-1:0] cmd_addr ,
input wire cmd_write ,
input wire [2:0] cmd_size ,
input wire [2:0] cmd_burst ,
input wire [3:0] cmd_length ,
input wire cmd_busy ,
input wire cmd_stop ,
output wire [DATA_WIDTH-1:0] cmd_rdata ,
output wire cmd_error ,
output wire cmd_ready
);
localparam BUSY = 2'b01;
localparam NONSEQ = 2'b10;
localparam SEQ = 2'b11;
localparam IDLE = 2'b00;
localparam ADDR = 2'b01;
localparam DATA = 2'b10;
localparam SINGLE = 3'b000;
localparam INCR = 3'b001;
localparam WRAP4 = 3'b010;
localparam INCR4 = 3'b011;
localparam WRAP8 = 3'b100;
localparam INCR8 = 3'b101;
localparam WRAP16 = 3'b110;
localparam INCR16 = 3'b111;
/**************************************************************************/
reg [2:0] burst;
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 burst <= 3'd0;
end else if(HTRANS == NONSEQ && HREADY) begin
#1 burst <= HBURST;
end else if(HTRANS == IDLE && HREADY) begin
#1 burst <= HBURST;
end else begin
#1 burst <= burst;
end
end
/**************************************************************************/
reg stop1;
reg stop2;
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 stop1 <= 1'b0;
end else if(HREADY) begin
#1 stop1 <= 1'b0;
end else if(cmd_stop) begin
#1 stop1 <= 1'b1;
end else begin
#1 stop1 <= stop1;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 stop2 <= 1'b0;
end else if((cmd_stop || stop1) && HREADY) begin
#1 stop2 <= 1'b1;
end else if(HREADY) begin
#1 stop2 <= 1'b0;
end else begin
#1 stop2 <= stop2;
end
end
/**************************************************************************/
reg [1:0] state;
reg [1:0] nextstate;
always @(*) begin
case(state)
IDLE:
if(cmd_start) nextstate = ADDR;
else nextstate = IDLE;
ADDR:
if(!HREADY) nextstate = ADDR;
else nextstate = DATA;
DATA:
if(!HREADY) nextstate = DATA;
else if(stop2) nextstate = IDLE;
else if(burst == SINGLE) nextstate = IDLE;
else if(burst == INCR) nextstate = DATA;
default: nextstate = IDLE;
endcase
end
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 state <= IDLE; else #1 state <= nextstate;
/**************************************************************************/
reg [1:0] trans;
reg [1:0] nexttrans;
always @(*) begin
case(trans)
IDLE:
if(cmd_start) nexttrans = NONSEQ;
else nexttrans = IDLE;
NONSEQ:
if(!HREADY) nexttrans = NONSEQ;
else if(cmd_stop || stop1) nexttrans = IDLE;
else if(HBURST == SINGLE) nexttrans = IDLE;
else if(HBURST == INCR) nexttrans = SEQ;
SEQ:
if(!HREADY) nexttrans = SEQ;
else if(cmd_stop || stop1) nexttrans = IDLE;
else if(HBURST == INCR) nexttrans = SEQ;
default: nexttrans = IDLE;
endcase
end
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 trans <= IDLE; else #1 trans <= nexttrans;
/**************************************************************************/
reg [2:0] hburst_d1;
reg hwrite_d1;
reg [ADDR_WIDTH-1:0] haddr_d1;
reg hready_d1;
reg [2:0] hsize_d1;
reg [DATA_WIDTH-1:0] hwdata_d1;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 hburst_d1 <= 3'b0; else #1 hburst_d1 <= HBURST;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 hwrite_d1 <= 1'b0; else #1 hwrite_d1 <= HWRITE;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 haddr_d1 <= 'd0; else #1 haddr_d1 <= HADDR;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 hready_d1 <= 1'b0; else #1 hready_d1 <= HREADY;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 hwdata_d1 <= 'd0; else #1 hwdata_d1 <= HWDATA;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 hsize_d1 <= 3'd0; else #1 hsize_d1 <= HSIZE;
/**************************************************************************/
/* when HTRANS is IDLE, HADDR can be changed;
* when HTRANS is NONSEQ, HADDR can only be changed at the first cycle if no
* ready;
* so need a nonseq counter;
*/
reg [3:0] cnt_nonseq_d;
wire [3:0] cnt_nonseq = (HTRANS == NONSEQ) ? cnt_nonseq_d + 1'b1 : 4'd0;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 cnt_nonseq_d <= 4'd0; else #1 cnt_nonseq_d <= cnt_nonseq;
/**************************************************************************/
assign HBURST = (HTRANS == IDLE) ? cmd_burst :
(HTRANS == NONSEQ && cnt_nonseq == 4'd1) ? cmd_burst : hburst_d1;
assign HWRITE = (HTRANS == IDLE) ? cmd_write :
(HTRANS == NONSEQ && cnt_nonseq == 4'd1) ? cmd_write : hwrite_d1;
assign HSIZE = (HTRANS == IDLE) ? cmd_size :
(HTRANS == NONSEQ && cnt_nonseq == 4'd1) ? cmd_size : hsize_d1;
assign HWDATA = (state == DATA && hready_d1 && hwrite_d1) ? cmd_wdata : hwdata_d1;
assign HTRANS = trans;
assign HADDR = (HTRANS == IDLE) ? cmd_addr :
(HTRANS == NONSEQ && cnt_nonseq == 4'd1) ? cmd_addr :
(HTRANS == SEQ && hready_d1) ? haddr_d1 + 3'd4 : haddr_d1;
endmodule
7.1.2 Decoder
`timescale 1ns/1ps
module decoder #(
parameter ADDR_WIDTH = 32
)(
input wire [ADDR_WIDTH-1:0] HADDR ,
output wire HSEL1 ,
output wire HSEL2 ,
output wire HSELD
);
/*
* Address Space
* Subordinate1 0x0000_0000~0x0000_ffff 64KB
* Subordinate2 0x0001_0000~0x0001_ffff 64KB
* default 0x0002_0000~0xffff_ffff 4194242KB
*/
reg hsel1;
reg hsel2;
reg hseld;
always @(*) begin
case(HADDR[31:16])
16'h0000:
{hseld, hsel2, hsel1} = 3'b001;
16'h0001:
{hseld, hsel2, hsel1} = 3'b010;
default:
{hseld, hsel2, hsel1} = 3'b100;
endcase
end
assign HSEL1 = hsel1;
assign HSEL2 = hsel2;
assign HSELD = hseld;
endmodule
7.1.3 Subordinate
`timescale 1ns/1ps
module subordinate1 #(
parameter DATA_WIDTH = 32 ,
parameter ADDR_WIDTH = 32
)(
input wire HCLK ,
input wire HRESETn ,
input wire [ADDR_WIDTH-1:0] HADDR ,
input wire [2:0] HBURST ,
input wire [2:0] HSIZE ,
input wire [1:0] HTRANS ,
input wire [DATA_WIDTH-1:0] HWDATA ,
input wire HWRITE ,
input wire HSELx ,
`ifdef RARELY_USED_SIGNAL
input wire HMASTLOCK ,
input wire [3:0] HPROT ,
input wire HNONSEC ,
input wire HEXCL ,
input wire [3:0] HMASTER ,
input wire [DATA_WIDTH/8-1:0] HWSTRB ,
`endif
output wire [DATA_WIDTH-1:0] HRDATA ,
output wire HREADYOUT ,
output wire HRESP
`ifdef RARELY_USED_SIGNAL
output wire HEXOKAY ,
`endif
);
localparam IDLE = 3'b000;
localparam STAGE1 = 3'b001;
localparam STAGE2 = 3'b010;
localparam STAGE3 = 3'b011;
localparam STAGE4 = 3'b100;
localparam STAGE5 = 3'b101;
localparam HIDLE = 2'b00;
localparam BUSY = 2'b01;
localparam NONSEQ = 2'b10;
localparam SEQ = 2'b11;
localparam BYTE = 3'b000;
localparam HALFWORD = 3'b001;
localparam WORD = 3'b010;
localparam DOUBLEWORD = 3'b011;
localparam WORD_LINE_4 = 3'b100;
localparam WORD_LINE_8 = 3'b101;
localparam USER_SET1 = 3'b110;
localparam USER_SET2 = 3'b111;
reg [DATA_WIDTH-1:0] reg0; //addr = 0
reg [DATA_WIDTH-1:0] reg1; //addr = 4
reg [DATA_WIDTH-1:0] reg2; //addr = 8
reg [DATA_WIDTH-1:0] reg3; //addr = c
reg [DATA_WIDTH-1:0] reg4; //addr = 10
reg [DATA_WIDTH-1:0] reg5; //addr = 14
reg [DATA_WIDTH-1:0] reg6; //addr = 18
reg [DATA_WIDTH-1:0] reg7; //addr = 1c
/******************************************************/
/*
* latch HSIZE HBURST HADDR HWRITE
*/
reg [ADDR_WIDTH-1:0] addr;
reg [2:0] size;
reg [2:0] burst;
reg write;
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 addr <= 'd0;
end else if(HSELx && (HTRANS == NONSEQ || HTRANS == SEQ) && HREADYOUT) begin
#1 addr <= HADDR;
end else begin
#1 addr <= addr;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 size <= 3'd0;
end else if(HSELx && HTRANS == NONSEQ && HREADYOUT) begin
#1 size <= HSIZE;
end else begin
#1 size <= size;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 burst <= 3'd0;
end else if(HSELx && HTRANS == NONSEQ && HREADYOUT) begin
#1 burst <= HBURST;
end else begin
#1 burst <= burst;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 write <= 3'd0;
end else if(HSELx && HTRANS == NONSEQ && HREADYOUT) begin
#1 write <= HWRITE;
end else begin
#1 write <= write;
end
end
/******************************************************/
/******************************************************/
reg [2:0] state;
reg [2:0] nextstate;
always @(*) begin
case(state)
IDLE : if(HSELx && HTRANS == NONSEQ) nextstate = STAGE1;
STAGE1 : nextstate = STAGE2;
STAGE2 : nextstate = STAGE3;
STAGE3 : nextstate = STAGE4;
STAGE4 : nextstate = STAGE5;
STAGE5 : if(HTRANS == IDLE || !HSELx) nextstate = IDLE;
else if(HTRANS == SEQ && HREADYOUT) nextstate = STAGE1;
default: nextstate = IDLE;
endcase
end
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 state <= IDLE; else #1 state <= nextstate;
/******************************************************/
/******************************************************/
/*
* write register
*/
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg0 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'h0) begin
case(size)
BYTE: #1 reg0[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg0[15:0] <= HWDATA[15:0] ;
WORD: #1 reg0[31:0] <= HWDATA[31:0] ;
default: #1 reg0 <= HWDATA ;
endcase
end else begin
#1 reg0 <= reg0;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg1 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'h4) begin
case(size)
BYTE: #1 reg1[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg1[15:0] <= HWDATA[15:0] ;
WORD: #1 reg1[31:0] <= HWDATA[31:0] ;
default: #1 reg1 <= HWDATA ;
endcase
end else begin
#1 reg1 <= reg1;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg2 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'h8) begin
case(size)
BYTE: #1 reg2[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg2[15:0] <= HWDATA[15:0] ;
WORD: #1 reg2[31:0] <= HWDATA[31:0] ;
default: #1 reg2 <= HWDATA ;
endcase
end else begin
#1 reg2 <= reg2;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg3 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'hc) begin
case(size)
BYTE: #1 reg3[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg3[15:0] <= HWDATA[15:0] ;
WORD: #1 reg3[31:0] <= HWDATA[31:0] ;
default: #1 reg3 <= HWDATA ;
endcase
end else begin
#1 reg3 <= reg3;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg4 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'h10) begin
case(size)
BYTE: #1 reg4[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg4[15:0] <= HWDATA[15:0] ;
WORD: #1 reg4[31:0] <= HWDATA[31:0] ;
default: #1 reg4 <= HWDATA ;
endcase
end else begin
#1 reg4 <= reg4;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg5 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'h14) begin
case(size)
BYTE: #1 reg5[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg5[15:0] <= HWDATA[15:0] ;
WORD: #1 reg5[31:0] <= HWDATA[31:0] ;
default: #1 reg5 <= HWDATA ;
endcase
end else begin
#1 reg5 <= reg5;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg6 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'h18) begin
case(size)
BYTE: #1 reg6[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg6[15:0] <= HWDATA[15:0] ;
WORD: #1 reg6[31:0] <= HWDATA[31:0] ;
default: #1 reg6 <= HWDATA ;
endcase
end else begin
#1 reg6 <= reg6;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg7 <= 'd0;
end else if(state == STAGE5 && write && addr[15:0] == 16'h1c) begin
case(size)
BYTE: #1 reg7[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg7[15:0] <= HWDATA[15:0] ;
WORD: #1 reg7[31:0] <= HWDATA[31:0] ;
default: #1 reg7 <= HWDATA ;
endcase
end else begin
#1 reg7 <= reg7;
end
end
/******************************************************/
/******************************************************/
/*
* read register
*/
assign HRDATA = (state == STAGE5 && !write && addr[15:0] == 16'h0) ? reg0 :
(state == STAGE5 && !write && addr[15:0] == 16'h4) ? reg1 :
(state == STAGE5 && !write && addr[15:0] == 16'h8) ? reg2 :
(state == STAGE5 && !write && addr[15:0] == 16'hc) ? reg3 :
(state == STAGE5 && !write && addr[15:0] == 16'h10) ? reg4 :
(state == STAGE5 && !write && addr[15:0] == 16'h14) ? reg5 :
(state == STAGE5 && !write && addr[15:0] == 16'h18) ? reg6 :
(state == STAGE5 && !write && addr[15:0] == 16'h1c) ? reg7 : 'd0;
/******************************************************/
assign HREADYOUT = (state == IDLE || state == STAGE5);
endmodule
`timescale 1ns/1ps
module subordinate2 #(
parameter DATA_WIDTH = 32 ,
parameter ADDR_WIDTH = 32
)(
input wire HCLK ,
input wire HRESETn ,
input wire [ADDR_WIDTH-1:0] HADDR ,
input wire [2:0] HBURST ,
input wire [2:0] HSIZE ,
input wire [1:0] HTRANS ,
input wire [DATA_WIDTH-1:0] HWDATA ,
input wire HWRITE ,
input wire HSELx ,
`ifdef RARELY_USED_SIGNAL
input wire HMASTLOCK ,
input wire [3:0] HPROT ,
input wire HNONSEC ,
input wire HEXCL ,
input wire [3:0] HMASTER ,
input wire [DATA_WIDTH/8-1:0] HWSTRB ,
`endif
output wire [DATA_WIDTH-1:0] HRDATA ,
output wire HREADYOUT ,
output wire HRESP
`ifdef RARELY_USED_SIGNAL
output wire HEXOKAY ,
`endif
);
localparam IDLE = 1'b0;
localparam STAGE1 = 1'b1;
localparam HIDLE = 2'b00;
localparam BUSY = 2'b01;
localparam NONSEQ = 2'b10;
localparam SEQ = 2'b11;
localparam BYTE = 3'b000;
localparam HALFWORD = 3'b001;
localparam WORD = 3'b010;
localparam DOUBLEWORD = 3'b011;
localparam WORD_LINE_4 = 3'b100;
localparam WORD_LINE_8 = 3'b101;
localparam USER_SET1 = 3'b110;
localparam USER_SET2 = 3'b111;
reg [DATA_WIDTH-1:0] reg0; //addr = 0
reg [DATA_WIDTH-1:0] reg1; //addr = 4
reg [DATA_WIDTH-1:0] reg2; //addr = 8
reg [DATA_WIDTH-1:0] reg3; //addr = c
reg [DATA_WIDTH-1:0] reg4; //addr = 10
reg [DATA_WIDTH-1:0] reg5; //addr = 14
reg [DATA_WIDTH-1:0] reg6; //addr = 18
reg [DATA_WIDTH-1:0] reg7; //addr = 1c
/******************************************************/
/*
* latch HSIZE HBURST HADDR HWRITE
*/
reg [ADDR_WIDTH-1:0] addr;
reg [2:0] size;
reg [2:0] burst;
reg write;
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 addr <= 'd0;
end else if(HSELx && (HTRANS == NONSEQ || HTRANS == SEQ) && HREADYOUT) begin
#1 addr <= HADDR;
end else begin
#1 addr <= addr;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 size <= 3'd0;
end else if(HSELx && HTRANS == NONSEQ && HREADYOUT) begin
#1 size <= HSIZE;
end else begin
#1 size <= size;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 burst <= 3'd0;
end else if(HSELx && HTRANS == NONSEQ && HREADYOUT) begin
#1 burst <= HBURST;
end else begin
#1 burst <= burst;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 write <= 3'd0;
end else if(HSELx && HTRANS == NONSEQ && HREADYOUT) begin
#1 write <= HWRITE;
end else begin
#1 write <= write;
end
end
/******************************************************/
/******************************************************/
reg state;
reg nextstate;
always @(*) begin
case(state)
IDLE : if(HSELx && HTRANS == NONSEQ) nextstate = STAGE1;
STAGE1 : if(HTRANS == IDLE || !HSELx) nextstate = IDLE;
else if(HTRANS == SEQ && HREADYOUT) nextstate = STAGE1;
default: nextstate = IDLE;
endcase
end
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) #1 state <= IDLE; else #1 state <= nextstate;
/******************************************************/
/******************************************************/
/*
* write register
*/
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg0 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'h0) begin
case(size)
BYTE: #1 reg0[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg0[15:0] <= HWDATA[15:0] ;
WORD: #1 reg0[31:0] <= HWDATA[31:0] ;
default: #1 reg0 <= HWDATA ;
endcase
end else begin
#1 reg0 <= reg0;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg1 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'h4) begin
case(size)
BYTE: #1 reg1[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg1[15:0] <= HWDATA[15:0] ;
WORD: #1 reg1[31:0] <= HWDATA[31:0] ;
default: #1 reg1 <= HWDATA ;
endcase
end else begin
#1 reg1 <= reg1;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg2 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'h8) begin
case(size)
BYTE: #1 reg2[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg2[15:0] <= HWDATA[15:0] ;
WORD: #1 reg2[31:0] <= HWDATA[31:0] ;
default: #1 reg2 <= HWDATA ;
endcase
end else begin
#1 reg2 <= reg2;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg3 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'hc) begin
case(size)
BYTE: #1 reg3[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg3[15:0] <= HWDATA[15:0] ;
WORD: #1 reg3[31:0] <= HWDATA[31:0] ;
default: #1 reg3 <= HWDATA ;
endcase
end else begin
#1 reg3 <= reg3;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg4 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'h10) begin
case(size)
BYTE: #1 reg4[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg4[15:0] <= HWDATA[15:0] ;
WORD: #1 reg4[31:0] <= HWDATA[31:0] ;
default: #1 reg4 <= HWDATA ;
endcase
end else begin
#1 reg4 <= reg4;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg5 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'h14) begin
case(size)
BYTE: #1 reg5[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg5[15:0] <= HWDATA[15:0] ;
WORD: #1 reg5[31:0] <= HWDATA[31:0] ;
default: #1 reg5 <= HWDATA ;
endcase
end else begin
#1 reg5 <= reg5;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg6 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'h18) begin
case(size)
BYTE: #1 reg6[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg6[15:0] <= HWDATA[15:0] ;
WORD: #1 reg6[31:0] <= HWDATA[31:0] ;
default: #1 reg6 <= HWDATA ;
endcase
end else begin
#1 reg6 <= reg6;
end
end
always @(posedge HCLK or negedge HRESETn) begin
if(!HRESETn) begin
#1 reg7 <= 'd0;
end else if(state == STAGE1 && write && addr[15:0] == 16'h1c) begin
case(size)
BYTE: #1 reg7[7:0] <= HWDATA[7:0] ;
HALFWORD: #1 reg7[15:0] <= HWDATA[15:0] ;
WORD: #1 reg7[31:0] <= HWDATA[31:0] ;
default: #1 reg7 <= HWDATA ;
endcase
end else begin
#1 reg7 <= reg7;
end
end
/******************************************************/
/******************************************************/
/*
* read register
*/
assign HRDATA = (state == STAGE1 && !write && addr[15:0] == 16'h0) ? reg0 :
(state == STAGE1 && !write && addr[15:0] == 16'h4) ? reg1 :
(state == STAGE1 && !write && addr[15:0] == 16'h8) ? reg2 :
(state == STAGE1 && !write && addr[15:0] == 16'hc) ? reg3 :
(state == STAGE1 && !write && addr[15:0] == 16'h10) ? reg4 :
(state == STAGE1 && !write && addr[15:0] == 16'h14) ? reg5 :
(state == STAGE1 && !write && addr[15:0] == 16'h18) ? reg6 :
(state == STAGE1 && !write && addr[15:0] == 16'h1c) ? reg7 : 'd0;
/******************************************************/
assign HREADYOUT = (state == IDLE || state == STAGE1);
endmodule
7.1.4 Multiplexor
`timescale 1ns/1ps
module multiplexor #(
parameter DATA_WIDTH = 32
)(
input wire HCLK ,
input wire HRESETn ,
input wire HSEL1 ,
input wire HSEL2 ,
input wire HSELD ,
input wire HREADYIN1 ,
input wire HRESPIN1 ,
input wire [DATA_WIDTH-1:0] HRDATAIN1 ,
input wire HREADYIN2 ,
input wire HRESPIN2 ,
input wire [DATA_WIDTH-1:0] HRDATAIN2 ,
input wire HREADYIND ,
input wire HRESPIND ,
input wire [DATA_WIDTH-1:0] HRDATAIND ,
output wire [DATA_WIDTH-1:0] HRDATAOUT ,
output wire HRESPOUT ,
output wire HREADYOUT
);
localparam IDLE = 2'b00;
localparam SUB1 = 2'b01;
localparam SUB2 = 2'b10;
localparam SUBD = 2'b11;
reg sel1, sel2, seld;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) sel1 <= 1'b0; else if(HREADYIN1) sel1 <= HSEL1; else sel1 <= sel1;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) sel2 <= 1'b0; else if(HREADYIN2) sel2 <= HSEL2; else sel2 <= sel2;
always @(posedge HCLK or negedge HRESETn) if(!HRESETn) seld <= 1'b0; else if(HREADYIND) seld <= HSELD; else seld <= seld;
assign HREADYOUT = (sel1) ? HREADYIN1 :
(sel2) ? HREADYIN2 :
(seld) ? HREADYIND : 1'b1;
assign HRDATAOUT = (sel1) ? HRDATAIN1 :
(sel2) ? HRDATAIN2 :
(seld) ? HRDATAIND : 'd0;
endmodule
7.1.5 Testbench
`timescale 1ns/1ps
module testbench();
localparam DATA_WIDTH = 32;
localparam ADDR_WIDTH = 32;
//size
localparam BYTE = 3'd0;
localparam HALFWORD = 3'd1;
localparam WORD = 3'd2;
//burst
localparam SINGLE = 3'b000;
localparam INCR = 3'b001;
//global
reg HCLK ;
reg HRESETn ;
//ahb
wire [ADDR_WIDTH-1:0] HADDR ;
wire [2:0] HBURST ;
wire [2:0] HSIZE ;
wire [1:0] HTRANS ;
wire [DATA_WIDTH-1:0] HWDATA ;
wire HWRITE ;
reg cmd_start ;
reg [DATA_WIDTH-1:0] cmd_wdata ;
reg [ADDR_WIDTH-1:0] cmd_addr ;
reg cmd_write ;
reg [2:0] cmd_size ;
reg [2:0] cmd_burst ;
reg [3:0] cmd_length ;
reg cmd_busy ;
reg cmd_stop ;
wire [DATA_WIDTH-1:0] cmd_rdata ;
wire cmd_error ;
wire cmd_ready ;
//decoder
wire HSEL1 ;
wire HSEL2 ;
//subordinate
wire [DATA_WIDTH-1:0] HRDATA1 ;
wire HREADY1 ;
wire [DATA_WIDTH-1:0] HRDATA2 ;
wire HREADY2 ;
//multiplexor
wire [DATA_WIDTH-1:0] HRDATA ;
wire HRESP ;
wire HREADY ;
wire HRESETns ;
rstn_sync u_rstn_sync(
.clk_i (HCLK) ,
.rstn_async_i (HRESETn) ,
.rstn_sync_o (HRESETns)
);
ahb #(
.DATA_WIDTH(DATA_WIDTH),
.ADDR_WIDTH(ADDR_WIDTH)
)u_ahb(
.HCLK (HCLK) ,
.HRESETn (HRESETns) ,
.HADDR (HADDR) ,
.HBURST (HBURST) ,
.HSIZE (HSIZE) ,
.HTRANS (HTRANS) ,
.HWDATA (HWDATA) ,
.HWRITE (HWRITE) ,
.HRDATA (HRDATA) ,
.HREADY (HREADY) ,
.HRESP () ,
.cmd_start (cmd_start) ,
.cmd_wdata (cmd_wdata) ,
.cmd_addr (cmd_addr) ,
.cmd_write (cmd_write) ,
.cmd_size (cmd_size) ,
.cmd_burst (cmd_burst) ,
.cmd_length (cmd_length),
.cmd_busy (cmd_busy) ,
.cmd_stop (cmd_stop) ,
.cmd_rdata (cmd_rdata) ,
.cmd_error (cmd_error) ,
.cmd_ready (cmd_ready)
);
decoder #(
.ADDR_WIDTH(ADDR_WIDTH)
)u_decoder(
.HADDR (HADDR) ,
.HSEL1 (HSEL1) ,
.HSEL2 (HSEL2) ,
.HSELD ()
);
subordinate1 #(
.DATA_WIDTH(DATA_WIDTH),
.ADDR_WIDTH(ADDR_WIDTH)
)u_subordinate1(
.HCLK (HCLK) ,
.HRESETn (HRESETns) ,
.HADDR (HADDR) ,
.HBURST (HBURST) ,
.HSIZE (HSIZE) ,
.HTRANS (HTRANS) ,
.HWDATA (HWDATA) ,
.HWRITE (HWRITE) ,
.HSELx (HSEL1) ,
.HRDATA (HRDATA1) ,
.HREADYOUT (HREADY1) ,
.HRESP ()
);
subordinate2 #(
.DATA_WIDTH(DATA_WIDTH),
.ADDR_WIDTH(ADDR_WIDTH)
)u_subordinate2(
.HCLK (HCLK) ,
.HRESETn (HRESETns) ,
.HADDR (HADDR) ,
.HBURST (HBURST) ,
.HSIZE (HSIZE) ,
.HTRANS (HTRANS) ,
.HWDATA (HWDATA) ,
.HWRITE (HWRITE) ,
.HSELx (HSEL2) ,
.HRDATA (HRDATA2) ,
.HREADYOUT (HREADY2) ,
.HRESP ()
);
multiplexor #(
.DATA_WIDTH(DATA_WIDTH)
)u_multiplexor(
.HCLK (HCLK) ,
.HRESETn (HRESETns) ,
.HSEL1 (HSEL1) ,
.HSEL2 (HSEL2) ,
.HSELD () ,
.HREADYIN1 (HREADY1) ,
.HRESPIN1 () ,
.HRDATAIN1 (HRDATA1) ,
.HREADYIN2 (HREADY2) ,
.HRESPIN2 () ,
.HRDATAIN2 (HRDATA2) ,
.HREADYIND () ,
.HRESPIND () ,
.HRDATAIND () ,
.HRDATAOUT (HRDATA) ,
.HRESPOUT () ,
.HREADYOUT (HREADY)
);
initial begin
$fsdbDumpfile("ahb.fsdb");
$fsdbDumpvars(0, testbench);
end
always #10 HCLK = ~HCLK;
initial begin
HCLK = 1'b0;
HRESETn = 1'b1;
cmd_start = 1'b0;
cmd_wdata = 'd0;
cmd_addr = 'd0;
cmd_write = 1'b0;
cmd_size = 3'd0;
cmd_burst = 3'd0;
cmd_length = 4'd0;
cmd_stop = 1'b0;
cmd_busy = 1'b0;
#55 HRESETn = 1'b0;
#50 HRESETn = 1'b1;
repeat(10) @(posedge HCLK);
/*case0(32'h0000_0000, 32'd100);
case0(32'h0001_0000, 32'd200);
case1(32'h0000_0000);
case1(32'h0001_0000);*/
case2(32'h0000_0000, 32'd0);
case2(32'h0001_0000, 32'd0);
case3(32'h0000_0000);
case3(32'h0001_0000);
repeat(2) @(posedge HCLK);
$finish;
end
task case0(input [ADDR_WIDTH-1:0] addr, input [DATA_WIDTH-1:0] data); //Single write
@(posedge HCLK) #1;
cmd_start = 1'b1;
@(posedge HCLK) #1;
cmd_start = 1'b0;
cmd_size = WORD;
cmd_burst = SINGLE;
cmd_write = 1;
cmd_addr = addr;
@(posedge HCLK) #1;
cmd_wdata = data;
cmd_size = $urandom();
cmd_burst = $urandom();
cmd_write = $urandom();
cmd_addr = $urandom();
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY);
endtask
task case1(input [ADDR_WIDTH-1:0] addr); //Single read
@(posedge HCLK) #1;
cmd_start = 1'b1;
@(posedge HCLK) #1;
cmd_start = 1'b0;
cmd_size = WORD;
cmd_burst = SINGLE;
cmd_write = 0;
cmd_addr = addr;
@(posedge HCLK) #1;
cmd_wdata = $urandom();
cmd_size = $urandom();
cmd_burst = $urandom();
cmd_write = $urandom();
cmd_addr = $urandom();
#1 wait(HREADY);
endtask
task case2(input [ADDR_WIDTH-1:0] addr, input [DATA_WIDTH-1:0] data);//Incr write
@(posedge HCLK) #1;
cmd_start = 1'b1;
@(posedge HCLK) #1;
cmd_start = 1'b0;
cmd_size = WORD;
cmd_burst = INCR;
cmd_write = 1;
cmd_addr = addr;
#1 wait(HREADY); //write reg0
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY); //write reg1
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY); //write reg2
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY); //write reg3
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY); //write reg4
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY); //write reg5
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY); //write reg6
@(posedge HCLK) #1;
cmd_wdata = $urandom();
#1 wait(HREADY); cmd_stop = 1'b1; //write reg7
@(posedge HCLK) #1;
cmd_stop = 1'b0;
cmd_wdata = $urandom();
#1 wait(HREADY);
endtask
task case3(input [ADDR_WIDTH-1:0] addr);//Incr read
@(posedge HCLK) #1;
cmd_start = 1'b1;
@(posedge HCLK) #1;
cmd_start = 1'b0;
cmd_size = WORD;
cmd_burst = INCR;
cmd_write = 0;
cmd_addr = addr;
#1 wait(HREADY); //read reg0
@(posedge HCLK) #1;
#1 wait(HREADY); //read reg1
@(posedge HCLK) #1;
#1 wait(HREADY); //read reg2
@(posedge HCLK) #1;
#1 wait(HREADY); //read reg3
@(posedge HCLK) #1;
#1 wait(HREADY); //read reg4
@(posedge HCLK) #1;
#1 wait(HREADY); //read reg5
@(posedge HCLK) #1;
#1 wait(HREADY); //read reg6
@(posedge HCLK) #1;
#1 wait(HREADY); cmd_stop = 1'b1; //read reg7
@(posedge HCLK) #1;
cmd_stop = 1'b0;
#1 wait(HREADY);
endtask
endmodule
7.2 仿真
7.2.1 Single读写传输
先单次写入100到Subordinate1的reg0,再单次写入200到Subordinate2的reg0,再单次读Subordinate1的reg0,最后单次读Subordinate2的reg0。其中Subordinate1的读写均有4个等待周期,而Subordinate2没有等待周期

7.2.2 Incr读写传输
先写Subordinate1的reg0~reg7,再写Subordinate2的reg0~reg7,再读Subordinate1的reg0~reg7,再读Subordinate2的reg0~reg7,

声明
参考自ARM官方的AMBA 5 – Arm®下的AHB文档
浙公网安备 33010602011771号