基于Verilog HDL的axi-lite主机模块

news2024/11/16 5:48:43

基于Verilog HDL的axi-lite主机模块

  前文对axi_lite接口协议的各个信号做了详细讲解,本文通过Verilog Hdl编写一个通用接口转axi_lite接口协议的模块。

1、生成xilinx官方提供axi源码

  Xilinx其实给用户提供了axi相关模块,获取方式如下,首先打开vivado软件,然后点击Manage IP,之后选择New IP Location,如下图所示。

在这里插入图片描述

图1 创建IP

  直接点击下一步。

在这里插入图片描述

图2 向导界面

  之后需要选择器件型号,然后确定工程路径,之后点击Finish。

在这里插入图片描述

图3 Manage IP Settings

  进入工程后,在Tools选项卡下点击Create and Package New IP,如下图所示。

在这里插入图片描述

图4 生成IP

  进入向导界面后直接点击下一步。

在这里插入图片描述

图5 Create and Package New IP

  然后选择创建AXI外围设备,如下图所示。

在这里插入图片描述

图6 创建AXI 外围设备

  根据需求修改一下IP名,然后点击下一步,如下图所示。

在这里插入图片描述

图7 修改IP名

  然后选择生成IP的协议类型,此处选择lite协议,然后选择主机,数据位宽只能固定为32位。之后点击下一步,如下图所示。

在这里插入图片描述

图8 生成axi_lite主机模块

  通过修改上图设置,可以生成axi_lite、axi_full、axi_stream协议的主机和从机模块,后文会使用其余模块,就不再详细讲解。

  最后需要选择Edit IP,才会生成IP的源文件,如下图所示。

在这里插入图片描述

图9 生成IP

  之后会生成一个工程,生成的工程会包含axi_lite_master_v1_0_M00_AXI的一个模块,这个模块就是生成的axi_lite_master模块。

在这里插入图片描述

图10 生成文件

  本文只是告诉读者vivado能够生成一个axi协议的相关模块,但是该模块的代码不够简洁,其实使用起来没那么方便。

  后文不会对生成的模块进行解读和使用,后文会根据前文所讲协议内容自己编写一个axi_lite_master模块,而该模块的axi_lite端口信号与生成的模块保持一致。

2、axi_lite_master模块设计

  用户端口肯定会设计的比较简单,只包括读写使能、地址、数据信号,由于axi_lite接口需要和从机进行应答,那么用户端口写入的数据和地址就不能立即写入,需要等待。

  解决这个问题的方法有两种思路,第一种是在写和读部分都给用户输出一个模块忙闲指示信号,只有当模块对应功能空闲时,才能进行下次操作。

  第二种就是在写数据、写地址、读地址端口处加一个FIFO,用来存储用户的数据和指令,当FIFO不为空,就读出FIFO中的数据通过axi_lite发送给从机。这种方式的优点是用户不需要等待模块空闲,直接将需要发送的指令、数据输入即可,简化逻辑,但是FIFO会消耗更多资源。

  本文采用第二种设计方式,整个模块通过检测三个FIFO是否为空,向从机发起读写时序。

  端口信号如下所示,首先是axi_lite的时钟、复位和五个通道的信号,然后是用户的端口信号,用户的写使能信号与写数据、写地址信号对齐,读使能与读地址信号对齐。用户的地址信号位宽默认为8位,而axi_lite的地址和数据信号一般均为32位,可以根据实际情况进行拼接。

