【AHB总线协议】从机接口的Verilog实现

目录

一、实验目的

二、实验工具及环境

三、实验内容及步骤

1.实验1.1 从机接口模块的设计

2.实验1.2 十六位增量突发的仿真

四、实验结论及分析


一、实验目的

        学习并掌握基本的AHB总线传输协议;使用Verilog HDL语言对AHB从机驱动模块进行设计,并满足正常的时序要求,使从机响应八位增量突发和十六位增量突发;掌握Modelsim仿真工具的使用。

二、实验工具及环境

1.Windows 10;

2.Modelsim 10.5;

3.Vivado 20.1;

4.Visual Studio Code。

三、实验内容及步骤

1.实验1.1 从机接口模块的设计

本实验需要设计一个AHB从机接口模块,提供AHB总线到从机Memory的接口,其框图如下图所示:

 为了简化设计,不需要全面考虑所有可能的接口配置情况,只需要考虑HSIZE=3’b010即八位(一个字节)传输、HPROT=4’b0001即数据/预取指、HRESP为OKAY即传输成功的情况。

实验材料中给出的testbench验证从机在八位增量突发模式下的工作情况,通过阅读实验材料发现八位增量突发时序图如下所示:

地址寄存:

在AHB总线传输协议中,进行一次数据传输的时候需要先发送地址再在下一个时钟周期发送或接收响应的数据,即先地址周期后数据周期,时序图如下图所示:

 所以AHB从机在应当在前一个时钟周期读取地址,在后一个时钟周期采样数据。为了保证Memory在读写数据的时候地址的正确性,需要在前一个上升沿到来的时候寄存地址总线HADDR上的数据,因此使用一个地址寄存器address。核心代码如下所示:

地址对齐:

由于AHB总线宽度为32位,而从机为8位寻址,要使得数据在一个八位增量突发的周期顺次写入相邻的地址,就需要使得地址总线寻址HADDR与Memory寻址addr相对齐。又由于32/8 = 4,因此HAB地址总线HADDR上传输的地址是以4为增量递增连续八个地址周期,在本次实验中,为32’h00000000到32’h0000001c;Memory地址则是以一为增量递增的,在本实验中为8’h00到8’h07。

对应的从机则需要将从地址总线HADDR上接收到的地址数据除4来进行这一个映射,但是除法器非常消耗硬件资源同时会造成速度下降,因此采用右移两位来替代除4的运算,代码为

AHB_Slave状态机:

使用dot自动生成AHB_Slave状态机状态转移图如下图所示

 根据需求使用了三段式Moore状态机来实现这个AHB总线的从机,命名为AHB_Slave,并根据从机未选中、从机选中进行写传输、从机选中进行读传输分为三个状态:IDLE、MEM_W、MEM_R,并使用独热码表示:

 三段式状态机第一段为状态转移同步逻辑,通过时钟信号HCLK和异步复位信号HRESETn进行状态的转移:

 三段式状态机的第二段为产生下一个状态的组合逻辑,根据HWRITE以及当前的状态确定下一个状态。根据HWRITE信号的定义,当HWRITE=1,状态跳转到MEM_W进行写操作;当HWRITE=0,状态跳转到MEM_R进行读操作。

 三段式状态机的第三段为产生输出的组合逻辑,根据当前状态对输出信号HREADY、addr、dout、HRDATA、wr_en进行相应的输出。在IDLE状态时,由于从机未被选中,所以将HREADY置位为1表示从机空闲,addr置位为缺省状态8’h00;当从机处于MEM_W状态时需要对Memory进行操作,将寄存器address在地址周期寄存的地址送到addr,写数据总线HWDATA上的数据送到dout,在此时HRDATA没有使用置位为缺省32’d0;当从机处于MEM_R状态时需要对Memory进行读操作,将寄存器address在地址周期寄存的地址送到addr,将从Memory的din端口读出的数据送往读数据总线HRDATA,此时dout未使用置位为缺省的32’d0。具体代码如下所示:

 详细代码见【附录】

2.实验1.2 十六位增量突发的仿真

本实验与实验1.1唯一的区别就在于实验1.1的testbench验证的是八位增量突发而实验1.2验证的是从机十六位增量突发,因此只需要改变testbench产生相应的激励就可以实现。

