FPGA - ZYNQ 基于Axi_Lite的PS和PL交互

前言

FPGA - ZYNQ 基于EMIO的PS和PL交互中介绍了ZYNQ 中PS端和PL端交互的开发流程,接下来构建基于基于Axi_Lite的PS和PL交互。

开发流程

Axi_Lite从机

FPGA - AXI4_Lite(实现用户端与axi4_lite之间的交互逻辑)中,详解介绍了AXI4总线,以及AXI_LITE端口信号及其功能,其中axi4_lite 读写过程框架图中介绍了axi4_lite主机搭建过程。

接下来构建axi4_lite从机

Axi4_lite端口信号及其功能

根据axi_lite读写信号分析:

在这里我们需要构建axi4lite_slaveuser_ram

axi4lite_slave:

`timescale 1ns / 1ps

module axilite_slave #(
	parameter USER_WR_DATA_WIDTH    = 32 , //用户写数据位宽和AXI4―Lite数据位宽保持一致
    parameter USER_RD_DATA_WIDTH    = 32 , //用户读数据位宽和AXI4―Lite数据位宽保持一致
	parameter AXI_DATA_WIDTH 		= 32,  //AXI4_LITE总线规定,数据位宽只支持32Bit或者64bit
	parameter AXI_ADDR_WIDTH        = 32	
)(

	input       								    axi_clk,    
	input       								    reset,

	output  reg								        s_wr_vld,
	output  reg    [USER_WR_DATA_WIDTH-1:0]	        s_wr_data,
	output  reg    [AXI_ADDR_WIDTH-1 :0]		    s_wr_addr,

	output  reg								        s_rd_addr_vld,
	output  reg    [AXI_ADDR_WIDTH-1 :0]		    s_rd_addr,
	input          [USER_RD_DATA_WIDTH-1:0]	        s_rd_data,
	input                                           s_rd_data_vld,

	input          [AXI_ADDR_WIDTH -1:0]  	        s_axi_awaddr, //axi write address channel
	input          [2:0] 				 	        s_axi_awprot, 
	input      		 				 	            s_axi_awvalid, 
	output reg    		 			 	            s_axi_awready,

	input          [AXI_DATA_WIDTH-1:0]	 	        s_axi_wdata,   //axi write data channel
	input          [AXI_DATA_WIDTH/8-1:0] 	        s_axi_wstrb,
	input           					 	        s_axi_wvalid,
	output  reg          					 	    s_axi_wready,

	output         [1:0]	         	            s_axi_bresp,  //axi wirte response channel
	output  reg	   			         	            s_axi_bvalid,
	input        			         	            s_axi_bready,

	input      		 							    s_axi_arvalid, // axi read address channel
	output  reg       		 					    s_axi_arready, 
	input          [AXI_ADDR_WIDTH-1:0] 			s_axi_araddr,
	input          [2:0] 							s_axi_arprot, 

	output  reg    [AXI_DATA_WIDTH-1:0]	    		s_axi_rdata,   // axi read data channel
	output         [1:0] 				    		s_axi_rresp,
	output  reg     					    		s_axi_rvalid,
	input        						   	 		s_axi_rready
    );

(* dont_touch="true" *) reg a_reset_sync_d0;
(* dont_touch="true" *) reg a_reset_sync_d1;
(* dont_touch="true" *) reg a_reset_sync;
/*------------------------------------------*\
               状态机信号定义
\*------------------------------------------*/
reg [1:0] wr_cur_status;
reg [1:0] wr_nxt_status;
reg [1:0] rd_cur_status;
reg [1:0] rd_nxt_status;

localparam WR_IDLE    = 3'b000;
localparam WE_DATA    = 3'b001;
localparam WR_BRESP   = 3'b010;

localparam RD_IDLE    = 3'b000;
localparam RD_PRE     = 3'b001;
localparam RD_DATA    = 3'b010;

/*------------------------------------------*\
                 assign
\*------------------------------------------*/
assign s_axi_bresp = 0;
assign s_axi_rresp  = 0;

/*------------------------------------------*\
                     CDC
\*------------------------------------------*/
always @(posedge axi_clk) begin
	a_reset_sync_d0 <= reset;
	a_reset_sync_d1 <= a_reset_sync_d0;
	a_reset_sync    <= a_reset_sync_d1;
end

/*------------------------------------------*\
             AXILITE从机写过程
\*------------------------------------------*/
always @(posedge axi_clk) begin
    if (a_reset_sync) 
        wr_cur_status <= WR_IDLE;
    else 
        wr_cur_status <= wr_nxt_status;
end

always @(*) begin
    if (a_reset_sync) 
        wr_nxt_status <= WR_IDLE;
    else 
    	case(wr_cur_status)
    		WR_IDLE : begin
    			if (s_axi_awvalid && s_axi_wvalid) 
    				wr_nxt_status <= WE_DATA;
    			else 
    				wr_nxt_status <= wr_cur_status;
    		end
    		WE_DATA : begin
    			wr_nxt_status <= WR_BRESP;
    		end
    		WR_BRESP : begin
    			if (s_axi_bvalid && s_axi_bready) 
    				wr_nxt_status <= WR_IDLE;
    			else 
    				wr_nxt_status <= wr_cur_status;
    		end
    	    default : wr_nxt_status <= WR_IDLE;
    	endcase    
end

always @(*) begin
    if (a_reset_sync) begin
    	s_axi_awready <= 0;
    	s_axi_wready  <= 0;
    end  
    else begin
    	s_axi_awready <= wr_cur_status == WE_DATA;
    	s_axi_wready  <= wr_cur_status == WE_DATA;    	
    end  
end

always @(posedge axi_clk) begin
    if (a_reset_sync) 
        s_axi_bvalid <= 0;
    else if (s_axi_bvalid && s_axi_bready) 
        s_axi_bvalid <= 0;
    else if (wr_cur_status == WR_BRESP)
        s_axi_bvalid <= 1'b1;
    else 
    	s_axi_bvalid <= s_axi_bvalid;
end

always @(posedge axi_clk) begin
    if (wr_cur_status == WE_DATA) begin
    	s_wr_vld  <= 1'b1;
    	s_wr_data <= s_axi_wdata;
    	s_wr_addr <= s_axi_awaddr;
    end
    else begin
    	s_wr_vld  <= 0;
    	s_wr_data <= s_wr_data;
    	s_wr_addr <= s_wr_addr;    	
    end    
end

/*------------------------------------------*\
             AXILITE从机读过程
\*------------------------------------------*/
always @(posedge axi_clk) begin
    if (a_reset_sync) 
        rd_cur_status <= RD_IDLE;
    else 
        rd_cur_status <= rd_nxt_status;
end

always @(*) begin
    if (a_reset_sync) 
        rd_nxt_status <= RD_IDLE;
    else 
    	case(rd_cur_status)
    		RD_IDLE : begin
    			if (s_axi_arvalid)
    				rd_nxt_status <= RD_PRE;
    			else 
    				rd_nxt_status <= rd_cur_status;
    		end
    		RD_PRE : begin
    			rd_nxt_status <= RD_DATA;
    		end
    		RD_DATA : begin
    			if (s_axi_rvalid && s_axi_rready) 
    				rd_nxt_status <= RD_IDLE;
    			else 
    				rd_nxt_status <= rd_cur_status;	
    		end

    	    default : rd_nxt_status <= RD_IDLE;
    	endcase    
end

always @(*) begin
    if (a_reset_sync) 
        s_axi_arready <= 0; 
    else 
        s_axi_arready <= rd_cur_status == RD_PRE;
end

always @(posedge axi_clk) begin
    if (rd_cur_status == RD_PRE) begin
    	s_rd_addr_vld <= 1'b1;
    	s_rd_addr     <= s_axi_araddr;
    end
    else begin
    	s_rd_addr_vld <= 0;   
    	s_rd_addr     <= s_rd_addr; 	
    end     
end

always @(posedge axi_clk) begin
    if (a_reset_sync) begin
 		s_axi_rdata  <= 0;
 		s_axi_rvalid <= 0;
    end
    else if (s_axi_rvalid && s_axi_rready) begin
		s_axi_rvalid <= 0;
    end
    else if (s_rd_data_vld) begin
    	s_axi_rvalid <= 1'b1;
    	s_axi_rdata  <= s_rd_data;
    end
    else begin
    	s_axi_rvalid <= s_axi_rvalid;
    	s_axi_rdata  <= s_axi_rdata;
    end    
end

endmodule

user_ram :

`timescale 1ns / 1ps



