vivado PL与PS的数据交互之AXI4_LITE 协议
本文详细介绍了基于Zynq平台的PL与PS的数据交互的AXI4-Lite协议开发过程,包括协议概述、IP核生成与应用、仿真测试、上板测试等关键内容。通过测试验证IP功能的方法,为Zynq平台PS与PL的数据交互开发提供了完整的技术方案。文章配套的代码示例和参数化设计方法,可直接应用于实际工程项目开发。
以下内容仅记录本人工作中发现的问题以及解决方法,仅供参考。
文章记录了作者在基于zynq平台PL与PS的数据交互开发过程中,总结的AXI4_LITE通信协议开发经验,并在文章中详细介绍了VIVADO软件中AXI4_LITE IP核的生成、测试版本及正式版本工程BD(BLOCK DESIGN)的搭建、从机PL端AXI4_LITE IP核的协议层代码开发、主机PS端应用层的代码开发、仿真测试工程以及上板工程的完整代码及测试结果。
本文着重介绍PL与PS的AXI4-Lite协议交互,AXI4-Lite协议的PL端应用开发详见另一篇笔记:vivado PL端串口通信协议及AXI_Uartlite (2.0)IP驱动开发https://blog.csdn.net/weixin_46168087/article/details/155612353?spm=1001.2014.3001.5502
目录
一、AXI4_LITE协议概述
AXI协议官方下载链接:https://developer.arm.com/documentation/ihi0022/latest
AXI4-Lite 是 AXI4 协议的轻量级子集,专为低速、低带宽的控制类交互设计(如寄存器读写、外设配置),是 Zynq-7000中 PS与 PL通信的核心总线之一,也是嵌入式硬件开发中最常用的 AXI 子协议。
| 协议 | 适用场景 | 核心差异 |
|---|---|---|
| AXI4-Lite | 低速控制(寄存器读写) | 无突发、固定 32/64 位、轻量 |
| AXI4 | 高速数据传输(如 DDR) | 支持突发(最大 256 拍)、缓存 |
| AXI-Stream | 流式数据(如视频 / 音频) | 无地址、连续数据流、低延迟 |
1.AXI4-LITE协议接口
AXI4-Lite 包括全局时钟复位和 5 个独立通道(写地址/写数据/写响应/读地址/读数据通道),主设备(如 Zynq PS)发起请求,从设备(如 PL 自定义 IP)响应,AXI4-Lite协议接口如下:
| 序号 | 类型 | 命名 | 位宽(bit) | 输入/输出 | 定义 |
| 1 | 全局信号 | ACLK | 1 | input | 时钟 |
| 2 | ARESET | 1 | input | 复位:低有效 | |
| 3 | 写地址通道 | AWVALID | 1 | input | 写地址有效位:高有效 |
| 4 | AWREADY | 1 | output | 从机写地址通道准备就绪信号:高有效 | |
| 5 | AWDATA | 4 | input | 写地址 | |
| 7 | 写数据通道 | WVALID | 1 | input | 写数据有效位:高有效 |
| 8 | WREADY | 1 | output | 从机写数据通道准备就绪信号:高有效 | |
| 9 | WDATA | 32 | input | 写数据 | |
| 10 | WSTRB | 4 | input |
写数据选择:bit[0]=1=data[7:0] bit[1]=1=data[15:8]以此类推,默认全高 |
|
| 11 | 写响应通道 | BVALID | 1 | output | 写入响应有效位:高有效 |
| 12 | BREADY | 1 | input | 主机接收响应通道准备就绪信号:高有效 | |
| 13 | BRESP | 2 | output | 写响应状态(一般不用) | |
| 14 | 读地址通道 | ARVALID | 1 | input | 读地址有效位:高有效 |
| 15 | ARREADY | 1 | output | 从机读地址通道准备就绪信号:高有效 | |
| 16 | ARDATA | 4 | input | 读地址 | |
| 18 | 读数据通道 | RVALID | 1 | output | 读数据有效位:高有效 |
| 19 | RREADY | 1 | input | 主机读数据通道准备就绪信号:高有效 | |
| 20 | RDATA | 32 | output | 读数据 | |
| 21 | RRESP | 2 | output | 读响应状态(00 = 成功,一般不用) |
2.AXI4-Lite 写数据工作流程
写入、读取之间存在通道依赖关系。读写流程示意图中的单向箭头表示可在箭头起点信号前或后触发的信号。双向箭头表示必须在箭头起点信号触发后才能触发的信号。