根据testbench.v代码可以发现,测试平台也是一个状态机,通过计数器cnt来控制对从机的激励的发生。对于八位增量突发来说,cnt从0计数到7产生连续八拍时钟周期的控制信号以达到模拟八位增量突发的效果。同理,只需要对cnt增加一位位宽使其在达到10000的时候不溢出,同时将所有4’h8替换为5’d16,再将HBURST由原先八位增量突发对应的101改成十六位增量突发对应的111即可完成对testbench的改写,仿真运行结果见第四部分。

四、实验结论及分析

实验1.1 八位增量突发的仿真结果

如上图所示,测试平台一共发起三次读写操作,其中第一次是对本实验所完成的从机模块进行写操作(other_slave信号为0、HWRITE信号为1)、第二次是对其他从机模块进行写操作(other_slave信号为1、HWRITE信号为1)、第三次则是对本实验所完成的从机模块进行读操作(other_slave信号为0、HWRITE信号为0),其仿真结果分析如下:

 第一次传输:AHB_Slave的写操作

 仿真结果如图所示,当HSEL和HWRITE都为高的时候从机进入写状态,在八个连续的总线周期过程中,HADDR和HWDATA发送地址和对应的数据。可以观察到HADDR从32’h00000000一直变化到32’h00000020,addr从8’h00一直变化到8’h08,这表明从机接口对Memory连续8个地址进行数据写操作,并使能wr_mem为高电平写操作,在addr变化的同时wr_data依次送入32’h00000002到32’h00000010一共8个数据,且地址相位在前,数据相位在后,正确的完成了八位增量突发的响应。其Memory List内容如下图所示:

 通过观察发现,从8’h00到8’h10连续8个地址上对应32’h00000002到32’h0000000e八个数据,这表明AHB_Slave正确完成了八位增量突发的响应。

第二次传输:非AHB_Slave响应

 仿真结果如图所示,此次传输HSEL为低电平表示未选中AHB_Slave从机。通过观察wr_data可以发现恒为32’h00000000,表示从机接口AHB_Slave并没有对Memory进行写操作,满足设计要求。

第三次传输:AHB_Slave的读操作

 仿真结果如图所示,此次传输HSEL为高电平表示从机AHB_Slave被选中且HWRITE为低电平表示读数据传输。通过观察HADDR和HRDATA可以发现,在前一个周期(地址周期)HADDR发送读数据的地址后,AHB_Slave将HADDR做一个映射到Memory地址addr并使能wr_mem为低电平读操作,在addr变化的同时,Memory将数据从din放入读数据总线HRDATA上,由此可以看到在后一个周期(数据周期)上有对应的正确数据,因此AHB_Slave满足读操作的时序要求,功能完备。

实验1.2 十六位增量突发的仿真结果

第一次传输:AHB_Slave的写操作

 仿真结果如图所示,当HSEL和HWRITE都为高的时候从机进入写状态,在十六个连续的总线周期过程中,HADDR和HWDATA发送地址和对应的数据。可以观察到addr从8’h00一直变化到8’h0f,这表明从机接口对Memory连续16个地址进行数据写操作,并使能wr_mem为高电平写操作,在addr变化的同时wr_data依次送入32’h00000002到32’h00000020一共16个数据,且地址相位在前,数据相位在后,正确的完成了十六位增量突发的响应。其Memory List内容如下图所示:

通过观察发现,从8’h00到8’h0f连续16个地址上对应32’h00000002到32’h00000020十六个数据,这表明AHB_Slave正确完成了十六位增量突发的响应。

第二次传输:非AHB_Slave响应

 仿真结果如图所示,此次传输HSEL为低电平表示未选中AHB_Slave从机。通过观察wr_data可以发现恒为32’h00000000,表示从机接口AHB_Slave并没有对Memory进行写操作,满足设计要求。

第三次传输:AHB_Slave的读操作

 仿真结果如图所示,此次传输HSEL为高电平表示从机AHB_Slave被选中且HWRITE为低电平表示读数据传输。通过观察HADDR和HRDATA可以发现,在前一个周期(地址周期)HADDR发送读数据的地址后,AHB_Slave将HADDR做一个映射到Memory地址addr并使能wr_mem为低电平读操作,在addr变化的同时,Memory将数据从din放入读数据总线HRDATA上,由此可以看到在后一个周期(数据周期)上有对应的正确数据,因此AHB_Slave满足读操作的时序要求,功能完备。

【附录】

  1. AHB_Slave.v
