一、实验介绍
用ZYNQ系统设计IP核,最常用的就是使用AXI总线将PS同PL部分的IP核连接起来,控制PL侧的某些寄存器,进而控制PL逻辑的执行。
最近学习了一个自定义IP核——PWM(脉冲宽度调制)的实验,实现一个呼吸灯。模块框图如下:
其中通过输入用户自定义的周期(period)和占空比(duty),实现输出脉冲宽度调制波形(pwm_wave)。模块代码如下:
`timescale 1ns / 1ps
module ax_pwm
#(
parameter N = 32 //pwm bit width
)
(
input clk,
input rst,
input[N - 1:0]period,
input[N - 1:0]duty,
output pwm_out
);
reg[N - 1:0] period_r;
reg[N - 1:0] duty_r;
reg[N - 1:0] period_cnt;
reg pwm_r;
assign pwm_out = pwm_r;
always@(posedge clk or posedge rst)
begin
if(rst==1)
begin
period_r <= { N {1'b0} };
duty_r <= { N {1'b0} };
end
else
begin
period_r <= period;
duty_r <= duty;
end
end
always@(posedge clk or posedge rst)
begin
if(rst==1)
period_cnt <= { N {1'b0} };
else
period_cnt <= period_cnt + period_r;
end
always@(posedge clk or posedge rst)
begin
if(rst==1)
begin
pwm_r <= 1'b0;
end
else
begin
if(period_cnt >= duty_r)
pwm_r <= 1'b1;
else
pwm_r <= 1'b0;
end
end
endmodule
因此period和duty这两个参数要让PS端通过AXI总线分别传给PL端的两个寄存器,再让PWM模块去读取这两个寄存器。如下图所示:
二、关键实验步骤
1、Vivado工程建立
(1)Tools -> Create and Package IP
(2)选择创建一个新的AXI4设备
(3)指定接口类型、寄存器数量等,这里我选择了AXI Lite Slave接口,4个寄存器。
(4)创建好IP之后,我们可以在IP Catalog中找到刚才创建的IP,但这时的IP只有简单的寄存器读写功能,我们需要修改IP,选择IP,右键 "Edit in IP Packager",进入该IP的工程
(5)添加PWM功能的核心代码(上文已给出) "Add Source" 并选择"Copy sources into IP Directory"
(6)修改ip顶层代码"ax_pwm_v1_0.v",添加一个pwm输出端口
(7)修改“ax_pwm_v1_0.v”,在例化“ax_pwm_V1_0_S00_AXI”中添加pwm端口的例化
(8) 修改“ax_pwm_v1_0_s00_AXI.v”文件,添加 pwm端口,这个文件是实现 AXI4 Lite Slave 的 核心代码
(9) 修改“ax_pwm_v1_0_s00_AXI.v”文件,例化pwm核心功能代码,将寄存器 slv_reg0和 slv_reg1 用于pwm模块的参数控制。
(10)在component.xml中的“File Groups”和“Customization Parameters”选项中点击“Merge changes ......”,之后Re-Package IP。
到此,自定义IP的创建与修改已结束。
(11)然后我们需要将自定义IP添加到工程的block design中,自动连线。再将pwm引脚Make External、Generate Output Products。
(12)添加管脚约束pwm.xdc,把pwm_0输出端口分配给PL侧的LED1,做一个呼吸灯。
set_property IOSTANDARD LVCMOS33 [get_ports pwm_0]
set_property PACKAGE_PIN M14 [get_ports pwm_0]
(13)Generate Bitstream,Export Hardware(勾选Include bitstream)
2、SDK软件编写调试
(1)在xparameter.h中可以知道自定义IP的寄存器基地址0x43C00000,在ax_pwm.h中可以了解到寄存器0的偏移值为0,寄存器1的偏移值为4。
(2)对这两个寄存器进行操作,配置pwm模块的输入频率,并通过改变占空比,实现PWM。
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "ax_pwm.h"
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"
int main()
{
init_platform();
int *period = (int *)0x43c00000;
int *duty = (int *)0x43c00004;
*period = 17179;
while (1)
{
for (*duty = 0x8fffffff; *duty < 0xffffffff; *duty = *duty + 100000)
{
usleep(100);
}
}
cleanup_platform();
return 0;
}
(3)运行代码,观察实验结果