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 基本传输

一次传输分成两个阶段:

  1. 地址阶段:一个时钟周期,但是如果上一次传输的数据阶段有多周期,那么这次的地址阶段就有多周期;
  2. 数据阶段:多个时钟周期,取决于HREADY。

image

第二次传输的地址阶段和第一次传输的数据阶段重合,实现高性能流水线传输。

3.2 传输类型

传输类型由HTRANS[1:0]决定,

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

image

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的位宽。在地址阶段准备好,并在整个传输期间保持不变。HSIZEHBURST信号共同决定了回环突发传输的地址边界。

3.4 写选通

写数据选通是一项可选功能,使主控器能够执行仅更新寄存器部分有效字节的写传输。

AHB协议中写数据选通信号的适用规则如下:

  1. 对于数据位宽小于总线位宽的传输,HSIZE和HADDR共同决定哪些字节通道为活跃通道;
    就是说对应有效字节的选通信号才有效;
  2. 允许所有写选通信号均取消置位的传输,在此情况下,该传输不会写入任何字节;
  3. 写选通信号与写数据通道的映射关系不依赖于系统的字节序;
  4. 对于读传输,建议将写选通信号全部取消置位
  5. 允许在写突发传输的各个数据节拍之间改变写选通信号的设置。

3.5 突发操作

本协议定义了4拍、8拍及16拍的突发传输、未定义长度的突发传输以及单次传输。协议支持以下两种突发模式:

  1. 递增突发:访问连续的存储位置,突发中的每次传输地址均为前一次地址的递增;
  2. 回环突发:在跨越特定地址边界时进行地址回绕。该地址边界由突发传输的拍数(由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示例

image
image
image

3.6 等待传输

从属器在需要更多时间提供或采样数据时,通过驱动 HREADYOUT信号插入等待状态。

在传输被等待状态延长期间,主控器对传输类型与地址的修改权限将受到限制:

  1. 等待状态期间的传输类型变更规则;
  2. 等待状态期间的地址变更规则。

3.6.1 等待状态期间传输类型改变

从机请求等待时,主机符合下列情况,才能改变传输类型:

  1. 主机的传输类型时IDLE;
  2. 主机的传输类型是固定长度burst期间的BUSY;
  3. 主机的传输类型是不定长度burst期间的BUSY。

3.6.1.1 IDLE传输

image

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

3.6.1.2 固定长度burst期间的BUSY

image

允许从BUSY改为SEQ。

3.6.1.3 不定长度burst期间的BUSY

image

允许从BUSY改为NONSEQ。

3.6.2 等待状态期间地址改变

3.6.2.1 IDLE传输期间

image

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

3.6.2.2 错误响应

image

在处于等待状态的传输过程中,若从属器返回ERROR响应,则允许主控器在HREADY为低电平时改变地址。

四、总线互连

4.1 互连

互连组件为系统中的主控器与从属器提供连接支持。

4.2 地址译码

image

地址译码器为总线上的每个从属器生成一个选通信号 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 读数据与响应的多路选择器

image

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没有等待周期

image

7.2.2 Incr读写传输

先写Subordinate1的reg0~reg7,再写Subordinate2的reg0~reg7,再读Subordinate1的reg0~reg7,再读Subordinate2的reg0~reg7,

image

声明

参考自ARM官方的AMBA 5 – Arm®下的AHB文档

posted @ 2025-12-05 15:25  Holybanban  阅读(11)  评论(0)    收藏  举报