根据写数据流程图可知,写数据只与三个通道有关,写地址通道/写数据通道/写响应通道,简单概括一下写数据的流程为:
step1:写入数据(WDATA)、写入地址(AWDATA)、写数据有效位拉高(WVALID)、写地址有效位拉高(AWVALID)
step2:等待从机写数据通道准备就绪(WREADY)、等待从机写地址通道准备就绪(AWREADY)
step3:等待写入响应有效(BVALID)、主机接收响应通道准备就绪信号拉高(BREADY)、返回step1循环
3.AXI4-Lite 读数据 工作流程

根据读数据流程图可知,读数据只与两个通道有关,读地址通道/读数据通道,简单概括一下读数据的流程为::
step1:写入读地址(ARDATA)、读地址有效位拉高(WVALID)
step2:等待从机读地址通道准备就绪(ARREADY)
step3:主机读数据通道准备就绪信号拉高(RREADY)
step4:等待从机读数据有效位拉高(RVALID)、提取数据(RDATA)、返回step1循环
二、AXI4_LITE IP核
1.AXI4-Lite IP核的生成
打开vivado软件,点击Tools下拉选项,点击Create and Package New IP。

点击next。

这里选择IP存放的路径,以及更改自定义IP的命名为axi4_lite_slave。

接口模式选择从机模式,寄存器数量先随便写4,后面再改。

选择编辑IP,进行下一步根据实际应用来更改IP。

2.AXI4-Lite IP核的应用
在上一章节点击完成后可以看到IP编辑界面已经打开,IP已经生成,观察代码可以发现读数据状态机的内容,即为重复4个寄存器的读写,若前面设置寄存器数量为128个,则状态机会重复128次。