module user_ram #(
	parameter USER_WR_DATA_WIDTH 	=   32 ,
	parameter USER_RD_DATA_WIDTH 	=   32 ,
	parameter AXI_DATA_WIDTH        =   32 , //注意AXI4的数据位宽只有32Bit或者64bit
	parameter AXI_ADDR_WIDTH        =   32	
)(
	input                                           clk          ,
	input                                           reset        ,
	input  								            s_wr_vld     ,
	input      [USER_WR_DATA_WIDTH-1:0]	            s_wr_data    ,
	input      [AXI_ADDR_WIDTH-1 :0]		        s_wr_addr    ,

	input  								            s_rd_addr_vld,
	input      [AXI_ADDR_WIDTH-1 :0]		        s_rd_addr    ,
	output reg [USER_RD_DATA_WIDTH-1:0]	            s_rd_data    ,
	output reg                                      s_rd_data_vld

    );
localparam SIZE = 1024;
reg [AXI_DATA_WIDTH-1:0] ram [SIZE - 1 : 0] ;


always @(posedge clk) begin
    if (s_wr_vld) 
    	ram[s_wr_addr] <= s_wr_data;
end

always @(posedge clk) begin
    if (reset) begin
        s_rd_data_vld <= 0;
        s_rd_data     <= 0;
    end
    else if (s_rd_addr_vld) begin
    	s_rd_data_vld <= 1'b1;
    	s_rd_data     <= ram[s_rd_addr];
    end   
    else begin
    	s_rd_data_vld <= 0;
    	s_rd_data     <= 0;
    end
        
