AXI4-lite总线协议总共包括21条信号线。每个通道都有其自己的双向握手机制信号线xxVALID和xxREADY,关于双向握手机制,可以参见上一篇博客AXI FULL协议学习与仿真。下面是信号线的详细描述。
全局信号
ACLK:全局时钟信号,在上升沿时对信号采样。所有的输入信号都通过上升沿采集,所有的输出信号都在上升沿时变化。
ARESETn:全局复位信号,低电平有效。在复位期间,所有的xxVALID信号必须复位为低电平。其他的信号可以是任意值。
写地址通道信号
主机(master)控制的信号
AWVALID
AWADDR 地址信号线,传输地址信息。
AWPROT 访问权限信号线,xilinx建议赋值为3’b000.xilinx IP 一般忽略此信号。
从机(slave)控制的信号
AWREADY
写数据通道信号
主机(master)控制的信号
WVALID
WDATA 数据信号线,传输数据信息。
WSTRB 数据总线有效字节控制。比如32位的总线,WSTRB等于4’b0010,那么代表WDATA[15:8]中的数据有效。其他无效。如果要求WDATA[31:0]32位全有效,那么WSTRB就应该等于4’b1111.
从机(slave)控制的信号
WREADY
写应答通道信号
主机(master)控制的信号
BREADY
从机(slave)控制的信号
BVALID
BRESP 应答类型。AXI4-lite不支持EXOKAY类型的响应。
OKEY 0 正常访问成功
SLVERR 2 从机错误
DECERR 3 解码错误,比如没有从机的地址。
读地址通道信号
主机(master)控制的信号
ARVALID
ARADDR 地址信号线,传输地址信息。
ARPROT 访问权限信号线,xilinx建议赋值为3’b000.xilinx IP 一般忽略此信号。
从机(slave)控制的信号
ARREADY
读数据通道信号
主机(master)控制的信号
RREADY
从机(slave)控制的信号
RVALID
RDATA 数据信号线,传输数据信息。
RRESP 同写应答信号BRESP。
代码实现
从机:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/03/08 22:32:21
// Design Name:
// Module Name: axi_lite_slave
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module axi_lite_slave( //AXI Lite从机
//全局信号
input logic ACLK,
input logic ARESETn,
//写地址通道
input logic AWVALID,
input logic [31:0]AWADDR,
input logic [2:0]AWPROT,
output logic AWREADY,
//写数据通道
input logic WVALID,
input logic [31:0] WDATA,
input logic [3:0] WSTRB,
output logic WREADY,
//写响应通道
input logic BREADY,
output logic BVALID,
output logic [1:0] BRESP,
//读地址通道
input logic ARVALID,
input logic [31:0] ARADDR,
input logic [2:0] ARPROT,
output logic ARREADY,
//读数据通道
input logic RREADY,
output logic RVALID,
output logic [31:0] RDATA,
output logic [1:0] RRESP,
//其他
output logic busy //主机往从机写数据时,busy为高,知道数据被写入寄存器
);
parameter N=4; //四个寄存器
logic [31:0] DataReg [0:N-1];
logic [31:0]rd_addr;
logic [31:0]wr_addr;
logic [31:0]rd_data;
logic [31:0]wr_data;
logic wr_en;
//wr_en
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
wr_en<=0;
else if(WVALID&&WREADY)
wr_en<=1; //此时写数据地址和写数据均被暂存,可以进行写入
else
wr_en<=0;
//busy
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
busy<=0;
else if(AWVALID)
busy<=1;
else if(wr_en)
busy<=0;
//响应来自主机的写请求
//写地址通道
//AWREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
AWREADY<=0;
else if(AWVALID&&~AWREADY) //写地址通道有有效数据,拉高AWREADY以接收
AWREADY<=1;
else if(AWVALID&&AWREADY) //写地址通道数据传输结束
AWREADY<=0;
//wr_addr
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
wr_addr<=0;
else if(AWVALID&&AWREADY)
wr_addr<=AWADDR;
//写数据通道
//wr_data
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
wr_data<=0;
else if(WVALID&&WREADY)
wr_data<=WDATA;
//WREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
WREADY<=0;
else if(WVALID&&~WREADY)
WREADY<=1;
else if(WVALID&&WREADY)
WREADY<=0;
//将数据写入寄存器
always_ff@(posedge ACLK)
if(wr_en)
DataReg[wr_addr]<=wr_data;
//写响应通道
//BVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
BVALID<=0;
else if(WVALID&&WREADY)
BVALID<=1;
else if(BVALID&&BREADY&&BRESP==2'b00)
BVALID<=0;
//BRESP
always_comb
begin
BRESP=2'b00; //OKEY
end
//响应来自主机的读请求
//读地址通道
//ARREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
ARREADY<=0;
else if(ARVALID&&~ARREADY)
ARREADY<=1;
else if(ARVALID&&ARREADY) //读地址通道数据接受完毕
ARREADY<=0;
//rd_addr
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
rd_addr<=0;
else if(ARVALID&&ARREADY) //数据有效,存储地址
rd_addr<=ARADDR;
//读数据通道
//RVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
RVALID<=0;
else if(ARREADY&&ARVALID) //读地址通道结束,拉高RVALID发送数据
RVALID<=1;
else if(RVALID&&RREADY) //数据发送完毕
RVALID<=0;
//RRESP
always_comb
begin
RRESP=2'b00;
end
//RDATA
always_comb
begin
if(RVALID)
RDATA=DataReg[rd_addr];
else
RDATA=32'd0;
end
endmodule
主机:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/03/09 09:08:00
// Design Name:
// Module Name: axi_lite_master_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module axi_lite_master_test;
//全局信号
logic ACLK;
logic ARESETn;
//写地址通道
logic AWVALID;
logic [31:0]AWADDR;
logic [2:0]AWPROT;
logic AWREADY;
//写数据通道
logic WVALID;
logic [31:0] WDATA;
logic [3:0] WSTRB;
logic WREADY;
//写响应通道
logic BREADY;
logic BVALID;
logic [1:0] BRESP;
//读地址通道
logic ARVALID;
logic [31:0] ARADDR;
logic [2:0] ARPROT;
logic ARREADY;
//读数据通道
logic RREADY;
logic RVALID;
logic [31:0] RDATA;
logic [1:0] RRESP;
//其他
logic start_write;
logic start_read;
logic [31:0] rd_data;
logic busy;
//ACLK AND ARESETn
initial begin
ACLK=0;
forever begin
#5 ACLK=~ACLK;
end
end
initial begin
ARESETn=0;
#10
ARESETn=1;
end
//start_write
initial begin
start_write=0;
#50
start_write=1; //拉高一个周期以开始写数据操作
#10
start_write=0;
end
//发起写请求
//写地址通道
//AWVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
AWVALID<=0;
else if(start_write)
AWVALID<=1;
else if(AWVALID&&AWREADY) //写地址通道传输完毕
AWVALID<=0;
//AWADDR
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
AWADDR<=32'd0;
else if(start_write)
AWADDR<=32'd1;
//AWPROT
always_comb
begin
AWPROT=3'b000;
end
//写数据通道
//WDATA
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
WDATA<=0;
else if(AWVALID&&AWREADY) //写地址通道完成
WDATA<=32'd6;
//WVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
WVALID<=0;
else if(AWVALID&&AWREADY) //写地址通道结束,开始写数据,事实上,写数据和写地址通道可以同时进行
WVALID<=1;
else if(WVALID&&WREADY) //写数据完毕
WVALID<=0;
//WSTRB
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
WSTRB<=4'b0000;
else if(AWVALID&&AWREADY)
WSTRB<=4'b1111;
//写响应通道
//BREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
BREADY<=0;
else if(AWVALID&&AWREADY) //写地址通道结束后就可以提前拉高
BREADY<=1;
else if(BREADY&&BVALID&&BRESP==2'b00) //
BREADY<=0;
//发起读请求
//start_read
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
start_read<=0;
else if(BRESP==2'b00&&BVALID&&BREADY)
start_read<=1;
else
start_read<=0;
//读地址通道
//ARVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
ARVALID<=0;
else if(start_read)
ARVALID<=1;
else if(ARVALID&&ARREADY) //读地址通道结束
ARVALID<=0;
//ARADDR
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
ARADDR<=0;
else if(start_read)
ARADDR<=32'd1;
//ARPROT
always_comb
begin
ARPROT=3'b000;
end
//读数据通道
//RREADY
always_ff @(posedge ACLK,negedge ARESETn)
if(!ARESETn)
RREADY<=0;
else if(ARVALID&&ARREADY) //读地址通道结束后,拉高RREADY以准备接收数据
RREADY<=1;
else if(RREADY&&RVALID) //读数据完成
RREADY<=0;
//rd_data
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
rd_data<=0;
else if(RVALID&&RREADY) //同时为高,可读取数据
begin
rd_data<=RDATA;
$display("%d",rd_data);
end
//例化
axi_lite_slave U( //AXI Lite从机
//全局信号
.ACLK(ACLK),
.ARESETn(ARESETn),
//写地址通道
.AWVALID(AWVALID),
.AWADDR(AWADDR),
.AWPROT(AWPROT),
.AWREADY(AWREADY),
//写数据通道
.WVALID(WVALID),
.WDATA(WDATA),
.WSTRB(WSTRB),
.WREADY(WREADY),
//写响应通道
.BREADY(BREADY),
.BVALID(BVALID),
.BRESP(BRESP),
//读地址通道
.ARVALID(ARVALID),
.ARADDR(ARADDR),
.ARPROT(ARPROT),
.ARREADY(ARREADY),
//读数据通道
.RREADY(RREADY),
.RVALID(RVALID),
.RDATA(RDATA),
.RRESP(RRESP),
//其他
.busy(busy)
);
endmodule
仿真波形如下:
实现功能为主机往从机地址为1处写入6,然后再读出,可以看到,读出结果确实为6,代码功能正确。
修改主机代码,使得写地址和写数据同时进行(这对AXI Lite来说是可以的),有如下仿真波形图,结果仍为6,功能正确。
从机代码未考虑到WVALID比AWVALID先断言的情况,现修正如下(已经上板测试成功):
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/03/08 22:32:21
// Design Name:
// Module Name: axi_lite_slave
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module axi_lite_slave( //AXI Lite从机
//全局信号
input logic ACLK,
input logic ARESETn,
//写地址通道
input logic AWVALID,
input logic [31:0]AWADDR,
input logic [2:0]AWPROT,
output logic AWREADY,
//写数据通道
input logic WVALID,
input logic [31:0] WDATA,
input logic [3:0] WSTRB,
output logic WREADY,
//写响应通道
input logic BREADY,
output logic BVALID,
output logic [1:0] BRESP,
//读地址通道
input logic ARVALID,
input logic [31:0] ARADDR,
input logic [2:0] ARPROT,
output logic ARREADY,
//读数据通道
input logic RREADY,
output logic RVALID,
output logic [31:0] RDATA,
output logic [1:0] RRESP,
//其他
output logic busy //主机往从机写数据时,busy为高,知道数据被写入寄存器
);
parameter N=4; //四个寄存器
logic [31:0] DataReg [0:N-1];
logic [31:0]rd_addr;
logic [31:0]wr_addr;
logic [31:0]rd_data;
logic [31:0]wr_data;
logic [1:0] wr_reg_sel;
logic [1:0] rd_reg_sel;
logic wr_en;
assign wr_reg_sel=wr_addr[3:2];
assign rd_reg_sel=rd_addr[3:2];
//wr_en
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
wr_en<=0;
else if(WVALID&&WREADY&&AWVALID&&AWREADY)
wr_en<=1; //此时写数据地址和写数据均被暂存,可以进行写入
else
wr_en<=0;
//busy
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
busy<=0;
else if(AWVALID||WVALID)
busy<=1;
else if(wr_en)
busy<=0;
//响应来自主机的写请求
//写地址通道
//AWREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
AWREADY<=0;
else if(AWVALID&&WVALID&&~AWREADY) //写地址和写数据均有效时才拉高
AWREADY<=1;
else if(AWVALID&&AWREADY&&WVALID) //写地址通道数据传输结束
AWREADY<=0;
//wr_addr
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
wr_addr<=0;
else if(AWVALID&&AWREADY)
wr_addr<=AWADDR;
//写数据通道
//wr_data
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
wr_data<=0;
else if(WVALID&&WREADY)
wr_data<=WDATA;
//WREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
WREADY<=0;
else if(WVALID&&AWVALID&&~WREADY)
WREADY<=1;
else if(WVALID&&WREADY&&AWVALID)
WREADY<=0;
//将数据写入寄存器
always_ff@(posedge ACLK)
if(wr_en)
DataReg[wr_reg_sel]<=wr_data;
//写响应通道
//BVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
BVALID<=0;
else if(WVALID&&WREADY)
BVALID<=1;
else if(BVALID&&BREADY&&BRESP==2'b00)
BVALID<=0;
//BRESP
always_comb
begin
BRESP=2'b00; //OKEY
end
//响应来自主机的读请求
//读地址通道
//ARREADY
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
ARREADY<=0;
else if(ARVALID&&~ARREADY)
ARREADY<=1;
else if(ARVALID&&ARREADY) //读地址通道数据接受完毕
ARREADY<=0;
//rd_addr
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
rd_addr<=0;
else if(ARVALID&&ARREADY) //数据有效,存储地址
rd_addr<=ARADDR;
//读数据通道
//RVALID
always_ff@(posedge ACLK,negedge ARESETn)
if(!ARESETn)
RVALID<=0;
else if(ARREADY&&ARVALID) //读地址通道结束,拉高RVALID发送数据
RVALID<=1;
else if(RVALID&&RREADY) //数据发送完毕
RVALID<=0;
//RRESP
always_comb
begin
RRESP=2'b00;
end
//RDATA
always_comb
begin
if(RVALID)
RDATA=DataReg[rd_reg_sel];
else
RDATA=32'd0;
end
endmodule