提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
ZYNQ开发系列——使用AXI4LITE接口进行PS和PL交互
- 前言
- PS端AXI接口
- AXI4LITE slave模块的设计
- 后记
前言
前面我们讲到使用AXI4LITE来作为总线接口来实现PS和PL的交互。同时我们为了支持《从零开始研发GPS接收机连载系列》博文的内容,将PS端的内容与我的ZYNQ7030+AD9363硬件平台进行同步。
PS端AXI接口
在ZYNQ7030+AD9363平台中,PS端也是规划为使用AXI4LITE总线与PL进行交互,其设计如下
这个设计上很简单。
ZYNQ7内核的协议交互接口主要配置如下:
配置一个M AXI接口,这个作为PS和PL的AXI交互接口,端口直接出来是AXI3协议接口,后面通过AXI_interconnect模块进行协议转换为AXI4LITE
为其分配地址以及区间为:
注意,BUS_AXI这个端口的PROTOCOL协议需改成AXI4LITE。
AXI4LITE slave模块的设计
在我的博文《ZYNQ开发系列——AXI4LITE协议的理解》中说明了可以直接利用vivado的 创建新AXI4外设的功能来替我们把AXI4LITE接口的代码给写出来,在这里我们稍加进行修改,变成如下模样,注意我们是需要对总线进行分段设计,做成如下框图所示:
这个AXI4LITE总线转换模块代码如下:
`timescale 1 ns / 1 psmodule axi_bus
(output BUS_CLK,output BUS_RST_N,input [15 : 0] BUS_AXI_awaddr,input [2 : 0] BUS_AXI_awprot,input BUS_AXI_awvalid,output reg BUS_AXI_awready,input [31 : 0] BUS_AXI_wdata, input [3 : 0] BUS_AXI_wstrb,input BUS_AXI_wvalid,output reg BUS_AXI_wready,output [1 : 0] BUS_AXI_bresp,output reg BUS_AXI_bvalid,input BUS_AXI_bready,input [15 : 0] BUS_AXI_araddr,input [2 : 0] BUS_AXI_arprot,input BUS_AXI_arvalid,output reg BUS_AXI_arready,output reg [31 : 0] BUS_AXI_rdata,output [1 : 0] BUS_AXI_rresp,output reg BUS_AXI_rvalid,input BUS_AXI_rready,input clk_sys,input rst_n,output hreg_wren,output time_wren,output acq_wren,output trkflg_wren,output [11:0]track_wren,output hreg_rden,output time_rden,output acq_rden,output trkflg_rden,output [11:0]track_rden, output [5:0]fpga_waddress,output [5:0]fpga_raddress,output [31:0]fpga_wdata,input [31:0]fpga_rdata );reg [15 : 0] axi_awaddr ;
reg [15 : 0] axi_araddr ;
reg [31 : 0] axi_rdata ;
reg [31 : 0] axi_rdata_pre ;
reg axi_rvalid ;wire all_reg_rden;
wire all_reg_wren;
reg [31:0] all_reg;reg [15:0] all_waddr;
reg [31:0] reg_data_out;
reg aw_en;assign BUS_CLK = clk_sys;
assign BUS_RST_N = rst_n;assign BUS_AXI_bresp = 2'b00;
assign BUS_AXI_rresp = 2'b00;always @( posedge BUS_CLK)
beginif ( BUS_RST_N == 1'b0 )beginBUS_AXI_awready <= 1'b0;aw_en <= 1'b1;end elsebegin if (~BUS_AXI_awready && BUS_AXI_awvalid && BUS_AXI_wvalid && aw_en)beginBUS_AXI_awready <= 1'b1;aw_en <= 1'b0;endelse if (BUS_AXI_bready && BUS_AXI_bvalid)beginaw_en <= 1'b1;BUS_AXI_awready <= 1'b0;endelse beginBUS_AXI_awready <= 1'b0;endend
end always @( posedge BUS_CLK)
beginif ( BUS_RST_N == 1'b0 )beginaxi_awaddr <= 0;end elsebegin if (~BUS_AXI_awready && BUS_AXI_awvalid && BUS_AXI_wvalid && aw_en)beginaxi_awaddr <= BUS_AXI_awaddr;endend
end always @( posedge BUS_CLK)
beginif ( BUS_RST_N == 1'b0 )beginBUS_AXI_wready <= 1'b0;end elsebegin if (~BUS_AXI_wready && BUS_AXI_wvalid && BUS_AXI_awvalid && aw_en )beginBUS_AXI_wready <= 1'b1;endelsebeginBUS_AXI_wready <= 1'b0;endend
end assign all_reg_wren = BUS_AXI_wready && BUS_AXI_wvalid && BUS_AXI_awready && BUS_AXI_awvalid;assign fpga_waddress = axi_awaddr[7:2];
assign fpga_wdata = BUS_AXI_wdata;assign hreg_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0000_0000;
assign time_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0000_0001;
assign acq_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0011_1110;
assign trkflg_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0011_1111;genvar i;
generate
for (i = 0; i < 12; i = i + 1) begin: TRACK_CS_Nassign track_wren[i] = all_reg_wren && ( axi_awaddr[15:8] == 8'b0100_0000 + i);
end
endgenerate///always @( posedge BUS_CLK)
beginif ( BUS_RST_N == 1'b0 )beginBUS_AXI_bvalid <= 0;end elsebegin if (BUS_AXI_awready && BUS_AXI_awvalid && ~BUS_AXI_bvalid && BUS_AXI_wready && BUS_AXI_wvalid)beginBUS_AXI_bvalid <= 1'b1;end elsebeginif (BUS_AXI_bready && BUS_AXI_bvalid) beginBUS_AXI_bvalid <= 1'b0; end endend
end always @( posedge BUS_CLK)
beginif ( BUS_RST_N == 1'b0 )beginBUS_AXI_arready <= 1'b0;end elsebegin if (~BUS_AXI_arready && BUS_AXI_arvalid)beginBUS_AXI_arready <= 1'b1;endelsebeginBUS_AXI_arready <= 1'b0;endend
end always @( posedge BUS_CLK )beginif ( BUS_RST_N == 1'b0 )beginBUS_AXI_rvalid <= 0;end elsebegin if (BUS_AXI_arready && BUS_AXI_arvalid && ~BUS_AXI_rvalid)beginBUS_AXI_rvalid <= 1'b1;end else if (BUS_AXI_rvalid && BUS_AXI_rready)beginBUS_AXI_rvalid <= 1'b0;end endend assign fpga_raddress = BUS_AXI_araddr[7:2];assign hreg_rden = BUS_AXI_araddr[15:8] == 8'b0000_0000;
assign time_rden = BUS_AXI_araddr[15:8] == 8'b0000_0001;
assign acq_rden = BUS_AXI_araddr[15:8] == 8'b0011_1110;
assign trkflg_rden = BUS_AXI_araddr[15:8] == 8'b0011_1111;generate
for (i = 0; i < 12; i = i + 1) begin: TRACK_CS_N1assign track_rden[i] = (BUS_AXI_araddr[15:8] == 8'b0100_0000 + i) ;
end
endgenerate assign all_reg_rden = BUS_AXI_arready && BUS_AXI_arvalid && BUS_AXI_rvalid == 0;always @( posedge BUS_CLK)
beginif ( BUS_RST_N == 1'b0 )beginBUS_AXI_rdata <= 0;end elsebegin if (all_reg_rden)beginBUS_AXI_rdata <= fpga_rdata;end end
end endmodule
注意:
1、 AXI地址是以字节为单位进行递增的,因此每一个地址实际上代表的是一个字节,而我们一般交互一次是32bit数据,因此AXI地址我们要舍去低2bit。
2、 采用地址分区块的方式,各个模块的地址范围已经在这里进行了划分,这样每个模块只需要关注对偏移地址的分配,各个模块的地址不会互相干扰和重合
例如:
assign hreg_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0000_0000;
assign time_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0000_0001;
assign acq_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0011_1110;
assign trkflg_wren = all_reg_wren && axi_awaddr[15:8] == 8'b0011_1111;
assign hreg_rden = BUS_AXI_araddr[15:8] == 8'b0000_0000;
assign time_rden = BUS_AXI_araddr[15:8] == 8'b0000_0001;
assign acq_rden = BUS_AXI_araddr[15:8] == 8'b0011_1110;
assign trkflg_rden = BUS_AXI_araddr[15:8] == 8'b0011_1111;
特别要注意,参考vivado的 创建新AXI4外设的例程,对于读总线操作,需要进行数据汇聚操作
每一个模块需要将读数据写成三态总线的形式:
assign hreg_rdata = (hreg_rden == 1) ? data_out : 32'hzzzzzzzz;
后记
这里对PL端的AXI4-LITE slave总线接口进行了设计,但为了验证其是否正确,在后面的博文中将会讲到对齐进行读写验证操作