end
endmodule

IP核生成

在ZYNQ开发中,要将构建的axi4lite_slave和user_ram打包为 IP核

首先,创建新工程,将axi4lite_slave和user_ram代码导入:

点击OK

点击Finish

可以看到在Sources界面中已经有axi4lite_slave和user_ram文件

可以看到axi4lite_slave 是顶层。


然后开始打包创建IP核

点击NEXT

选择IP保存位置 点击Next

点击OK

点击Finish

然后会弹出一个新工程:

这里是IP配置信息:

保持默认 然后点击Review and Package , 点击Package IP:

点击Yes

然后打开IP保存位置文件夹,可以看到如下:

点击src文件夹里面是axilite_slave.v文件

点击xgui文件夹是axilite_slave_v1_0.tcl文件

这样axilite_slave IP核打包完成。

然后切换user_ram文件为顶层:

右击user_ram文件 点击Set as Top

然后重复axilite_slave打包过程,

打开IP核存放地址

至此,axi4lite_slave和user_ram文件打包ip核完成。


硬件系统搭建

搭建硬件系统

具体构建过程可见:

FPGA - ZYNQ 基于EMIO的PS和PL交互https://blog.csdn.net/weixin_46897065/article/details/137865852?spm=1001.2014.3001.5501如下:


然后需要将上面打包的IP核,加载到IP库中:

点击Seting ,再点击IP,然后点Repository

找到上面axi4lite_slave和user_ram IP 核存放位置,点击Select:

点击ok

然后搜索axilite_slave和user_ram:

双击添加,点击Run Block Automation

然后点击RUN connection Automation

连线完成如下:


由于axi4lite_slave和user_ram是高复位,所以删除原来的低复位,重新连接高复位引脚:

删除重新连接:

复位连接完成如下:

然后将axilite_slave 引脚user_ram 引脚相连:

连接完成如下:

然后点击重新布局:

然后点击验证设计:

点击OK

然后按照FPGA - ZYNQ 基于EMIO的PS和PL交互中的开发流程:

生成封装,生成底层和顶层文件,

然后生成比特流,导出硬件,启动SDK。


SDK 程序设计

创建SDK工程

点击空工程 点击finish

添加source file


在硬件系统搭建中,我们看到,自动连线后,会出现一个AXI Interconnect。如下图:

这个模块在PS设计中,通过API接口实现axilite读写。

基于自定义AXI_lite 与 PS API接口 之间的映射关系

PS端API函数 和 AXI4_lite 总线的映射关系 对应关系
写入数据 Xil_Out32()函数
读出数据 Xil_In32() 函数

1,利用API接口函数实现读写axilite读写:

#include "xparameters.h"
#include "sleep.h"
#include "xil_io.h"

#define AXI_LITE_BASEADDR 0x40000000

 通过函数编写

int main()
{
	u32 rddata;

	Xil_Out32(AXI_LITE_BASEADDR,1000);
	Xil_Out32(AXI_LITE_BASEADDR + 4,500);

	Xil_Out32(AXI_LITE_BASEADDR + 8,800);


	rddata = Xil_In32(AXI_LITE_BASEADDR);
	rddata = Xil_In32(AXI_LITE_BASEADDR + 4);
	return 0;
}

2,利用指针实现读写axilite读写:

#include "xparameters.h"
#include "sleep.h"
#include "xil_io.h"

#define AXI_LITE_BASEADDR 0x40000000

int main()
{
	u32* LitePtr  = (u32*)AXI_LITE_BASEADDR;  //强制转换 转为地址
	u32 wrdata = 0;
	u32 rddata = 0;

	int i = 0;

	//向PL写数据
	for (i = 0; i < 128; i++ )
	{
		*LitePtr++ = wrdata++;
	}

	LitePtr  = (u32*)AXI_LITE_BASEADDR;
	for (i = 0; i < 128; i++ )
	{
		rddata = *LitePtr++;
		printf("rddata= %d \n",rddata);
	}
	return 0;
}

最后,下载验证。

总结

在这里,实现了基于Axi_Lite的PS和PL交互,和axilite_slave(axilite从机)的实现,以及自定义IP核的创建,并且在SDK程序中实现了2种axilite的读写。

相关推荐
stm 学习ing2 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
北城笑笑13 小时前
FPGA 14 ,硬件开发板分类详解,FPGA开发板与普通开发板烧录的区别
fpga开发·fpga
2202_7544215413 小时前
一个计算频率的模块
驱动开发·fpga开发
小灰灰的FPGA14 小时前
低速接口项目之串口Uart开发(七)——如何在FPGA项目中实现自适应波特率串口功能
fpga开发
fei_sun1 天前
【Verilog】第一章作业
fpga开发·verilog
深圳市雷龙发展有限公司longsto1 天前
基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面
fpga开发
9527华安2 天前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
able陈2 天前
为什么verilog中递归函数需要定义为automatic?
fpga开发
fei_sun2 天前
【Verilog】第二章作业
fpga开发·verilog
碎碎思2 天前
如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目
fpga开发·接口隔离原则