自定义IP核

自定义LED IP核,控制PL LED呈现呼吸灯的效果,且PS可以通过AXI接口来控制呼吸灯的开关和呼吸的频率。

image-20230709192907640

IP核设计

下边是第三方设计好的呼吸灯Verilog代码,大概原理为调整占空比控制LED灯的呼吸效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// File name:           breath_led
// Descriptions: 自定义IP核-呼吸灯
//*******************************************************************************
module breath_led(
input sys_clk , //时钟信号
input sys_rst_n , //复位信号
input sw_ctrl , //呼吸灯开关控制信号 1:亮 0:灭
input set_en , //设置呼吸灯频率设置使能信号
input [9:0] set_freq_step , //设置呼吸灯频率变化步长

output led //LED
);
//** main code
//*****************************************************
//parameter define
parameter START_FREQ_STEP = 10'd100; //设置频率步长初始值
//reg define
reg [15:0] period_cnt ; //周期计数器
reg [9:0] freq_step ; //呼吸灯频率间隔步长
reg [15:0] duty_cycle ; //设置高电平占空比的计数点
reg inc_dec_flag; //用于表示高电平占空比的计数值,是递增还是递减
//为1时表示占空比递减,为0时表示占空比递增
//wire define
wire led_t ;
//将周期信号计数值与占空比计数值进行比较,以输出驱动led的PWM信号
assign led_t = ( period_cnt <= duty_cycle ) ? 1'b1 : 1'b0 ;
assign led = led_t & sw_ctrl;

//周期信号计数器在0-50_000之间计数
always @ (posedge sys_clk) begin
if (!sys_rst_n)
period_cnt <= 16'd0;
else if(!sw_ctrl)
period_cnt <= 16'd0;
else if( period_cnt == 16'd50_000 )
period_cnt <= 16'd0;
else
period_cnt <= period_cnt + 16'd1;
end

//设置频率间隔
always @(posedge sys_clk) begin
if(!sys_rst_n)
freq_step <= START_FREQ_STEP;
else if(set_en) begin
if(set_freq_step == 0)
freq_step <= 10'd1;
else if(set_freq_step >= 10'd1_000)
freq_step <= 10'd1_000;
else
freq_step <= set_freq_step;
end
end

//设定高电平占空比的计数值
always @(posedge sys_clk) begin
if (sys_rst_n == 1'b0) begin
duty_cycle <= 16'd0;
inc_dec_flag <= 1'b0;
end
else if(!sw_ctrl) begin //呼吸灯开关关闭时,信号清零
duty_cycle <= 16'd0;
inc_dec_flag <= 1'b0;
end
//每次计数完了一个周期,就调节占空比计数值
else if( period_cnt == 16'd50_000 ) begin
if( inc_dec_flag ) begin //占空比递减
if( duty_cycle == 16'd0 )
inc_dec_flag <= 1'b0;
else if(duty_cycle < freq_step)
duty_cycle <= 16'd0;
else
duty_cycle <= duty_cycle - freq_step;
end
else begin //占空比递增
if( duty_cycle >= 16'd50_000 )
inc_dec_flag <= 1'b1;
else
duty_cycle <= duty_cycle + freq_step;
end
end
else //未计数完一个周期时,占空比保持不变
duty_cycle <= duty_cycle ;
end
endmodule

==上述verilog代码实现的呼吸灯涉及的输入输出和参数如下:==
Input:
sys_clk , //时钟信号
sys_rst_n , //复位信号
sw_ctrl , //呼吸灯开关控制信号 1:亮 0:灭
set_en , //设置呼吸灯频率设置使能信号
set_freq_step , //设置呼吸灯频率变化步长
Output:
led //LED
Parameters:
parameter START_FREQ_STEP = 10’d100; //设置频率步长初始值

1 新建IP

打开Vivado 选择manage IP,选择保存IP的路径

tools—create and package new IP

配置IP名称,接口等信息

2023-07-10 17-03-49 的屏幕截图

2023-07-10 17-05-46 的屏幕截图

IP Catalog— user Reposity—AXI Peripheral —自定义IP—右击—edit in IP packager

2023-07-10 17-09-56 的屏幕截图

2 修改代码

vivado自动创建.v实例化了AXI4,里边可以找到定义的寄存器

2023-07-10 18-44-23 的屏幕截图

右键breath_led_IP_v1_0,add Sources,新建一个.v文件准备实现自定义IP核逻辑。

添加呼吸灯模块代码后,需要在breath_led_IP_v1_0_S01_AXI_inst中例化呼吸灯IP核,并添加参数。

2023-07-10 18-44-23

2023-07-10 18-56-15 的屏幕截图

完成后在breath_led_IP_v1_0中例化breath_led_IP_v1_0_S01_AXI,添加参数。
定义led 和 申明START_FREQ_STEP,以及后续例化中补充接口和参数

2023-07-10 19-02-40 的屏幕截图

点击Run Sythesized对模块综合,检查代码是否错误

3 打包IP核

回到Package IP界面

compatibility 添加支持的开发板、修改Customization Parameters定制参数

2023-07-10 19-10-38 的屏幕截图

Re-Package IP,自此,之前创建的IP核打包完成

vivado硬件设计

创建一个工程

在Setting—IP—Repository中添加之前创建的IP项目,添加完成后在Ip Catalog中也可以右键修改

2023-07-10 19-18-58 的屏幕截图

2023-07-10 19-20-17 的屏幕截图

添加Zynq处理器以及自定义IP核,验证连接没有出错后

在Design Sources中右键system,Generate output product,然后点击Create HDL Wrapper

2023-07-10 19-48-20 的屏幕截图

验证无误后,生成bitstream文件

导出包含bitstream的xsa文件

vitis软件设计

三个不同的文件路径都可以看到IP核对应代码:

export/CustomIP_led/hw/drivers/breath_led_ip_v1_0

hw/drivers/breath_led_ip_v1_0

psu_cortexa53_0/standalone_domain/bsp/psu_cortexa53_0/libsrc/breath_led_ip_v1_0

1 利用导出的xsa新建平台

2 新建空项目,创建main.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "xparameters.h" //定义了IP核的基地址
#include "breath_led_IP.h" //定义了读写IP核的函数接口,本质是重新定义Xil_out()
#include "xil_io.h"
#include "stdio.h"

//对参数进行宏定义,降低阅读复杂度
#define LED_IP_BASEADDR XPAR_BREATH_LED_IP_0_S01_AXI_BASEADDR
#define LED_IP_REG0 BREATH_LED_IP_S01_AXI_SLV_REG0_OFFSET
#define LED_IP_REG1 BREATH_LED_IP_S01_AXI_SLV_REG1_OFFSET

int main(){
//打开呼吸灯开关
printf("led user ip test!\n");
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,0x800003e8);//配置频率
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG0,0x00000001);//打开LED
//sleep(5);
//BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG0,0x00000000);
return 0;
}

修改IP核:

修改.v文件

File Group、Review and Package、Run Sythesized检查错误、Re-Package重新封装IP

Show IP Status、Upgrade Selected、在对话框中选择Generate output Products

由于这里只是测试一下修改IP核从LED寄存器中读取数据,所以就不需要修改顶层设计了

直接生成bitstream文件

导出硬件xsa,包含bitstream文件,覆盖老版本xsa文件

回到vitis,platform,右键,update Hardware Specification