module AHB_Slave(
    input HCLK,
    input HRESETn,
    input HSEL,//从机选择
    input [3:0]HPROT,//保护控制 恒为4'b0001
    input [2:0]HSIZE,//传输大小 恒为3'b010
    input HWRITE,//传输方向:1写0读
    input [2:0]HBURST,//突发类型
    input [1:0]HTRANS,//传输类型
    input [31:0]HADDR,//地址总线
    input [31:0]HWDATA,//写数据总线
    output reg [31:0]HRDATA,//读数据总线
    output reg HREADY,//传输完成标志
    input [1:0]HRESP,//传输相应 恒为OKEY 2'b00
    
    output reg [7:0]addr,//AHB_Salve地址输出
    output reg wr_en,//Mem写:1写0读
    output reg [31:0]dout,//AHB_Salve数据输出
    input [31:0]din//AHB_Salve数据输入
    );
    //状态寄存器定义
    reg [2:0]cstate,nstate;
    //寄存地址
    reg [31:0]address;
    //状态机状态定义
    parameter IDLE  = 3'b100;
    parameter MEM_W = 3'b010;
    parameter MEM_R = 3'b001;

    //地址寄存逻辑
    always @(posedge HCLK or negedge HRESETn) begin
        if(!HRESETn)begin
            address <= 8'd0;
        end
        else begin
            address <= HADDR[9:2];//地址映射
        end
    end
    
    //状态转移同步逻辑
    always@(posedge HCLK or negedge HRESETn)begin
        if(!HRESETn)begin
            cstate <= IDLE;
        end
        else begin
            cstate <= nstate;
        end
    end
    //产生下一个状态组合逻辑
    always@(*)begin
        if(!HRESETn)begin//从机复位
            nstate = IDLE;
        end
        else if(HSEL)begin//从机被选中
            // nstate = HWRITE?MEM_W:MEM_R;
            if(HWRITE)
                nstate = MEM_W;
            else
                nstate = MEM_R;
        end
        else begin//从机未选中
            nstate = nstate;
        end
    end
    //产生输出组合逻辑
    always@(*)begin
        case(cstate)
            IDLE:begin
                HREADY = 1'b1;
                addr = 8'h00;
            end
            MEM_W:begin
                HREADY = 1'b1;
                wr_en = 1'b1;
                addr = address;
                dout = HWDATA;
                HRDATA = 32'd0;
            end
            MEM_R:begin
                HREADY = 1'b1;
                wr_en = 1'b0;
                addr = address;
                dout = 32'd0;
                HRDATA = din;
            end
        endcase
end
endmodule
  1. 十六位增量突发的testbench.v
