• 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:

    1. `timescale 1ns / 1ps
    2. module axilite_slave #(
    3. parameter USER_WR_DATA_WIDTH = 32 , //用户写数据位宽和AXI4―Lite数据位宽保持一致
    4. parameter USER_RD_DATA_WIDTH = 32 , //用户读数据位宽和AXI4―Lite数据位宽保持一致
    5. parameter AXI_DATA_WIDTH = 32, //AXI4_LITE总线规定,数据位宽只支持32Bit或者64bit
    6. parameter AXI_ADDR_WIDTH = 32
    7. )(
    8. input axi_clk,
    9. input reset,
    10. output reg s_wr_vld,
    11. output reg [USER_WR_DATA_WIDTH-1:0] s_wr_data,
    12. output reg [AXI_ADDR_WIDTH-1 :0] s_wr_addr,
    13. output reg s_rd_addr_vld,
    14. output reg [AXI_ADDR_WIDTH-1 :0] s_rd_addr,
    15. input [USER_RD_DATA_WIDTH-1:0] s_rd_data,
    16. input s_rd_data_vld,
    17. input [AXI_ADDR_WIDTH -1:0] s_axi_awaddr, //axi write address channel
    18. input [2:0] s_axi_awprot,
    19. input s_axi_awvalid,
    20. output reg s_axi_awready,
    21. input [AXI_DATA_WIDTH-1:0] s_axi_wdata, //axi write data channel
    22. input [AXI_DATA_WIDTH/8-1:0] s_axi_wstrb,
    23. input s_axi_wvalid,
    24. output reg s_axi_wready,
    25. output [1:0] s_axi_bresp, //axi wirte response channel
    26. output reg s_axi_bvalid,
    27. input s_axi_bready,
    28. input s_axi_arvalid, // axi read address channel
    29. output reg s_axi_arready,
    30. input [AXI_ADDR_WIDTH-1:0] s_axi_araddr,
    31. input [2:0] s_axi_arprot,
    32. output reg [AXI_DATA_WIDTH-1:0] s_axi_rdata, // axi read data channel
    33. output [1:0] s_axi_rresp,
    34. output reg s_axi_rvalid,
    35. input s_axi_rready
    36. );
    37. (* dont_touch="true" *) reg a_reset_sync_d0;
    38. (* dont_touch="true" *) reg a_reset_sync_d1;
    39. (* dont_touch="true" *) reg a_reset_sync;
    40. /*------------------------------------------*\
    41. 状态机信号定义
    42. \*------------------------------------------*/
    43. reg [1:0] wr_cur_status;
    44. reg [1:0] wr_nxt_status;
    45. reg [1:0] rd_cur_status;
    46. reg [1:0] rd_nxt_status;
    47. localparam WR_IDLE = 3'b000;
    48. localparam WE_DATA = 3'b001;
    49. localparam WR_BRESP = 3'b010;
    50. localparam RD_IDLE = 3'b000;
    51. localparam RD_PRE = 3'b001;
    52. localparam RD_DATA = 3'b010;
    53. /*------------------------------------------*\
    54. assign
    55. \*------------------------------------------*/
    56. assign s_axi_bresp = 0;
    57. assign s_axi_rresp = 0;
    58. /*------------------------------------------*\
    59. CDC
    60. \*------------------------------------------*/
    61. always @(posedge axi_clk) begin
    62. a_reset_sync_d0 <= reset;
    63. a_reset_sync_d1 <= a_reset_sync_d0;
    64. a_reset_sync <= a_reset_sync_d1;
    65. end
    66. /*------------------------------------------*\
    67. AXILITE从机写过程
    68. \*------------------------------------------*/
    69. always @(posedge axi_clk) begin
    70. if (a_reset_sync)
    71. wr_cur_status <= WR_IDLE;
    72. else
    73. wr_cur_status <= wr_nxt_status;
    74. end
    75. always @(*) begin
    76. if (a_reset_sync)
    77. wr_nxt_status <= WR_IDLE;
    78. else
    79. case(wr_cur_status)
    80. WR_IDLE : begin
    81. if (s_axi_awvalid && s_axi_wvalid)
    82. wr_nxt_status <= WE_DATA;
    83. else
    84. wr_nxt_status <= wr_cur_status;
    85. end
    86. WE_DATA : begin
    87. wr_nxt_status <= WR_BRESP;
    88. end
    89. WR_BRESP : begin
    90. if (s_axi_bvalid && s_axi_bready)
    91. wr_nxt_status <= WR_IDLE;
    92. else
    93. wr_nxt_status <= wr_cur_status;
    94. end
    95. default : wr_nxt_status <= WR_IDLE;
    96. endcase
    97. end
    98. always @(*) begin
    99. if (a_reset_sync) begin
    100. s_axi_awready <= 0;
    101. s_axi_wready <= 0;
    102. end
    103. else begin
    104. s_axi_awready <= wr_cur_status == WE_DATA;
    105. s_axi_wready <= wr_cur_status == WE_DATA;
    106. end
    107. end
    108. always @(posedge axi_clk) begin
    109. if (a_reset_sync)
    110. s_axi_bvalid <= 0;
    111. else if (s_axi_bvalid && s_axi_bready)
    112. s_axi_bvalid <= 0;
    113. else if (wr_cur_status == WR_BRESP)
    114. s_axi_bvalid <= 1'b1;
    115. else
    116. s_axi_bvalid <= s_axi_bvalid;
    117. end
    118. always @(posedge axi_clk) begin
    119. if (wr_cur_status == WE_DATA) begin
    120. s_wr_vld <= 1'b1;
    121. s_wr_data <= s_axi_wdata;
    122. s_wr_addr <= s_axi_awaddr;
    123. end
    124. else begin
    125. s_wr_vld <= 0;
    126. s_wr_data <= s_wr_data;
    127. s_wr_addr <= s_wr_addr;
    128. end
    129. end
    130. /*------------------------------------------*\
    131. AXILITE从机读过程
    132. \*------------------------------------------*/
    133. always @(posedge axi_clk) begin
    134. if (a_reset_sync)
    135. rd_cur_status <= RD_IDLE;
    136. else
    137. rd_cur_status <= rd_nxt_status;
    138. end
    139. always @(*) begin
    140. if (a_reset_sync)
    141. rd_nxt_status <= RD_IDLE;
    142. else
    143. case(rd_cur_status)
    144. RD_IDLE : begin
    145. if (s_axi_arvalid)
    146. rd_nxt_status <= RD_PRE;
    147. else
    148. rd_nxt_status <= rd_cur_status;
    149. end
    150. RD_PRE : begin
    151. rd_nxt_status <= RD_DATA;
    152. end
    153. RD_DATA : begin
    154. if (s_axi_rvalid && s_axi_rready)
    155. rd_nxt_status <= RD_IDLE;
    156. else
    157. rd_nxt_status <= rd_cur_status;
    158. end
    159. default : rd_nxt_status <= RD_IDLE;
    160. endcase
    161. end
    162. always @(*) begin
    163. if (a_reset_sync)
    164. s_axi_arready <= 0;
    165. else
    166. s_axi_arready <= rd_cur_status == RD_PRE;
    167. end
    168. always @(posedge axi_clk) begin
    169. if (rd_cur_status == RD_PRE) begin
    170. s_rd_addr_vld <= 1'b1;
    171. s_rd_addr <= s_axi_araddr;
    172. end
    173. else begin
    174. s_rd_addr_vld <= 0;
    175. s_rd_addr <= s_rd_addr;
    176. end
    177. end
    178. always @(posedge axi_clk) begin
    179. if (a_reset_sync) begin
    180. s_axi_rdata <= 0;
    181. s_axi_rvalid <= 0;
    182. end
    183. else if (s_axi_rvalid && s_axi_rready) begin
    184. s_axi_rvalid <= 0;
    185. end
    186. else if (s_rd_data_vld) begin
    187. s_axi_rvalid <= 1'b1;
    188. s_axi_rdata <= s_rd_data;
    189. end
    190. else begin
    191. s_axi_rvalid <= s_axi_rvalid;
    192. s_axi_rdata <= s_axi_rdata;
    193. end
    194. end
    195. endmodule

    user_ram

    1. `timescale 1ns / 1ps
    2. module user_ram #(
    3. parameter USER_WR_DATA_WIDTH = 32 ,
    4. parameter USER_RD_DATA_WIDTH = 32 ,
    5. parameter AXI_DATA_WIDTH = 32 , //注意AXI4的数据位宽只有32Bit或者64bit
    6. parameter AXI_ADDR_WIDTH = 32
    7. )(
    8. input clk ,
    9. input reset ,
    10. input s_wr_vld ,
    11. input [USER_WR_DATA_WIDTH-1:0] s_wr_data ,
    12. input [AXI_ADDR_WIDTH-1 :0] s_wr_addr ,
    13. input s_rd_addr_vld,
    14. input [AXI_ADDR_WIDTH-1 :0] s_rd_addr ,
    15. output reg [USER_RD_DATA_WIDTH-1:0] s_rd_data ,
    16. output reg s_rd_data_vld
    17. );
    18. localparam SIZE = 1024;
    19. reg [AXI_DATA_WIDTH-1:0] ram [SIZE - 1 : 0] ;
    20. always @(posedge clk) begin
    21. if (s_wr_vld)
    22. ram[s_wr_addr] <= s_wr_data;
    23. end
    24. always @(posedge clk) begin
    25. if (reset) begin
    26. s_rd_data_vld <= 0;
    27. s_rd_data <= 0;
    28. end
    29. else if (s_rd_addr_vld) begin
    30. s_rd_data_vld <= 1'b1;
    31. s_rd_data <= ram[s_rd_addr];
    32. end
    33. else begin
    34. s_rd_data_vld <= 0;
    35. s_rd_data <= 0;
    36. end
    37. end
    38. 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交互icon-default.png?t=N7T8https://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读写:

    1. #include "xparameters.h"
    2. #include "sleep.h"
    3. #include "xil_io.h"
    4. #define AXI_LITE_BASEADDR 0x40000000
    5. 通过函数编写
    6. int main()
    7. {
    8. u32 rddata;
    9. Xil_Out32(AXI_LITE_BASEADDR,1000);
    10. Xil_Out32(AXI_LITE_BASEADDR + 4,500);
    11. Xil_Out32(AXI_LITE_BASEADDR + 8,800);
    12. rddata = Xil_In32(AXI_LITE_BASEADDR);
    13. rddata = Xil_In32(AXI_LITE_BASEADDR + 4);
    14. return 0;
    15. }

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

    1. #include "xparameters.h"
    2. #include "sleep.h"
    3. #include "xil_io.h"
    4. #define AXI_LITE_BASEADDR 0x40000000
    5. int main()
    6. {
    7. u32* LitePtr = (u32*)AXI_LITE_BASEADDR; //强制转换 转为地址
    8. u32 wrdata = 0;
    9. u32 rddata = 0;
    10. int i = 0;
    11. //向PL写数据
    12. for (i = 0; i < 128; i++ )
    13. {
    14. *LitePtr++ = wrdata++;
    15. }
    16. LitePtr = (u32*)AXI_LITE_BASEADDR;
    17. for (i = 0; i < 128; i++ )
    18. {
    19. rddata = *LitePtr++;
    20. printf("rddata= %d \n",rddata);
    21. }
    22. return 0;
    23. }

    最后,下载验证。

    总结

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

  • 相关阅读:
    2023 最新 PDF.js 在 Vue3 中的使用(长期更新)
    十三、Mysql的存储引擎
    java stream中的peek()用法
    openEuler 服务器安装 JumpServer (all-in-one 模式)
    初识SDN(二)
    【JavaScript复习七】内置对象string截取字符串及其他方法
    Spring Boot @Value读不到Nacos配置中心的值。(properties配置文件)
    [.NET6]使用ML.NET+ONNX预训练模型整活B站经典《华强买瓜》
    jquery和jquery-ui拖动元素(vue2)
    CardView设置任意角为圆角
  • 原文地址:https://blog.csdn.net/weixin_46897065/article/details/137937509