S02_CH13_ AXI_PWM 实验
当学习了上一章的协议介绍内容后,开发基于这些协议的方案已经不是什么难事了,关键的一点就是从零到有的突破了。本章就以AXI-Lite总线实现8路LED自定义IP作为第一验证AXI-Lite总线应用的方案,带领大家快速进入实战状态。
13.1 自定义IP的封装
Step1:新建一个名为Miz_sys空的工程。
Step2:选择Tools Create and Package IP 创建IP
Step3:单击NEXT
Step4:由于我们需要挂在到总线上,因此创建一个带AXI总线的用户IP
Step5:设置IP的名字为PWM_LITE_ML版本号默认,并且记住IP的位置
Step6:设置总线形式为Lite总线,Lite总线是简化的AXI总线消耗的资源少,当然性能也是比完全版的AXI总线差一点,但是由于音频的速度并不高,因此采用Lite总线就够了,设置寄存器数量为16,因为后面我们需要用到16个寄存器。
Step7:选择Edit IP单击Finish完成
13.2 miz702_pwm用户IP的修改
IP创建完成后,并不能立马使用,还需要做一些修改。
Step1:打开PWM_AXI_ML.v文件在以下位置修改:
Step2:修改PWM_AXI_ML_v1_0_S00_AXI.v的端口部分
Step3:slv_reg0-slv_reg5为PS部分写入PL的寄存器。通过这个16个寄存器的值,我们可以控制PWM的占空比。
Step3:下面这段代码就是PS写PL部分的寄存器,一共有16个寄存器
always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin slv_reg0 <= 0; slv_reg1 <= 0; slv_reg2 <= 0; slv_reg3 <= 0; slv_reg4 <= 0; slv_reg5 <= 0; slv_reg6 <= 0; slv_reg7 <= 0; slv_reg8 <= 0; slv_reg9 <= 0; slv_reg10 <= 0; slv_reg11 <= 0; slv_reg12 <= 0; slv_reg13 <= 0; slv_reg14 <= 0; slv_reg15 <= 0; end else begin if (slv_reg_wren) begin case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 4'h0: 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 // Respective byte enables are asserted as per write strobes // Slave register 0 slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h1: 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 // Respective byte enables are asserted as per write strobes // Slave register 1 slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h2: 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 // Respective byte enables are asserted as per write strobes // Slave register 2 slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h3: 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 // Respective byte enables are asserted as per write strobes // Slave register 3 slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h4: 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 // Respective byte enables are asserted as per write strobes // Slave register 4 slv_reg4[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h5: 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 // Respective byte enables are asserted as per write strobes // Slave register 5 slv_reg5[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h6: 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 // Respective byte enables are asserted as per write strobes // Slave register 6 slv_reg6[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h7: 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 // Respective byte enables are asserted as per write strobes // Slave register 7 slv_reg7[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h8: 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 // Respective byte enables are asserted as per write strobes // Slave register 8 slv_reg8[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'h9: 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 // Respective byte enables are asserted as per write strobes // Slave register 9 slv_reg9[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'hA: 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 // Respective byte enables are asserted as per write strobes // Slave register 10 slv_reg10[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'hB: 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 // Respective byte enables are asserted as per write strobes // Slave register 11 slv_reg11[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'hC: 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 // Respective byte enables are asserted as per write strobes // Slave register 12 slv_reg12[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'hD: 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 // Respective byte enables are asserted as per write strobes // Slave register 13 slv_reg13[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'hE: 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 // Respective byte enables are asserted as per write strobes // Slave register 14 slv_reg14[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 4'hF: 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 // Respective byte enables are asserted as per write strobes // Slave register 15 slv_reg15[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end default : begin slv_reg0 <= slv_reg0; slv_reg1 <= slv_reg1; slv_reg2 <= slv_reg2; slv_reg3 <= slv_reg3; slv_reg4 <= slv_reg4; slv_reg5 <= slv_reg5; slv_reg6 <= slv_reg6; slv_reg7 <= slv_reg7; slv_reg8 <= slv_reg8; slv_reg9 <= slv_reg9; slv_reg10 <= slv_reg10; slv_reg11 <= slv_reg11; slv_reg12 <= slv_reg12; slv_reg13 <= slv_reg13; slv_reg14 <= slv_reg14; slv_reg15 <= slv_reg15; end endcase end end end |
Step4:新建一个PWM.v 文件实现PWM输出。
module PWM( input clk, input rst_n, input [31:0]fre_set, input [31:0]wav_set, output PWM_o ); reg [31:0]fre_cnt; always @(posedge clk)begin if(rst_n==1'b0)begin fre_cnt <=32'd0; end else begin if(fre_cnt<fre_set) begin fre_cnt <= fre_cnt+1'b1; end else begin fre_cnt<=32'd0; end end end assign PWM_o = (wav_set>fre_cnt); endmodule |
Step5:修改完成后重新封装一次自定义IP
Step6:单击NEXT
Step7:和第一次不同,这次选择第一个单选框然后单击NEXT
Step8:选择第一个单选框,然后单击NEXT
Step9:点击Overwrite
Step10:点击Finish 到此自定义IP结束
13.3 搭建硬件工程
Step1:另外新建一个VIVADO工程,根据自己的开发板正确配置芯片型号。
Step2:在Project manager区中单击Project settings。
Step3:选择IP设置区中的repository manager,将上一节我们封装好的IP的路劲添加进去。
Step:4:单击+号图标,将上一节封装的IP的路劲存放进去,单击OK。
Step5:新建一个BD文件,输入文件名,完成创建。
Step6:向BD文件中添加一个ZYNQ Processing system,根据自身硬件完成IP的配置。
Step7:单击添加IP图标,输入上一节我们自定义IP的模块名,将其添加入BD文件中。
Step8:直接点击Run connection automation,然后单击OK。
Step9:选中PWM_o,按Ctrl+T组合键引出端口。
Step10:单击IP icon 添加 ila CORE
Step11:双击打开ILA CORE
Step12:双击打开ILA CORE
General Options设置如下
Probe_Ports设置如下,之后单击OK
Step13:连接Probe0到PWM_o。
Step14:连接CLK接口到FCLK_CLK0接口。
Step15:右键单击Block文件,文件选择Generate the Output Products。
Step16:右键单击Block文件,选择Create a HDL wrapper,根据Block文件内容产生一个HDL 的顶层文件,并选择让vivado自动完成。
Step17:添加一个约束文件,打开对应自己硬件的原理图,查看LED部分引脚连接情况,此次我们只用4个LED完成实验。Miz702约束文件如下所示:
set_property SEVERITY {Warning} [get_drc_checks NSTD-1] set_property SEVERITY {Warning} [get_drc_checks UCIO-1] set_property PACKAGE_PIN T22 [get_ports {PWM_o[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {PWM_o[0]}] set_property PACKAGE_PIN T21 [get_ports {PWM_o[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {PWM_o[1]}] set_property PACKAGE_PIN U22 [get_ports {PWM_o[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {PWM_o[2]}] set_property PACKAGE_PIN U21 [get_ports {PWM_o[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {PWM_o[3]}] |
其他型号开发板参照对应型号的原理图的LED部分,修改成对应的引脚即可。
Step11:生成bit文件。
13.4 加载到SDK
Step1:导出硬件。
Step2:新建一个空SDK工程,并添加一个main.c的文件。
Step3:在main.c文件中添加以下程序,按Ctrl+S保存后自动开始编译。
#include <stdio.h> #include "xparameters.h" #include "xil_io.h" #include "sleep.h" #include "xil_types.h" int main() { Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR,99);//pwm0 fre Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR+4,10);//pwm0 wav Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR+8,99);//pwm1 fre Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR+12,20);//pwm1 wav Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR+16,99);//pwm2 fre Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR+20,40);//pwm2 wav Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR+24,99);//pwm3 fre Xil_Out32(XPAR_PWM_AXI_ML_0_BASEADDR+28,80);//pwm3 wav return 0; } |
Step4:右击工程,选择Debug as ->Debug configuration。
Step5:选中system Debugger,双击创建一个系统调试。
Step6:设置系统调试。
Step7:单击运行程序按钮运行程序。
Step8:回到VIVADO单击Open Target->Auto Connect
Step9:加载完成后的界面
Step10:单击箭头所指向启动触发,窗口显示采集到的信号波形。
13.5 程序分析
Main函数中,根据之前章节的讲解我们可知,此处是分别向AXI总线的寄存器中写入数据。在13.2小节里,我们观察到,pwm_o[0]的频率设置和波形设置是通过slv_reg0和slv_reg1控制的。
由程序可知,程序中设置的pwm_o[0]的频率和波形频率分别为99和10,我们在ila中实际测量一下看看波形是否正确(将黄色测量线拖放到某一点,然后点击,可设立一个参考点)。
由上图可知,我们写入的数据和实际的输出是完全一致的,验证了我们的想法。
13.6 本章小结
本章实现了第一个实现具体功能的8路PWM,通过点亮LED可以看到效果。这个简单的工程充分体现了SOC的优势。CPU无需参与就可以让8路PWM持续输出,这个输出是有PL控制的