module axi_lite_master #(
    parameter			UADDR_W		=		    8		        ,//用户地址位宽;
    parameter			UDATA_W	    =		    32	            ,//用户数据位宽;
    parameter           ADATA_W     =           32               //AXI_LITE的地址和数据位宽;
)(
    input									    M_AXI_ACLK      ,//AXI接口时钟信号;
    input									    M_AXI_ARESETN   ,//AXI接口复位信号,低电平有效;
    //AXI写地址通道信号;
    output  reg         [ADATA_W - 1 : 0]       M_AXI_AWADDR    ,//AXI地址信号;
    output              [2 : 0]                 M_AXI_AWPROT    ,//AXI地址端口信号;
    output  reg                                 M_AXI_AWVALID   ,//AIX地址数据有效指示信号,高电平有效;
    input                                       M_AXI_AWREADY   ,//AXI地址数据应答信号。
    //AXI写数据通道信号;
    output  reg         [ADATA_W - 1 : 0]       M_AXI_WDATA     ,//AXI写数据信号;
    output              [ADATA_W / 8 - 1 : 0]   M_AXI_WSTRB     ,//AXI写数据掩码信号,低电平有效;
    output  reg                                 M_AXI_WVALID    ,//AXI写入数据有效指示信号,高电平有效;
    input                                       M_AXI_WREADY    ,//AXI写入数据应答信号,高电平有效;
    //AXI写应答通道信号;
    input               [1 : 0]                 M_AXI_BRESP     ,//AXI写应答信号,为0时表示写入正确;
    input                                       M_AXI_BVALID    ,//AXI写应答有效指示信号,高电平有效;
    output  reg                                 M_AXI_BREADY    ,//AXI写应答响应信号,高电平有效;
    //AXI读地址通道信号;
    output  reg         [ADATA_W - 1 : 0]       M_AXI_ARADDR    ,//AXI读地址信号;
    output              [2 : 0]                 M_AXI_ARPROT    ,//AXI读地址端口信号;
    output  reg                                 M_AXI_ARVALID   ,//AXI读地址有效指示信号,高电平有效;
    input                                       M_AXI_ARREADY   ,//AXI读地址应答信号,高电平有效;
    //AXI读数据通道信号;
    input               [ADATA_W - 1 : 0]       M_AXI_RDATA     ,//AXI读数据信号;
    input               [1 : 0]                 M_AXI_RRESP     ,//AXI读数据状态应答信号,为0表示读数据正确;
    input                                       M_AXI_RVALID    ,//读数据有效指示信号,高电平有效;
    output  reg                                 M_AXI_RREADY    ,//主机接收从机读出数据的应答信号,高电平有效;
    //用户写数据信号;
    input                                       wr_en           ,//写使能信号,高电平有效;
    input               [UADDR_W - 1 : 0]       waddr           ,//写地址信号,与写使能对齐;
    input               [UDATA_W - 1 : 0]       wdata           ,//写数据信号,与写使能对齐;
    //用户读数据信号;
    input                                       rd_en           ,//读使能信号,高电平有效;
    input               [UADDR_W - 1 : 0]       raddr           ,//读地址信号,与读使能对齐;
    output  reg         [UDATA_W - 1 : 0]       rdata           ,//读数据信号;
    output  reg                                 rdata_vld       ,//读数据有效指示信号,高电平有效;
    output  reg                                 error            //错误指示信号,当写入错误或读出错误时为1,正常为0;
);

  axi_lite的读、写地址端口类型信号一般为0,且写入数据不会加掩码,因此这几个信号直接连接对应电平输出即可。

    assign M_AXI_AWPROT = 3'd0 ;//写地址端口信号始终为0;
    assign M_AXI_ARPROT = 3'd0 ;//读地址端口信号始终为0;
    assign M_AXI_WSTRB = {{ADATA_W/8}{1'b1}};//写数据掩码信号,本模块写入数据全部有效,因此全为高电平;
    assign wfifo_empty = wa_fifo_empty & wa_fifo_empty;//由于写地址和写数据可以同时进行,所以将两个写FIFO空指示信号合并;

  由于用户端口的写地址信号、写数据信号都与写使能对齐,且前文分析axi_lite的写数据和写地址信号可以对齐(隐含意思:两个FIFO可以一起读出数据)。因此可以把两个FIFO的空标志信号整理为一个空标志信号。

  下面是写地址FIFO、读地址FIFO、写数据FIFO的例化,地址FIFO的数据位宽为8,数据FIFO的数据位宽为32,FIFO深度均为32。FIFO输出数据会滞后读使能一个时钟。

    //用于存储写地址的FIFO;
    addr_fifo_8x32 u_waddr_fifo (
        .clk    ( M_AXI_ACLK        ),//input wire clk;
        .din    ( waddr             ),//input wire [7 : 0] din;
        .wr_en  ( wr_en             ),//input wire wr_en;
        .rd_en  ( wfifo_rd_en       ),//input wire rd_en;
        .dout   ( wa_fifo_dout      ),//output wire [7 : 0] dout;
        .full   ( wa_fifo_full      ),//output wire full;
        .empty  ( wa_fifo_empty     ) //output wire empty;
    );

    //用于存储读地址的FIFO;
    addr_fifo_8x32 u_raddr_fifo (
        .clk    ( M_AXI_ACLK        ),//input wire clk;
        .din    ( raddr             ),//input wire [7 : 0] din;
        .wr_en  ( rd_en             ),//input wire wr_en;
        .rd_en  ( ra_fifo_rd_en     ),//input wire rd_en;
        .dout   ( ra_fifo_dout      ),//output wire [7 : 0] dout;
        .full   ( ra_fifo_full      ),//output wire full;
        .empty  ( ra_fifo_empty     ) //output wire empty;
    );

    //用于存储写数据的FIFO;
    data_fifo_32x32 u_wdata_fifo (
        .clk    ( M_AXI_ACLK        ),//input wire clk;
        .din    ( wdata             ),//input wire [31 : 0] din;
        .wr_en  ( wr_en             ),//input wire wr_en;
        .rd_en  ( wfifo_rd_en       ),//input wire rd_en;
        .dout   ( wd_fifo_dout      ),//output wire [31 : 0] dout;
        .full   ( wd_fifo_full      ),//output wire full;
        .empty  ( wd_fifo_empty     ) //output wire empty;
    );

  写地址FIFO和写数据FIFO的读使能采用一个信号,即同时输出信号。

  然后下面是一个写标志信号wr_flag,该信号为高电平表示正在进行axi_lite写操作。当写地址、写数据FIFO均有数据且不处于写状态时拉高,当写应答通道响应时拉低。

    //写标志信号,初始值为0,当写入数据时拉高,一次写入完成后拉低;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            wr_flag <= 1'b0;
        end//当写应答通道的有效信号和应答信号均为高时,表示一次写数据完成;
        else if(M_AXI_BVALID & M_AXI_BREADY)begin
            wr_flag <= 1'b0;
        end//当不处于写数据状态且写FIFO中有数据时拉高;
        else if((~wa_fifo_empty) && (~wr_flag))begin
            wr_flag <= 1'b1;
        end
    end

  在wr_flag信号拉高时两个FIFO的读使能信号有效,将读使能信号打一拍,作为两个FIFO输出数据的有效指示信号。

    //写地址FIFO读使能信号;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            wfifo_rd_en <= 1'b0;
        end
        else begin//当FIFO没有空且写标志信号开始时拉高;
            wfifo_rd_en <= ((~wfifo_empty) && (~wr_flag));
        end
    end
    
    //将写地址FIFO读使能信号延迟一个时钟作为读出数据有效指示信号;
    always@(posedge M_AXI_ACLK)begin
        wfifo_rdata_vld <= wfifo_rd_en;
    end

  当写地址FIFO输出数据有效时,把写FIFO输出数据赋值给axi_lite写地址通道的写地址信号,由于位宽可能不同,需要进行拼接。

  同时把写数据FIFO输出数据赋值给axi_lite写数据通道的写数据信号,用户读写数据位宽可能与axi_lite写数据位宽不同,也可能需要拼接。

    //生成AXI写地址信号,初始值为0,当写地址FIFO读数据有效时,输出FIFO读出的数据,其余时间保持不变;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            M_AXI_AWADDR <= {{ADATA_W}{1'b0}};
            M_AXI_WDATA <= {{ADATA_W}{1'b0}};
        end
        else if(wfifo_rdata_vld)begin//将写地址FIFO输出数据赋值给AXI地址信号;
            M_AXI_AWADDR <= {{{ADATA_W - UADDR_W}{1'b0}},wa_fifo_dout[UADDR_W - 1 : 0]};
            M_AXI_WDATA <= {{{ADATA_W - UDATA_W}{1'b0}},wd_fifo_dout[UDATA_W - 1 : 0]};
        end
    end

  之后需要生成axi_lite写地址、写数据通道的有效指示信号,当FIFO读出数据后拉高,各自应答信号为高电平时拉低。

    //生成AXI写地址有效指示信号,与写地址信号对齐;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            M_AXI_AWVALID <= 1'b0;
        end
        else if(M_AXI_AWREADY)begin//当写地址信号被应答后拉低;
            M_AXI_AWVALID <= 1'b0;
        end
        else if(wfifo_rdata_vld)begin//当写地址FIFO输出有效数据后拉高;
            M_AXI_AWVALID <= 1'b1;
        end
    end

    //生成AXI写数据有效指示信号,与写地址信号对齐;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            M_AXI_WVALID <= 1'b0;
        end
        else if(M_AXI_WREADY)begin//当写数据信号被应答后拉低;
            M_AXI_WVALID <= 1'b0;
        end
        else if(wfifo_rdata_vld)begin//当FIFO输出有效数据后拉高;
            M_AXI_WVALID <= 1'b1;
        end
    end

  最后就是写应答通道的主机应答信号,该信号可以在写入数据完成时拉高,也可以在写入数据有效时就拉高,没有特别的要求。但必须在从机应答有效后拉低。

    //生成应答通道的应答数据,当写入数据后拉高,等待从机有效指示信号拉高后拉低。
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            M_AXI_BREADY <= 1'b0;
        end
        else if(M_AXI_BVALID)begin//从机应答信号拉高;
            M_AXI_BREADY <= 1'b0;
        end
        else if(M_AXI_WREADY & M_AXI_WVALID)begin//写入数据完成;
            M_AXI_BREADY <= 1'b1;
        end
    end

  下面是与读通道相关的信号,读地址通道与写地址通道类似。首先依旧需要一个读状态的标志信号rd_flag,但是为了避免同时读写同一地址数据,此处需要在写FIFO为空的时候,才会拉高读状态。

    //读标志信号;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            rd_flag <= 1'b0;
        end
        else if(M_AXI_RREADY & M_AXI_RREADY)begin//当读数据通道的应答信号和输出数据有效信号同时为高时,表示读取成功;
            rd_flag <= 1'b0;
        end
        else if((~ra_fifo_empty) && (~rd_flag) && wfifo_empty && (~wr_flag))begin//当读地址FIFO不为空且不处于读数据状态时拉高;
            rd_flag <= 1'b1;
        end
    end
    
    //读地址FIFO读使能信号,当需要同时执行读写操作时,优先执行写操作,防止同时读写同一地址引发错误。
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            ra_fifo_rd_en <= 1'b0;
        end
        else begin//当FIFO没有空且读标志信号开始时拉高;
            ra_fifo_rd_en <= ((~ra_fifo_empty) && (~rd_flag) && wfifo_empty && (~wr_flag));
        end
    end
    
    //将读地址FIFO读使能信号延迟一个时钟作为读出数据有效指示信号;
    always@(posedge M_AXI_ACLK)begin
        ra_fifo_rdata_vld <= ra_fifo_rd_en;
    end

  因此注意,用户写使能不能一直拉高,会造成无法进行读操作。

  读地址FIFO输出数据使能、axi_lite读地址、读地址有效指示信号与前文写地址通道信号类似,对应代码如下,不再赘述。

    //生成AXI读地址信号,初始值为0,当读地址FIFO读数据有效时,输出FIFO读出的数据,其余时间保持不变;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            M_AXI_ARADDR <= {{ADATA_W}{1'b0}};
        end
        else if(ra_fifo_rdata_vld)begin//将读地址FIFO输出数据赋值给AXI地址信号;
            M_AXI_ARADDR <= {{{ADATA_W - UADDR_W}{1'b0}},ra_fifo_dout[UADDR_W - 1 : 0]};
        end
    end

    //生成AXI读地址有效指示信号,与读地址信号对齐;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            M_AXI_ARVALID <= 1'b0;
        end
        else if(M_AXI_ARREADY)begin//当读地址信号被应答后拉低;
            M_AXI_ARVALID <= 1'b0;
        end
        else if(ra_fifo_rdata_vld)begin//当读地址FIFO输出有效数据后拉高;
            M_AXI_ARVALID <= 1'b1;
        end
    end

    //生成读数据通道的应答信号;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            M_AXI_RREADY <= 1'b0;
        end
        else if(M_AXI_RVALID)begin//当读数据有效时拉低;
            M_AXI_RREADY <= 1'b0;
        end
        else if(M_AXI_ARVALID & M_AXI_ARREADY)begin//当从机应答读地址信号时拉高;
            M_AXI_RREADY <= 1'b1;
        end
    end

  然后将axi_lite读出的数据和数据有效指示信号输出,对应代码如下所示。

    //生成用户读数据及读数据有效信号;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            rdata <= {{UDATA_W}{1'b0}};
            rdata_vld <= 1'b0;
        end
        else begin//将AXI读出数据输出,并且把数据有效信号拉高一个时钟周期;
            rdata <= M_AXI_RVALID ? M_AXI_RDATA[UDATA_W-1 : 0] : rdata;
            rdata_vld <= M_AXI_RVALID;
        end
    end

  最后会给用户生成一个错误指示信号,根据读数据和写应答通道从机反馈的状态信号是否为0,判断写数据和读数据是否正确,对应代码如下。

    //生成错误指示信号,初始值为0;
    always@(posedge M_AXI_ACLK)begin
        if(M_AXI_ARESETN==1'b0)begin//初始值为0;
            error <= 1'b0;
        end//每次读写使能信号有效时清零;
        else if(wfifo_rd_en | ra_fifo_rd_en)begin
            error <= 1'b0;
        end//当写入数据失败或者读出数据错误时拉高;
        else if((M_AXI_ARVALID & M_AXI_ARREADY & (M_AXI_RRESP!=2'd0)) | (M_AXI_BREADY & M_AXI_BREADY & (M_AXI_BRESP != 2'd0)))begin
            error <= 1'b1;
        end
    end

  整个模块的设计就完成了,将用户端口时序转换为axi_lite接口协议。

3、axi_lite_master仿真

  仿真有两种方式,第一种就是使用一个axi_lite从机模块与该模块一起仿真。第二种是使用一个axi_lite的IP作为从机进行仿真,使用IP需要阅读该IP手册,了解其内部寄存器,相对比较麻烦。

  因此可以使用本文前半部分的方式,生成一个axi_lite的从机模块,作为仿真模型,生成从机的步骤省略,参考图9生成主机模块步骤即可。

  编写的TestBench如下所示:

`timescale 1 ns/1 ns
module test();
    parameter	CYCLE		=   10              ;//系统时钟周期,单位ns,默认10ns;
    parameter	RST_TIME	=   10              ;//系统复位持续时间,默认10个系统时钟周期;
    parameter   UADDR_W     =   8               ;//用户地址位宽;
    parameter   UDATA_W     =   32              ;//用户数据位宽;
    parameter   ADATA_W     =   32              ;//AXI_LITE的地址和数据位宽;
    //时钟复位信号;
    reg			                clk             ;//系统时钟,默认100MHz;
    reg			                rst_n           ;//系统复位,默认低电平有效;
    //用户写数据信号;
    reg                         wr_en           ;
    reg   [UADDR_W - 1 : 0]     waddr           ;
    reg   [UDATA_W - 1 : 0]     wdata           ;
    //用户读数据信号;
    reg                         rd_en           ;
    reg   [UADDR_W - 1 : 0]     raddr           ;
    wire  [UDATA_W - 1 : 0]     rdata           ;
    wire                        rdata_vld       ;
    wire                        error           ;
    //AXI写地址通道信号;
    wire  [ADATA_W - 1 : 0]     M_AXI_AWADDR    ;
    wire  [2 : 0]               M_AXI_AWPROT    ;
    wire                        M_AXI_AWVALID   ;
    wire                        M_AXI_AWREADY   ;
    //AXI写数据通道信号;
    wire  [ADATA_W - 1 : 0]     M_AXI_WDATA     ;
    wire  [ADATA_W / 8 - 1 : 0] M_AXI_WSTRB     ;
    wire                        M_AXI_WVALID    ;
    wire                        M_AXI_WREADY    ;
    //AXI写应答通道信号;
    wire  [1 : 0]               M_AXI_BRESP     ;
    wire                        M_AXI_BVALID    ;
    wire                        M_AXI_BREADY    ;
    //AXI读地址通道信号;
    wire  [ADATA_W - 1 : 0]     M_AXI_ARADDR    ;
    wire  [2 : 0]               M_AXI_ARPROT    ;
    wire                        M_AXI_ARVALID   ;
    wire                        M_AXI_ARREADY   ;
    //AXI读数据通道信号;
    wire  [ADATA_W - 1 : 0]     M_AXI_RDATA     ;
    wire  [1 : 0]               M_AXI_RRESP     ;
    wire                        M_AXI_RVALID    ;
    wire                        M_AXI_RREADY    ;

    //例化主机模块
    axi_lite_master #(
        .UADDR_W        ( UADDR_W       ),
        .UDATA_W        ( UDATA_W       ),
        .ADATA_W        ( ADATA_W       )
    )
    u_axi_lite_master (
        .M_AXI_ACLK     ( clk           ),
        .M_AXI_ARESETN  ( rst_n         ),
        //AXI写地址通道信号;
        .M_AXI_AWREADY  ( M_AXI_AWREADY ),
        .M_AXI_AWADDR   ( M_AXI_AWADDR  ),
        .M_AXI_AWPROT   ( M_AXI_AWPROT  ),
        .M_AXI_AWVALID  ( M_AXI_AWVALID ),
        //AXI写数据通道信号;
        .M_AXI_WDATA    ( M_AXI_WDATA   ),
        .M_AXI_WSTRB    ( M_AXI_WSTRB   ),
        .M_AXI_WVALID   ( M_AXI_WVALID  ),
        .M_AXI_WREADY   ( M_AXI_WREADY  ),
        //AXI写应答通道信号;
        .M_AXI_BRESP    ( M_AXI_BRESP   ),
        .M_AXI_BVALID   ( M_AXI_BVALID  ),
        .M_AXI_BREADY   ( M_AXI_BREADY  ),
        //AXI读地址通道信号;
        .M_AXI_ARREADY  ( M_AXI_ARREADY ),
        .M_AXI_ARADDR   ( M_AXI_ARADDR  ),
        .M_AXI_ARPROT   ( M_AXI_ARPROT  ),
        .M_AXI_ARVALID  ( M_AXI_ARVALID ),
        //AXI读数据通道信号;
        .M_AXI_RDATA    ( M_AXI_RDATA   ),
        .M_AXI_RRESP    ( M_AXI_RRESP   ),
        .M_AXI_RVALID   ( M_AXI_RVALID  ),
        .M_AXI_RREADY   ( M_AXI_RREADY  ),
        //用户读写信号;
        .wr_en          ( wr_en         ),
        .waddr          ( waddr         ),
        .wdata          ( wdata         ),
        .rd_en          ( rd_en         ),
        .raddr          ( raddr         ),
        .rdata          ( rdata         ),
        .rdata_vld      ( rdata_vld     ),
        .error          ( error         )
    );

    //例化AXI从机模块
    axi_lite_slave #(
        .C_S_AXI_DATA_WIDTH ( ADATA_W   ),
        .C_S_AXI_ADDR_WIDTH ( ADATA_W   )
    )
    u_axi_lite_slave (
        .S_AXI_ACLK     ( clk           ),
        .S_AXI_ARESETN  ( rst_n         ),
        //AXI写地址通道信号;
        .S_AXI_AWREADY  ( M_AXI_AWREADY ),
        .S_AXI_AWADDR   ( M_AXI_AWADDR  ),
        .S_AXI_AWPROT   ( M_AXI_AWPROT  ),
        .S_AXI_AWVALID  ( M_AXI_AWVALID ),
        //AXI写数据通道信号;
        .S_AXI_WDATA    ( M_AXI_WDATA   ),
        .S_AXI_WSTRB    ( M_AXI_WSTRB   ),
        .S_AXI_WVALID   ( M_AXI_WVALID  ),
        .S_AXI_WREADY   ( M_AXI_WREADY  ),
        //AXI写应答通道信号;
        .S_AXI_BRESP    ( M_AXI_BRESP   ),
        .S_AXI_BVALID   ( M_AXI_BVALID  ),
        .S_AXI_BREADY   ( M_AXI_BREADY  ),
        //AXI读地址通道信号;
        .S_AXI_ARREADY  ( M_AXI_ARREADY ),
        .S_AXI_ARADDR   ( M_AXI_ARADDR  ),
        .S_AXI_ARPROT   ( M_AXI_ARPROT  ),
        .S_AXI_ARVALID  ( M_AXI_ARVALID ),
        //AXI读数据通道信号;
        .S_AXI_RDATA    ( M_AXI_RDATA   ),
        .S_AXI_RRESP    ( M_AXI_RRESP   ),
        .S_AXI_RVALID   ( M_AXI_RVALID  ),
        .S_AXI_RREADY   ( M_AXI_RREADY  )
    );

    //生成周期为CYCLE数值的系统时钟;
    initial begin
        clk = 0;
        forever #(CYCLE/2) clk = ~clk;
    end

    //生成复位信号;
    initial begin
        rst_n = 1;
        #2;
        rst_n = 0;//开始时复位10个时钟;
        #(RST_TIME*CYCLE);
        rst_n = 1;
    end

    initial begin
        wr_en <= 1'b0;
        rd_en <= 1'b0;
        waddr <= 0;
        raddr <= 0;
        wdata <= 0;
        wait(rst_n);//等待复位信号为高电平后;
        repeat(10)@(posedge clk);
        wr_data(13,133);
        repeat(10)@(posedge clk);
        rd_data(13);
        repeat(10)@(posedge clk);
        fork//并行运行写数据和读数据两个函数;
            wr_data(20,311);
            rd_data(20);
        join
        repeat(15)@(posedge clk);
        rd_data(20);
        repeat(20)@(posedge clk);
        $stop;
    end

    //用户写数据任务;
    task wr_data(
        input	[UADDR_W - 1 : 0]		addr	,
        input	[UDATA_W - 1 : 0]       data
    );
        begin
            wr_en <= 1'b0;
            @(posedge clk);
            wr_en <= 1'b1;
            waddr <= addr;
            wdata <= data;
            @(posedge clk);
            wr_en <= 1'b0;
            @(posedge clk);
        end
    endtask 

    //用户读数据任务;
    task rd_data(
        input	[UADDR_W - 1 : 0]		addr
    );
        begin
            rd_en <= 1'b0;
            @(posedge clk);
            rd_en <= 1'b1;
            raddr <= addr;
            @(posedge clk);
            rd_en <= 1'b0;
            @(posedge clk);
        end
    endtask

endmodule

  运行仿真结果如下,当用户向地址13中写入133时,橙色信号是主机写地址通道输出的三个信号,天蓝色是主机写数据使出的三个信号,粉红色是从机输出该主机的写应答通道信号。

  写地址有效信号和写数据有效指示信号同时拉高,在各自应答有效后拉低。而黄色信号是主机的应答从机的信号,此处在写入数据后拉高。

在这里插入图片描述

图11 写时序仿真

  写时序仿真正确,下图是用户端口从地址13中读出数据。

  白色信号表示读地址通道主机输出的三个信号,橙黄色表示读出从机的数据信号,而紫红色表示输出给用户的读数据。

  最终从地址13读出的数据为133,与前文写入数据一致,证明读写时序均没有问题。

在这里插入图片描述

图12 读时序仿真

  下图中用户端口同时读写从机的同一地址数据,根据模块的仲裁,首先会向地址为20的寄存器中写入数据311,然后在从地址20中读出数据,最后读出数据也为311。

在这里插入图片描述

图13 用户端口同时读写同一地址

  上图中axi_lite的读写时序中间有段间隔,这是由于中间判断信号造成的。

  关于该模块的仿真到此结束,通过前文的讲解,实现axi_lite协议其实难度不大,相对SDRAM这些接口,axi_lite算是很简单的协议。

  由于FPGA内部一般不会使用从机模块,因此先跳过从机的实现,下文将讲解axi_full接口协议,会发现axi_full其实也比较简单,很多信号和模式FPGA并不会使用。

  需要本文工程的可以在公众号后台回复“axi_lite_master工程”(不包括引号)。


  如果对文章内容理解有疑惑或者对代码不理解,可以在评论区或者后台留言,看到后均会回复!

  如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!您的支持是我更新的最大动力!将持续更新工程!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1538721.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI视频风格转换动漫风:Stable Diffusion+TemporalKit

话不多说&#xff0c;直接开干。 基本方法 首先通过 Temporal-Kit 这个插件提取视频中的关键帧图片&#xff0c;然后使用 Stable Diffusion WebUI 重绘关键帧图片&#xff0c;然后再使用 Temporal-Kit 处理转换后的关键帧图片&#xff0c;它会自动补充关键帧之间的图片&#…

通讯录的动态实现

文章目录 通讯录的动态实现模块化编程通讯录的框架构建功能的具体实现初始化通讯录添加联系人删除联系人查找联系人修改联系人打印通讯录排序通讯录检查容量并扩容加载通讯录保留通讯录销毁通讯录 完整代码总结 通讯录的动态实现 模块化编程 分文件 不同模块放在不同的文件下 …

Word为图表设置图注并在图表清单中自动生成

1如果需要自动插入题注&#xff0c;请不要自己为文件增加新的标题样式或删除自带的标题1样式 2章节大标题最好是标题1&#xff0c;2,3而不要设置标题一、二、三&#xff0c;否则图例在自动生成时会显示 图一 -1&#xff0c;调整起来会非常不方便 若实在要使用大写中文标题&…

Rust之构建命令行程序(五):环境变量

开发环境 Windows 11Rust 1.77.0 VS Code 1.87.2 项目工程 这次创建了新的工程minigrep. 使用环境变量 我们将通过添加一个额外的功能来改进minigrep:一个不区分大小写的搜索选项&#xff0c;用户可以通过环境变量打开该选项。我们可以将此功能设置为命令行选项&#xff0c;…

【Mysql】面试题汇总

1. 存储引擎 1-1. MySQL 支持哪些存储引擎&#xff1f;默认使用哪个&#xff1f; 答&#xff1a; MySQL 支持的存储引擎包括 InnoDB、MyISAM、Memory 等。 Mysql 5.5 之前默认的是MyISAM&#xff0c;Mysql 5.5 之后默认的是InnoDB。 可以通过 show engines 查看 Mysql 支持…

【Vue】Vue集成Element-UI框架

&#x1f64b;‍ 一日之际在于晨 ⭐本期内容&#xff1a;Vue集成Element-UI框架 &#x1f3c6;系列专栏&#xff1a;从0开始的Vue之旅 文章目录 Element-UI简介安装Element-UInpm安装CDN安装 引入Element-UI测试是否引入成功总结 Element-UI简介 Element-UI官网&#xff1a;点…

yolov9目标检测可视化图形界面GUI源码

该系统是由微智启软件工作室基于yolov9pyside6开发的目标检测可视化界面系统 运行环境&#xff1a; window python3.8 安装依赖后&#xff0c;运行源码目录下的wzq.py启动 程序提供了ui源文件&#xff0c;可以拖动到Qt编辑器修改样式&#xff0c;然后通过pyside6把ui转成python…

C语言学习--八种排序算法

目录 排序的概念 1.直接插入排序 基本思想 代码实现 算法分析 2.希尔排序 基本思想 代码实现 算法分析 3.冒泡排序 基本思想 代码实现 算法分析 4.快速排序 基本思想 代码实现 算法分析 5.简单选择排序 基本思想 代码实现 算法分析 6.堆排序 基本思想 代…

数据中台:如何构建企业核心竞争力_光点科技

在当今信息化快速发展的商业环境下&#xff0c;“数据中台”已经成为构建企业核心竞争力的关键步骤。数据中台不仅是数据集成与管理的平台&#xff0c;更是企业智能化转型的加速器。本文将深入探讨数据中台的定义、特点、构建方法及其在企业中的作用。 数据中台的定义 数据中台…

小程序—音频

比较常见的需求&#xff1a;当阈值超出或识别不通过时&#xff0c;模拟蜂鸣器响&#xff08;准备相关音频文件&#xff09; 小程序支持播放和录制音频。小程序播放音频的方式有两种&#xff1a;内部音频和背景音频。 内部音频支持用户在使用小程序过程中播放音效;背景音频支持…

算法---前缀和练习-2(和为k的子数组)

和为k的子数组 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;点这里 2. 讲解算法原理 创建一个无序映射&#xff08;哈希表&#xff09; hash&#xff0c;用于统计前缀和的出现次数。初始时&#xff0c;将前缀和为 0 的次数设为 1&#xff0c;表示…

基于Spring Boot技术的幼儿园管理系统

摘 要 随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建幼儿园管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种活动信息、课程信息、菜谱信息、通知公…

现在阿里云云服务器租用多少钱?一张表,报价单

2024年阿里云服务器优惠价格表&#xff0c;一张表整理阿里云服务器最新报价&#xff0c;阿里云服务器网整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单&#xff0c;大家也可以直接移步到阿里云CLUB中心查看 aliyun.club 当前最新的云服务器优惠券…

python 空间距离计算

目录 python 空间距离计算 已知两点&#xff0c;画三角形 批量矩阵计算 python 空间距离计算 要在空间中找到一个点&#xff0c;使其位于点 b 和 c 之间的连线上&#xff0c;并且与点 b 的距离等于点 a 到点 b 的距离的2倍。 import numpy as npif __name__ __main__:a …

深度学习基础之《TensorFlow框架(10)—案例:实现线性回归(2)》

增加其他功能 一、增加变量显示 1、目的&#xff1a;在TensorBoard当中观察模型的参数、损失值等变量值的变化 2、收集变量 不同的变量要用不同的方式收集 &#xff08;1&#xff09;tf.summary.scalar(name, tensor) 收集对于损失函数和准确率等单值变量&#xff0c;name为…

什么是行业垂直类媒体?有哪些?怎么邀约

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体胡老师。 行业垂直类媒体是聚焦于特定行业或领域的媒体平台。 行业垂直类媒体不同于主流媒体&#xff0c;它们专注于提供与某个特定领域相关的深入内容和服务&#xff0c;例如商业新闻、旅游、数字…

WMI接口设计实现

WMI是Windows操作系统管理数据和操作的基础设施&#xff0c;系统管理员可以使用VB Script、PowerShell及Windows API&#xff08;C、C#等&#xff09;管理本地或远程计算机。 使用WMI框架应用程序可以直接访问EC RAM、 I/O端口、Memory地址、寄存器、Setup NV设定值&#xff0c…

vue 安装脚手架报错 certificate has expired

vue 安装脚手架的时候报错&#xff0c;报错信息如下&#xff1a; 错误信息&#xff1a;npm ERR! request to https://registry.npm.taobao.org/vue%2fcli failed, reason: certificate has expired 翻译&#xff1a;npm ERR&#xff01;请求到https://registry.npm.taobao.org…

【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美)

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C语言》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、AVL树的概念二、AVL树的模拟实现2.1 结点2.2 成员变量2.3 插入2.4 旋转2.4.1 左单旋2.4.2 右单旋2.4.3 …

python文学名著分享系统的设计与实现flask-django-nodejs-php

在此基础上&#xff0c;结合现有文学名著分享体系的特点&#xff0c;运用新技术&#xff0c;构建了以python为基础的文学名著分享信息化管理体系。首先&#xff0c;以需求为依据&#xff0c;根据需求分析结果进行了系统的设计&#xff0c;并将其划分为管理员和用户二种角色和多…