简化状态机,以及根据自己的需求定义读写数据寄存器的个数,定义AXI4_lite协议的基地址,将需要读出的值提出来,需要写入的值发出去,就是本节IP核的应用中改造这段代码的目的。
本实验自定义PL端从地址0x43c00000中读取来自PS端的6个32位的数data_out0~data_out5,PL端向PS端给地址0x43c00000写入4个32位的数data_in0~data_in3。
更改后的axi4_lite_slave_v1_0_S00_AXI.v文件:
module axi4_lite_slave_v1_0_S00_AXI #(
parameter integer C_S_AXI_DATA_WIDTH = 32,
parameter integer C_S_AXI_ADDR_WIDTH = 32,
parameter integer S_AXI_RD_DATA_DEPTH = 6,
parameter integer S_AXI_WR_DATA_DEPTH = 4,
parameter integer S_AXI_RDADDR_BASE = 32'h43c00000
)(
input wire S_AXI_ACLK ,
input wire S_AXI_ARESETN ,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR ,
input wire [2 : 0] S_AXI_AWPROT ,
input wire S_AXI_AWVALID ,
output wire S_AXI_AWREADY ,
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA ,
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB ,
input wire S_AXI_WVALID ,
output wire S_AXI_WREADY ,
output wire [1 : 0] S_AXI_BRESP ,
output wire S_AXI_BVALID ,
input wire S_AXI_BREADY ,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR ,
input wire [2 : 0] S_AXI_ARPROT ,
input wire S_AXI_ARVALID ,
output wire S_AXI_ARREADY ,
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA ,
output wire [1 : 0] S_AXI_RRESP ,
output wire S_AXI_RVALID ,
input wire S_AXI_RREADY ,
input wire [31:0] data_in0 ,
input wire [31:0] data_in1 ,
input wire [31:0] data_in2 ,
input wire [31:0] data_in3 ,
output wire [31:0] data_out0 ,
output wire [31:0] data_out1 ,
output wire [31:0] data_out2 ,
output wire [31:0] data_out3 ,
output wire [31:0] data_out4 ,
output wire [31:0] data_out5
);
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer ADDR_HSB = clogb2(S_AXI_RD_DATA_DEPTH-1)+ADDR_LSB;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg axi_rvalid;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg [0:S_AXI_RD_DATA_DEPTH-1];
wire slv_reg_rden;
wire slv_reg_wren;
integer byte_index;
reg aw_en;
wire [7:0] write_index;
wire [7:0] read_index ;
assign write_index = axi_awaddr[ADDR_HSB:ADDR_LSB];
assign read_index = axi_araddr[ADDR_HSB:ADDR_LSB];
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
assign S_AXI_AWREADY = axi_awready;
assign S_AXI_WREADY = axi_wready;
assign S_AXI_BRESP = axi_bresp;
assign S_AXI_BVALID = axi_bvalid;
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = axi_rresp;
assign S_AXI_RVALID = axi_rvalid;
assign data_out0 = slv_reg[0];
assign data_out1 = slv_reg[1];
assign data_out2 = slv_reg[2];
assign data_out3 = slv_reg[3];
assign data_out4 = slv_reg[4];
assign data_out5 = slv_reg[5];
always @( posedge S_AXI_ACLK )begin
if( S_AXI_ARESETN == 1'b0 )begin
axi_awready <= 1'b0;
aw_en <= 1'b1;
end
else begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)begin
axi_awready <= 1'b1;
aw_en <= 1'b0;
end
else if (S_AXI_BREADY && axi_bvalid) begin
aw_en <= 1'b1;
axi_awready <= 1'b0;
end
else begin
axi_awready <= 1'b0;
end
end
end
always @( posedge S_AXI_ACLK )begin
if ( S_AXI_ARESETN == 1'b0 )begin
axi_awaddr <= 0;
end
else begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)begin
axi_awaddr <= S_AXI_AWADDR;
end
end
end
always @( posedge S_AXI_ACLK )begin
if ( S_AXI_ARESETN == 1'b0 )begin
axi_wready <= 1'b0;
end
else begin
if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )begin
axi_wready <= 1'b1;
end
else begin
axi_wready <= 1'b0;
end
end
end
reg [7:0] slv_reg_cnt;
always @( posedge S_AXI_ACLK )begin
if ( S_AXI_ARESETN == 1'b0 )begin
for(slv_reg_cnt=0;slv_reg_cnt<S_AXI_RD_DATA_DEPTH;slv_reg_cnt = slv_reg_cnt+1)
slv_reg[slv_reg_cnt] <= 0;
end
else begin
if (slv_reg_wren)begin
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
slv_reg[write_index][(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
end
end
end
always @( posedge S_AXI_ACLK )begin
if ( S_AXI_ARESETN == 1'b0 )begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
end
else begin
if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)begin
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0;
end
else begin
if (S_AXI_BREADY && axi_bvalid) begin
axi_bvalid <= 1'b0;
end
end
end
end
always @( posedge S_AXI_ACLK )begin
if ( S_AXI_ARESETN == 1'b0 )begin
axi_arready <= 1'b0;
axi_araddr <= 32'b0;
end
else begin
if (~axi_arready && S_AXI_ARVALID)begin
axi_arready <= 1'b1;
axi_araddr <= S_AXI_ARADDR;
end
else begin
axi_arready <= 1'b0;
end
end
end
always @( posedge S_AXI_ACLK )begin
if ( S_AXI_ARESETN == 1'b0 )begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else begin
if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)begin
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0;
end
else if (axi_rvalid && S_AXI_RREADY)begin
axi_rvalid <= 1'b0;
end
end
end
always@(posedge S_AXI_ACLK or negedge S_AXI_ARESETN)begin
if (!S_AXI_ARESETN)begin
axi_rdata <= 'd0;
end
else begin
if (slv_reg_rden)begin
case(axi_araddr)
32'h43c00000 :begin axi_rdata <= data_in0 ;end
32'h43c00004 :begin axi_rdata <= data_in1 ;end
32'h43c00008 :begin axi_rdata <= data_in2 ;end
32'h43c0000C :begin axi_rdata <= data_in3 ;end
default :begin axi_rdata <= axi_rdata; end
endcase
end
end
end
endmodule
更改后的axi4_lite_slave_v1_0.v文件:
`timescale 1 ns / 1 ps
module axi4_lite_slave_v1_0 #
(
parameter integer C_S_AXI_DATA_WIDTH = 32,
parameter integer C_S_AXI_ADDR_WIDTH = 32,
parameter integer S_AXI_RD_DATA_DEPTH = 6,
parameter integer S_AXI_WR_DATA_DEPTH = 4,
parameter integer S_AXI_RDADDR_BASE = 32'h43c00000
)
(
input wire s00_axi_aclk,
input wire s00_axi_aresetn,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
input wire [2 : 0] s00_axi_awprot,
input wire s00_axi_awvalid,
output wire s00_axi_awready,
input wire [C_S_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
input wire s00_axi_wvalid,
output wire s00_axi_wready,
output wire [1 : 0] s00_axi_bresp,
output wire s00_axi_bvalid,
input wire s00_axi_bready,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
input wire [2 : 0] s00_axi_arprot,
input wire s00_axi_arvalid,
output wire s00_axi_arready,
output wire [C_S_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
output wire [1 : 0] s00_axi_rresp,
output wire s00_axi_rvalid,
input wire s00_axi_rready,
input wire [31:0] data_in0 ,
input wire [31:0] data_in1 ,
input wire [31:0] data_in2 ,
input wire [31:0] data_in3 ,
output wire [31:0] data_out0 ,
output wire [31:0] data_out1 ,
output wire [31:0] data_out2 ,
output wire [31:0] data_out3 ,
output wire [31:0] data_out4 ,
output wire [31:0] data_out5
);
// Instantiation of Axi Bus Interface S00_AXI
axi4_lite_slave_v1_0_S00_AXI # (
.C_S_AXI_DATA_WIDTH (C_S_AXI_DATA_WIDTH ),
.C_S_AXI_ADDR_WIDTH (C_S_AXI_ADDR_WIDTH ),
.S_AXI_RD_DATA_DEPTH (S_AXI_RD_DATA_DEPTH),
.S_AXI_WR_DATA_DEPTH (S_AXI_WR_DATA_DEPTH),
.S_AXI_RDADDR_BASE (S_AXI_RDADDR_BASE )
) axi4_lite_slave_v1_0_S00_AXI_inst (
.S_AXI_ACLK(s00_axi_aclk),
.S_AXI_ARESETN(s00_axi_aresetn),
.S_AXI_AWADDR(s00_axi_awaddr),
.S_AXI_AWPROT(s00_axi_awprot),
.S_AXI_AWVALID(s00_axi_awvalid),
.S_AXI_AWREADY(s00_axi_awready),
.S_AXI_WDATA(s00_axi_wdata),
.S_AXI_WSTRB(s00_axi_wstrb),
.S_AXI_WVALID(s00_axi_wvalid),
.S_AXI_WREADY(s00_axi_wready),
.S_AXI_BRESP(s00_axi_bresp),
.S_AXI_BVALID(s00_axi_bvalid),
.S_AXI_BREADY(s00_axi_bready),
.S_AXI_ARADDR(s00_axi_araddr),
.S_AXI_ARPROT(s00_axi_arprot),
.S_AXI_ARVALID(s00_axi_arvalid),
.S_AXI_ARREADY(s00_axi_arready),
.S_AXI_RDATA(s00_axi_rdata),
.S_AXI_RRESP(s00_axi_rresp),
.S_AXI_RVALID(s00_axi_rvalid),
.S_AXI_RREADY(s00_axi_rready),
.data_in0 (data_in0 ),
.data_in1 (data_in1 ),
.data_in2 (data_in2 ),
.data_in3 (data_in3 ),
.data_out0 (data_out0 ),
.data_out1 (data_out1 ),
.data_out2 (data_out2 ),
.data_out3 (data_out3 ),
.data_out4 (data_out4 ),
.data_out5 (data_out5 )
);
endmodule
改完代码后,回到IP封装界面如图:

将自定义的参数(读写数据位宽、寄存器深度、读写基地址)拖到可视窗口界面,在BD中即可实时更改IP参数。

点击重新生成IP,退出IP编辑界面。按照下图所示,可以看到IP路径已经自动添加进工程,更新一下IP。

回到BD,点击加号,输入IP命名,在BD中添加上一步生成的IP。

双击IP ,可以看到刚刚添加到可视窗口的参数编辑界面。

三、仿真测试工程
在没有硬件的情况下,PL端单独进行仿真验证刚刚改过的从机IP,需要生成一个主机IP,与从机互联,在PL端进行仿真测试。还是一样的生成流程,选择主机模式。主机IP直接创建用于测试,不需要编辑。


主机测试IP创建完成后添加进BD。双击主机IP,设置测试的起始值为0xabcd0001,设置基地址与从机一致,均为0x43C00000,由于从机设置测试读6个写4个,但主机逻辑为读写数据量一致,故这里读写寄存器的值设为大值6。

按照图示连接,导出design_1_wrapper.v文件。

sim_tb.v如下:
module sim_tb(
);
reg clk ;
reg rst ;
reg test_en;
design_1_wrapper design_1_wrapper(
.data_in0_0 (32'haa550001),
.data_in1_0 (32'haa550002),
.data_in2_0 (32'haa550003),
.data_in3_0 (32'haa550004),
.data_out0_0 ( ),
.data_out1_0 ( ),
.data_out2_0 ( ),
.data_out3_0 ( ),
.data_out4_0 ( ),
.data_out5_0 ( ),
.m00_axi_aclk_0 (clk ),
.m00_axi_aresetn_0 (rst ),
.m00_axi_error_0 ( ),
.m00_axi_init_axi_txn_0 (test_en ),
.m00_axi_txn_done_0 ( )
);
initial begin
clk = 0;
rst = 0;
test_en = 0;
#1000
rst = 1;
#100
test_en = 1;
#10
test_en = 0;
end
always #5 clk =~clk;
endmodule
下图所示为从机仿真时序,为方便测试,主机IP生成后未修改,未修改的主机逻辑为读写数据量一致,因此可以看到读数据后两个值不变,这里仅供理解AXI4_lite协议的读写时序,若要主从读写数据量完全一致,可以按照前面的方法根据个人需求再次编辑主从IP。

四、在线测试工程
在线测试即为上板工程,删除仿真测试的主机IP,在BD中创建ZYNQ最小系统,定义地址为0x43C0_0000,按照图示连接,导出design_1_wrapper.v文件。


新建一个工程顶层top.v,编译生成bit文件,导出xsa文件,新建一个vitis的helloword空工程。
module top(
inout wire [14:0]DDR_0_addr,
inout wire [2:0]DDR_0_ba,
inout wire DDR_0_cas_n,
inout wire DDR_0_ck_n,
inout wire DDR_0_ck_p,
inout wire DDR_0_cke,
inout wire DDR_0_cs_n,
inout wire [3:0]DDR_0_dm,
inout wire [31:0]DDR_0_dq,
inout wire [3:0]DDR_0_dqs_n,
inout wire [3:0]DDR_0_dqs_p,
inout wire DDR_0_odt,
inout wire DDR_0_ras_n,
inout wire DDR_0_reset_n,
inout wire DDR_0_we_n,
inout wire FIXED_IO_0_ddr_vrn,
inout wire FIXED_IO_0_ddr_vrp,
inout wire [53:0]FIXED_IO_0_mio,
inout wire FIXED_IO_0_ps_clk,
inout wire FIXED_IO_0_ps_porb,
inout wire FIXED_IO_0_ps_srstb
);
wire [31:0] data_out0_0 ;
wire [31:0] data_out1_0 ;
wire [31:0] data_out2_0 ;
wire [31:0] data_out3_0 ;
wire [31:0] data_out4_0 ;
wire [31:0] data_out5_0 ;
wire ps_aclk ;
ila_0 ila_0 (
.clk (ps_aclk ), // input wire clk
.probe0(data_out0_0 ), // input wire [31:0] probe0
.probe1(data_out1_0 ), // input wire [31:0] probe1
.probe2(data_out2_0 ), // input wire [31:0] probe2
.probe3(data_out3_0 ), // input wire [31:0] probe3
.probe4(data_out4_0 ), // input wire [31:0] probe4
.probe5(data_out5_0 ) // input wire [31:0] probe5
);
design_1_wrapper design_1_wrapper(
.DDR_0_addr (DDR_0_addr ),
.DDR_0_ba (DDR_0_ba ),
.DDR_0_cas_n (DDR_0_cas_n ),
.DDR_0_ck_n (DDR_0_ck_n ),
.DDR_0_ck_p (DDR_0_ck_p ),
.DDR_0_cke (DDR_0_cke ),
.DDR_0_cs_n (DDR_0_cs_n ),
.DDR_0_dm (DDR_0_dm ),
.DDR_0_dq (DDR_0_dq ),
.DDR_0_dqs_n (DDR_0_dqs_n ),
.DDR_0_dqs_p (DDR_0_dqs_p ),
.DDR_0_odt (DDR_0_odt ),
.DDR_0_ras_n (DDR_0_ras_n ),
.DDR_0_reset_n (DDR_0_reset_n ),
.DDR_0_we_n (DDR_0_we_n ),
.FIXED_IO_0_ddr_vrn (FIXED_IO_0_ddr_vrn ),
.FIXED_IO_0_ddr_vrp (FIXED_IO_0_ddr_vrp ),
.FIXED_IO_0_mio (FIXED_IO_0_mio ),
.FIXED_IO_0_ps_clk (FIXED_IO_0_ps_clk ),
.FIXED_IO_0_ps_porb (FIXED_IO_0_ps_porb ),
.FIXED_IO_0_ps_srstb (FIXED_IO_0_ps_srstb),
.data_in0_0 (32'haa550001 ),
.data_in1_0 (32'haa550002 ),
.data_in2_0 (32'haa550003 ),
.data_in3_0 (32'haa550004 ),
.data_out0_0 (data_out0_0 ),
.data_out1_0 (data_out1_0 ),
.data_out2_0 (data_out2_0 ),
.data_out3_0 (data_out3_0 ),
.data_out4_0 (data_out4_0 ),
.data_out5_0 (data_out5_0 ),
.ps_aclk (ps_aclk ),
.ps_aresetn ( )
);
endmodule
vitis中helloword.c如下:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_io.h"
#define AXI_LITE_BASEADDR XPAR_AXI4_LITE_SLAVE_0_S00_AXI_BASEADDR
int main()
{
init_platform();
u32 test_in0 ;
u32 test_in1 ;
u32 test_in2 ;
u32 test_in3 ;
test_in0 = Xil_In32(AXI_LITE_BASEADDR);
test_in1 = Xil_In32(AXI_LITE_BASEADDR + 0x04);
test_in2 = Xil_In32(AXI_LITE_BASEADDR + 0x08);
test_in3 = Xil_In32(AXI_LITE_BASEADDR + 0x0c);
Xil_Out32(AXI_LITE_BASEADDR, 0xabcd0001 );
Xil_Out32(AXI_LITE_BASEADDR+4 , 0xabcd0002 );
Xil_Out32(AXI_LITE_BASEADDR+8 , 0xabcd0003 );
Xil_Out32(AXI_LITE_BASEADDR+12, 0xabcd0004 );
Xil_Out32(AXI_LITE_BASEADDR+16, 0xabcd0005 );
Xil_Out32(AXI_LITE_BASEADDR+20, 0xabcd0006 );
print("Hello World\n\r");
print("Successfully ran Hello World application");
cleanup_platform();
return 0;
}
板卡上电,运行程序,vitis中可以看到PL写给PS的四组数据与代码中设置的测试值一致。

vivado中可以看到PS写给PL的六组数据与代码中设置的测试值一致。

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)