[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK入门篇连载-25PL 自定义 AXI-Lite-PWM

文档创建者:FPGA课程
浏览次数:137
最后更新:2024-09-28
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 1-SDK基础入门方案
本帖最后由 FPGA课程 于 2024-9-28 13:43 编辑

​ 软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!



1概述
本课就以AXI-Lite总线实现PWM(Pulse Width Modulation,脉冲宽度调制)自定义IP作为验证AXI-Lite总线应用的方案,实现2路PWM,通过点亮LED观察效果,带领大家快速进入实战状态。
本文实验目的:
1:设计一个简单的PWM发生器,并且通过AXI-LITE-SLAVE寄存器实现频率调整、占空比调整
2:通过VITIS-SDK实现对自定义IP中寄存器的读写访问
2系统框图
f5b7947080f449c798cd68cde4857fc7.jpg
3创建IP
3.1利用模板创建AXI-Lite IP
1:打开VIVADO软件,新建一个工程
2:单击ToolsàCreate and Package NEW IP,单击Next。
3:由于需要挂在到总线上,因此创建一个带AXI总线的用户IP,选择Create a new AXI4 peripheral,单击Next。
81c2082e79484132b647a34543ef3910.jpg
4:输入要创建的IP名字,此处命名为PWM_LITE_ML,选择保存路径,单击Next。
74251f5b29894376a44ee5ec5b6adb04.jpg
5:NameàS00_AXI;
Interface Type(接口类型)àLite;
Data Width(Bits)(数据位宽)à32位;
Number of  Registers(寄存器数量)à8 ;单击next。
40825b069d7840128c3cac5f2b2a8e52.jpg
设置总线形式为Lite总线,Lite总线是简化的AXI总线,消耗的资源少,当然性能比完整版的AXI总线差一点。因为频率要求不高,因此采用Lite总线就够了。设置寄存器数量为8,因为后面我们需要用到8个寄存器。
Step6:选择Edit IP,单击Finish完成
693938fbee1d440ba9faae2991837efa.jpg
3.2修改IP源码
IP创建后,需要对其进行修改,建立我们能够实际使用的IP。
1:打开PWM_LITE_ML_v1_0.v文件。
1f738a3443cb4836b2891509efeb196c.jpg
修改如下:
1、添加PWM_o端口
87a93cfb4f9642b7bf73b4f81b90462a.jpg 修改为   789c8cc6a84941e48d0897c16cda7e16.jpg
2、添加端口
97ede4b451ca4f0aafde9ab6a548cc33.jpg ​修改为 8e67e695dbc6444495c8a888f1ff336b.jpg
2:打开PWM_LITE_ML_v1_0_S00_AXI.v文件。
73288a6d54c24386ae7f96da6c9b45d0.jpg
修改如下:
1、添加PWM_o端口(注意录制的视频教程接口PWM_o名字改为了PWM)
48a33f55263d4f77b8cd7122c752520f.jpg ​   修改为 186cac8334cb4214aa7b376973f47e85.jpg
2、添加PWM.v的端口
462f3ea1bca94def8c09d2d98c1ea065.jpg ​                    
修改为
       5440d1570dd245ffafa844cbee99e56d.jpg
说明:slv_reg0-slv_reg7为PS部分写入PL的寄存器。通过这8个寄存器的值,我们可以控制PWM的占空比。
下面这段代码就是PS写PL部分的寄存器,一共有8个寄存器。
  1. always @( posedge S_AXI_ACLK )
  2.         begin
  3.           if ( S_AXI_ARESETN == 1'b0 )
  4.             begin
  5.               slv_reg0 <= 0;
  6.               slv_reg1 <= 0;
  7.               slv_reg2 <= 0;
  8.               slv_reg3 <= 0;
  9.               slv_reg4 <= 0;
  10.               slv_reg5 <= 0;
  11.               slv_reg6 <= 0;
  12.               slv_reg7 <= 0;
  13.             end
  14.           else begin
  15.             if (slv_reg_wren)
  16.               begin
  17.                 case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
  18.                   3'h0:
  19.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  20.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  21.                         // Respective byte enables are asserted as per write strobes
  22.                         // Slave register 0
  23.                         slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  24.                       end  
  25.                   3'h1:
  26.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  27.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  28.                         // Respective byte enables are asserted as per write strobes
  29.                         // Slave register 1
  30.                         slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  31.                       end  
  32.                   3'h2:
  33.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  34.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  35.                         // Respective byte enables are asserted as per write strobes
  36.                         // Slave register 2
  37.                         slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  38.                       end  
  39.                   3'h3:
  40.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  41.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  42.                         // Respective byte enables are asserted as per write strobes
  43.                         // Slave register 3
  44.                         slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  45.                       end  
  46.                   3'h4:
  47.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  48.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  49.                         // Respective byte enables are asserted as per write strobes
  50.                         // Slave register 4
  51.                         slv_reg4[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  52.                       end  
  53.                   3'h5:
  54.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  55.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  56.                         // Respective byte enables are asserted as per write strobes
  57.                         // Slave register 5
  58.                         slv_reg5[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  59.                       end  
  60.                   3'h6:
  61.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  62.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  63.                         // Respective byte enables are asserted as per write strobes
  64.                         // Slave register 6
  65.                         slv_reg6[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  66.                       end  
  67.                   3'h7:
  68.                     for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
  69.                       if ( S_AXI_WSTRB[byte_index] == 1 ) begin
  70.                         // Respective byte enables are asserted as per write strobes
  71.                         // Slave register 7
  72.                         slv_reg7[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
  73.                       end  
  74.                   default : begin
  75.                               slv_reg0 <= slv_reg0;
  76.                               slv_reg1 <= slv_reg1;
  77.                               slv_reg2 <= slv_reg2;
  78.                               slv_reg3 <= slv_reg3;
  79.                               slv_reg4 <= slv_reg4;
  80.                               slv_reg5 <= slv_reg5;
  81.                               slv_reg6 <= slv_reg6;
  82.                               slv_reg7 <= slv_reg7;
  83.                             end
  84.                 endcase
  85.               end
  86.           end
  87.         end     
复制代码

     
3:新建一个PWM.v 文件实现PWM输出。
  1. module PWM(
  2. input clk,
  3. input rst_n,
  4. input [31:0]fre_set,
  5. input [31:0]wav_set,
  6. output  PWM_o
  7. );

  8. reg [31:0]fre_cnt;
  9. always @(posedge clk)begin
  10.         if(rst_n==1'b0)begin
  11.                 fre_cnt <=32'd0;
  12.         end
  13.         else begin
  14.                 if(fre_cnt<fre_set) begin
  15.                         fre_cnt <= fre_cnt+1'b1;
  16.                 end
  17.                 else begin
  18.                         fre_cnt<=32'd0;
  19.                 end
  20.         end
  21. end

  22. assign PWM_o = (wav_set>fre_cnt);

  23. endmodule
复制代码

4:进一步修改。去掉“_v1_0”。
1、PWM_LITE_ML_v1_0_S00_AXI.v中:
module PWM_LITE_ML_v1_0_S00_AXI #修改为à module PWM_LITE_ML_S00_AXI #
6af3ca40aa184df09f1bbdbc4ef243e5.jpg 修改后   8a87d01929d64f0aa3ad9efb9cea586b.jpg
2、PWM_LITE_ML_v1_0.v中:
module PWM_LITE_ML_v1_0 #     修改为à module PWM_LITE_ML #
63dcf64436b442d0890aaa5bc9e2ac49.jpg ​修改后    bd5efa2b750745f28c362cd7cb81bd0c.jpg
PWM_LITE_ML_v1_0_S00_AXI #  修改为à PWM_LITE_ML_S00_AXI #
PWM_LITE_ML_v1_0_S00_AXI_inst 修改为à PWM_LITE_ML_S00_AXI_inst
39ab945024424157a3038b9f9db3b9af.jpg ​ 修改后             78888b3d07a347a7961a43b4f7869145.jpg
5:修改后,保存。出现如下界面,选择Automatically pick new top module。
0b28ad77390248bb9dea326a118d55b6.jpg
6:重新封装。选择ToolsàCreat and Pakage New IP,单击Next。
7:选择Package your current project,单击Next。
b43e0fbb49644b53b7ef70236717781a.jpg
59007b87ca6047bd8100eec417eb18a9.jpg
8:选择Overwrite。
9:选择Package IPàRewiew and Package àRe-Package IP。完成创建自定义IP。
4硬件电路分析
这里PWM输出的IO引出到LED上,调整PWM的占空比可以实现LED的亮度调节。
f465c19427054c8c91a081c373050e64.jpg
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。
5搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。
5.1SOC系统工程
52b08bc101754378a0be240ca81bb054.jpg
1:中断设置
本实验可以不设置中断
3968547303e24060aaa6cd8a3449734e.jpg
2:设置GP Master接口
a2cc1bf05eda4057bfd252361bfef3bb.jpg
3:设置复位输出
aa023a5c705544eb99e1d7503f4414a8.jpg
4:设置PL时钟
2149272cce0440179e4c0288de585821.jpg
5:添加自定义IP
设置自定义IP路径,并且添加IP
691fc63de0214d0a8b201e674b1665d3.jpg
5.2设置AXI外设地址分配
只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏
f988fac17d96481594041bc4c851be37.jpg
5.3编译并导出平台文件
以下步骤简写,有不清楚的看第一篇文章。
1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。
2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。
3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件
4:生成Bit文件。
5:导出到硬件: FileàExport HardwareàInclude bitstream
6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
dc7e9f9a88d94d32b5d0fd4fa5ee95a8.jpg
6搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
6.1创建SDK Platform工程
2bb25d7879b84b80ad646734b6edbf08.jpg
右击soc_base编译,编译的时间可能会有点长
6.2创建APP工程
c8dfc11032a34842ae86a3caeb48bf3c.jpg
7程序分析
main函数中,根据之前章节的讲解我们可知,此处是分别向AXI总线的寄存器中写入数据。在23.2.2小节里,我们观察到,pwm_o[0]的频率设置和波形设置是通过slv_reg0和slv_reg1控制的。
e576d542679146d6baa593efa4b20bec.jpg
从程序可以知道:
PWM0为1:100的占空比,所以LED很暗
PWM1为98:100的占空比,所以LED很亮(实际由于驱动接口用了转换芯片,驱动能力没那么强)
PWM2为10:100占空比,这里只能用Ila观察
PWM3为40:100占空比,这里只能用Ila观察

c99b85a390874501a953b98c810c6686.jpg
由上图可知,我们写入的数据和实际的输出是完全一致的,验证了我们的想法.
8方案演示
8.1硬件准备
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON(注意新版本的 MLK_H3_CZ08-7100-MZ7100FC),支持 JTAG 模式,对于老版本的核心板,JTAG 调试的时候 一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)

bc98113868ba424caba9c81362237a16.jpg
8.2实验结果
6a79510618ca4239970d29d4ef763085.jpg
单击 54a0a213eebc4f9a801c0b67787615e9.jpg 编辑,启动触发。窗口显示采集到的信号波形。

0b31aacf8e7d4cc2939ead59f5452de1.jpg
通过控制不同的占空比也可以调整上的2个LED的显示亮度,这个用户自己可以试下。












您需要登录后才可以回帖 登录 | 立即注册

本版积分规则