`timescale 1ns/1ns
module testbench1;

    reg     HCLK;
    reg     HRESETn;
    
    reg     write;
    reg     read;
    reg     other_slave;
    
    initial begin
                HCLK        = 1;
                HRESETn     = 0;
                write       = 0;
                read        = 0;
                other_slave = 0;
        #100    HRESETn     = 1;
        #10     write       = 1;
        #20     write       = 0;
        #1000   other_slave = 1;
                write       = 1;
        #20     write       = 0;
        #1000   other_slave = 0;
                read        = 1;
        #20     read        = 0;
    end
    
    always #10  HCLK = ~HCLK;
    
    wire            HSEL    = other_slave ? 1'b0 : 1'b1;
    wire    [3:0]   HPROT   = 4'b0001;
    wire    [2:0]   HSIZE   = 3'b010;
    reg             HWRITE;
    wire    [2:0]   HBURST  = 3'b111;/*****************/
    reg     [1:0]   HTRANS;
    reg     [31:0]  HADDR;
    reg     [31:0]  HWDATA;
    wire    [31:0]  HRDATA;
    wire            HREADY;
    wire    [1:0]   HRESP;
    
    wire    [7:0]   addr;
    wire            wr_mem;
    wire    [31:0]  wr_data;
    wire    [31:0]  rd_data;
    
    localparam  IDLE    =   3'b001,
                WRITE   =   3'b010,
                READ    =   3'b100;
    
    localparam  HTRANS_IDLE     = 2'b00,
                HTRANS_BUSY     = 2'b01,
                HTRANS_NONSEQ   = 2'b10,
                HTRANS_SEQ      = 2'b11;
    
    reg [2:0]   cstate;
    reg [2:0]   nstate;
    
    reg [4:0]   cnt;/*****************/
    
    always @ (posedge HCLK or negedge HRESETn) begin
        if(!HRESETn)
            cstate <= IDLE;
        else
            cstate <= nstate;
    end
    
    always @ (*) begin
        nstate = IDLE;
        case(cstate)
            IDLE : begin
                if(write)
                    nstate = WRITE;
                if(read)
                    nstate = READ;
            end
            WRITE : begin
                if(HREADY && cnt == 5'd16)
                    nstate = IDLE;
                else
                    nstate = WRITE;
            end
            READ : begin
                if(HREADY && cnt == 5'd16)
                    nstate = IDLE;
                else
                    nstate = READ;
            end
        endcase
    end
    
    always @ (posedge HCLK or negedge HRESETn) begin
        if(!HRESETn)
            cnt <= 5'h0;
        else begin
            case(cstate)
                IDLE : begin
                    cnt <= 5'h0;
                end
                WRITE : begin
                    if(HREADY && cnt < 5'd16)
                        cnt <= cnt + 1'h1;
                    if(HREADY && cnt == 5'd16)
                        cnt <= 5'h0;
                end
                READ : begin
                    if(HREADY && cnt < 5'd16)
                        cnt <= cnt + 1'h1;
                    if(HREADY && cnt == 5'd16)
                        cnt <= 5'h0;
                end
            endcase
        end
    end
    
    always @ (*) begin
        HTRANS = HTRANS_IDLE; // 2'b00
        case(cstate)
            WRITE : begin
                if(cnt == 5'h0)
                    HTRANS = HTRANS_NONSEQ; //2'b10
                else if(cnt == 5'd16)
                    HTRANS = HTRANS_IDLE;
                else
                    HTRANS = HTRANS_SEQ;    //2'b11
            end
            READ : begin
                if(cnt == 5'h0)
                    HTRANS = HTRANS_NONSEQ;
                else if(cnt == 5'd16)
                    HTRANS = HTRANS_IDLE;
                else
                    HTRANS = HTRANS_SEQ;
            end
        endcase
    end
    
    always @ (*) begin
        HWRITE = 1'b0;
        case(cstate)
            WRITE : begin
                if(cnt == 5'd16)
                    HWRITE = 1'b0;
                else
                    HWRITE = 1'b1;
            end
            READ : begin
                HWRITE = 1'b0;
            end
        endcase
    end
    
    always @ (posedge HCLK or negedge HRESETn) begin
        if(!HRESETn)
            HADDR <= 32'h0;
        else begin
            case(cstate)
                IDLE : begin
                    HADDR <= 32'h0;
                end
                WRITE : begin
                    if(HREADY && cnt < 5'd16)
                        HADDR <= HADDR + 32'h4;
                    if(HREADY && cnt == 5'd16)
                        HADDR <= 32'h0;
                end
                READ : begin
                    if(HREADY && cnt < 5'd16)
                        HADDR <= HADDR + 32'h4;
                    if(HREADY && cnt == 5'd16)
                        HADDR <= 32'h0;
                end
            endcase
        end
    end
    always @ (posedge HCLK or negedge HRESETn) begin
        if(!HRESETn)
            HWDATA <= 32'h0;
        else begin
            case(cstate)
                IDLE : begin
                    HWDATA <= 32'h0;
                end
                WRITE : begin
                    if(HREADY && !HSEL && cnt < 5'd16)
                        HWDATA <= HWDATA + 32'h1;
                    if(HREADY && HSEL && cnt < 5'd16)
                        HWDATA <= HWDATA + 32'h2;
                    if(HREADY && cnt == 5'd16)
                        HWDATA <= 32'h0;
                end
            endcase
        end
    end
    AHB_Slave ahb_slave (
        .HCLK       (HCLK),
        .HRESETn    (HRESETn),
        .HSEL       (HSEL),
        .HPROT      (HPROT),
        .HSIZE      (HSIZE),
        .HWRITE     (HWRITE),
        .HBURST     (HBURST),
        .HTRANS     (HTRANS),
        .HADDR      (HADDR),
        .HWDATA     (HWDATA),
        .HRDATA     (HRDATA),
        .HREADY     (HREADY),
        .HRESP      (HRESP),
        
        .addr       (addr),
        .wr_en      (wr_mem),
        .dout       (wr_data),
        .din        (rd_data)
    );
    RAM RAM (
        .clka   (HCLK),
        .wea    (wr_mem), //高写低读
        .addra  (addr),
        .dina   (wr_data),
        .douta  (rd_data)
);
endmodule

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值