【正点原子ZYNQ领航者7020】自定义IP核

文章目录

前言

无,仅作记录,不具有参考价值。

1.创建自定义IP核工程

这里的名字可以和图中不同 ,这里取的是breath_led_ip_test(因为之前已经有个我自己建立的breath_led_ipIP核,这里只是为了展示IP核的建立过程,verilog代码还是使用的是breath_led_ip_test的名字,vitis代码中用的是breath_led_ip的名字,后面会有一个使用breath_led_ipIP核的工程打包zip给你们下载,这里要关注的是建立过程)

在D:\ldy\VivadoIPs\custom_ip\ip_repo\breath_led_ip_test_1.0\hdl中添加breath_led.v文件,并导入到工程中

修改前的breath_led.v:

c 复制代码
module breath_led(
    input sys_clk,
    input sys_rst_n,
    output reg led
);
parameter CNT_2US_MAX = 100;
reg [6:0] cnt_2us;
reg [9:0] cnt_2ms;
reg [9:0] cnt_2s;
reg led_flag;


always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_2us <= 0;
    else if(cnt_2us < CNT_2US_MAX - 1)
        cnt_2us <= cnt_2us + 1;
    else
        cnt_2us <= 0;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_2ms <= 0;
    else if(cnt_2us == CNT_2US_MAX - 1 && cnt_2ms < 1000 - 1)
        cnt_2ms <= cnt_2ms + 1;
    else if(cnt_2us == CNT_2US_MAX - 1 && cnt_2ms == 1000 - 1)
        cnt_2ms <= 0;
    else
        cnt_2ms <= cnt_2ms;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_2s <= 0;
    else if(cnt_2us == CNT_2US_MAX - 1 && cnt_2ms == 1000 - 1 && cnt_2s < 1000 - 1)
        cnt_2s <= cnt_2s + 1;
    else if(cnt_2us == CNT_2US_MAX - 1 && cnt_2ms == 1000 - 1 && cnt_2s == 1000 - 1)
        cnt_2s <= 0;
    else
        cnt_2s <= cnt_2s;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        led_flag <= 1;
    else if(cnt_2us == CNT_2US_MAX - 1 && cnt_2ms == 1000 - 1 && cnt_2s == 1000 - 1)
        led_flag <= !led_flag;
    else
        led_flag <= led_flag;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        led <= 0;
    else if(led_flag) begin
        if(cnt_2ms <= cnt_2s)
            led <= 1;
        else
            led <= 0;
    end
    else begin
        if(cnt_2ms >= cnt_2s)
            led <= 1;
        else
            led <= 0;
    end
end

// ila_0 u_lia_0 (
// 	.clk(sys_clk), // input wire clk


// 	.probe0(sys_rst_n), // input wire [0:0]  probe0  
// 	.probe1(led), // input wire [0:0]  probe1 
// 	.probe2(cnt_2us), // input wire [6:0]  probe2 
// 	.probe3(cnt_2ms), // input wire [9:0]  probe3 
// 	.probe4(cnt_2s), // input wire [9:0]  probe4 
// 	.probe5(led_flag) // input wire [0:0]  probe5
// );


endmodule

然后开始修改breath_led.v:

在这里添加要引出的端口

添加参数的过程就是一层一层的向外扩展

修改后的breath_led.v:

c 复制代码
module breath_led(
    input sys_clk,
    input sys_rst_n,
    
    input sw_ctrl, //呼吸灯开关控制 1:打开呼吸灯 0:关闭呼吸灯
    input set_freq_en, //呼吸灯频率使能信号
    input [9:0] set_freq_step, //呼吸灯频率步进挡位1-1000
    
    output led
);

parameter START_FREQ_STEP = 10'd100;

reg [9:0] freq_step = 100;
reg [9:0] cnt_2us;
reg [9:0] cnt_2ms;
reg [9:0] cnt_2s;
reg led_flag; //亮度递增/递减的标志 0:递增 1:递减
reg led_t;

assign led = led_t & sw_ctrl;

//寄存set_freq_step的值,并将范围限定在1-1000
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        freq_step = START_FREQ_STEP;
    else if(set_freq_en) begin
        if(set_freq_step == 10'd0)
            freq_step <= 10'd1;
        else if(set_freq_step > 1000)
            freq_step <= 10'd1000;
        else
            freq_step <= set_freq_step;
    end
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_2us <= 0;
    else if(cnt_2us < freq_step - 1)
        cnt_2us <= cnt_2us + 1;
    else
        cnt_2us <= 0;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_2ms <= 0;
    else if(cnt_2us == freq_step - 1 && cnt_2ms < 1000 - 1)
        cnt_2ms <= cnt_2ms + 1;
    else if(cnt_2us == freq_step - 1 && cnt_2ms == 1000 - 1)
        cnt_2ms <= 0;
    else
        cnt_2ms <= cnt_2ms;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt_2s <= 0;
    else if(cnt_2us == freq_step - 1 && cnt_2ms == 1000 - 1 && cnt_2s < 1000 - 1)
        cnt_2s <= cnt_2s + 1;
    else if(cnt_2us == freq_step - 1 && cnt_2ms == 1000 - 1 && cnt_2s == 1000 - 1)
        cnt_2s <= 0;
    else
        cnt_2s <= cnt_2s;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        led_flag <= 1;
    else if(cnt_2us == freq_step - 1 && cnt_2ms == 1000 - 1 && cnt_2s == 1000 - 1)
        led_flag <= !led_flag;
    else
        led_flag <= led_flag;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        led_t <= 0;
    else if(led_flag) begin
        if(cnt_2ms <= cnt_2s)
            led_t <= 1;
        else
            led_t <= 0;
    end
    else begin
        if(cnt_2ms >= cnt_2s)
            led_t <= 1;
        else
            led_t <= 0;
    end
end

// ila_0 u_lia_0 (
// 	.clk(sys_clk), // input wire clk


// 	.probe0(sys_rst_n), // input wire [0:0]  probe0  
// 	.probe1(led), // input wire [0:0]  probe1 
// 	.probe2(cnt_2us), // input wire [6:0]  probe2 
// 	.probe3(cnt_2ms), // input wire [9:0]  probe3 
// 	.probe4(cnt_2s), // input wire [9:0]  probe4 
// 	.probe5(led_flag) // input wire [0:0]  probe5
// );


endmodule

修改完毕之后开始综合

按下之后vivado工程会自动关闭

接下来是最重要的一步!!!
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

在D:\ldy\VivadoIPs\custom_ip\ip_repo\breath_led_ip_1.0\drivers\breath_led_ip_v1_0\src(路径仅供参考)中替换Makefile若不替换,编译时会报错且很难补救,一定一定要确保更换确实完成了之后再进行下面步骤。

替换后的Makefile内部代码为:

c 复制代码
DRIVER_LIB_VERSION = 1.0
COMPILER=
ARCHIVER=
CP=cp
COMPILER_FLAGS=
EXTRA_COMPILER_FLAGS=
LIB=libxil.a

CC_FLAGS = $(COMPILER_FLAGS)
ECC_FLAGS = $(EXTRA_COMPILER_FLAGS)

RELEASEDIR=../../../lib/
INCLUDEDIR=../../../include/
INCLUDES=-I./. -I$(INCLUDEDIR)

SRCFILES:=$(wildcard *.c)

OBJECTS = $(addprefix $(RELEASEDIR), $(addsuffix .o, $(basename $(wildcard *.c))))

libs: $(OBJECTS)

DEPFILES := $(SRCFILES:%.c=$(RELEASEDIR)%.d)

include $(wildcard $(DEPFILES))

include $(wildcard ../../../../dep.mk)

$(RELEASEDIR)%.o: %.c
	${COMPILER} $(CC_FLAGS) $(ECC_FLAGS) $(INCLUDES) $(DEPENDENCY_FLAGS) $< -o $@

.PHONY: include
include: $(addprefix $(INCLUDEDIR),$(wildcard *.h))

$(INCLUDEDIR)%.h: %.h
	$(CP) $< $@

clean:
	rm -rf ${OBJECTS}
	rm -rf $(DEPFILES)

2.创建Vivado工程

创建工程,略

添加自定义的IP核,若不添加,block design中会找不到

选择ip_repo

选择刚刚或者之前创建的IP核

加入并配置zynq块(DDR3,uart,bank电压;过程略)

搜索并加入IP核

选中led引脚并引出

改名

创建pin.xdc文件

在pin.xdc中添加以下代码:

c 复制代码
#核心板
set_property -dict {PACKAGE_PIN J16 IOSTANDARD LVCMOS33} [get_ports pl_led]

3.Vitis软件设计

打开vitis并创建工程

过程略

在breath_led_test.h中可以找到操控寄存器函数

在breath_led_test_selftest.c中有待会会用到的头文件,选择这些头文件并复制

新建breath_led_test_ip_user文件夹,并创建左下红框处的文件

在main.c,main.h,breath_led_ip_user.c,breath_led_ip_user.h中添加以下代码:

main.c:

c 复制代码
#include "main.h"
#include "breath_led_ip_user.h"


int main(void)
{
	int freq_flag = 0; //定义频率的标志位,用户循环切换呼吸灯的频率
	int led_state;
	u32 data_in;
	xil_printf("* LED User IP Test\n\r");
	breath_led_set_freq(5);
	while(1)
	{
		if(freq_flag == 0)
		{
			breath_led_set_freq(50);
			freq_flag = 1;
		}
		else
		{
			breath_led_set_freq(500);
			freq_flag = 0;
		}
		//获取LED当前开关状态
		led_state = breath_led_readControlSw();
		if(led_state == 0)
		{
			//打开呼吸灯开关
			breath_led_control(1);
			xil_printf("* Breath LED ON *\n\r");
			sleep(8);
		}
		else
		{
			//关闭呼吸灯开关
			breath_led_control(0);
			xil_printf("* Breath LED OFF *\n\r");
			sleep(1);
		}
		data_in = breath_led_readReg2();
		xil_printf("data_in = %d\n\r", data_in);

	}

}

main.h:

c 复制代码
#ifndef __MAIN_H__
#define __MAIN_H__

#include "breath_led_ip.h"
#include "xparameters.h"
#include "stdio.h"
#include "xil_io.h"
#include "sleep.h"

#endif

breath_led_set_freq.c:

c 复制代码
#include "breath_led_ip_user.h"

void breath_led_control(u32 led_state)
{
	u32 reg0_state;
	reg0_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR, LED_IP_REG0);
	if(led_state)
	{
		BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG0, reg0_state | (u32)0x1);
	}
	else
	{
		BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG0, reg0_state & ~((u32)0x1));
	}
}

void breath_led_freq_set_EnableOrDisable(u32 sw)
{
	u32 reg1_state;
	reg1_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR, LED_IP_REG1);
	BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG1, (reg1_state & ~(1 << 31)) | (u32)(sw == 1));
}

void breath_led_set_freq(u32 freq)
{
	u32 reg1_state;
	reg1_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR, LED_IP_REG1);
	BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR, LED_IP_REG1, (reg1_state & ~(0x3ff)) | freq);
}

u32 breath_led_readControlSw(void)
{
	u32 led_state;
	led_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR, LED_IP_REG0) & 0x1;
	return led_state;
}

u32 breath_led_readReg2(void)
{
	u32 data_in;
	data_in = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR, LED_IP_REG2);
	return data_in;
}

breath_led_set_freq.h:

c 复制代码
#ifndef __BREATH_LED_IP_USER__
#define __BREATH_LED_IP_USER__

#include "main.h"

#define LED_IP_BASEADDR XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP基地址
#define LED_IP_REG0 BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //LED IP寄存器0的偏移地址
#define LED_IP_REG1 BREATH_LED_IP_S0_AXI_SLV_REG1_OFFSET //LED IP寄存器1的偏移地址
#define LED_IP_REG2 BREATH_LED_IP_S0_AXI_SLV_REG2_OFFSET //LED IP寄存器2的偏移地址

void breath_led_control(u32 led_state);
void breath_led_freq_set_EnableOrDisable(u32 sw);
void breath_led_set_freq(u32 freq);
u32 breath_led_readControlSw(void);
u32 breath_led_readReg2(void);

#endif

4.下载验证

略。

工程文件分享:

user_led.zip

使用时记得将ip_repo里面的IP核添加进vivado中

相关推荐
szxinmai主板定制专家17 小时前
ARM 还是 x86?CODESYS 数控机床控制器选型、FPGA 取舍与实时性真相
arm开发·人工智能·fpga开发·zynq·1024程序员节·codesys
XMAIPC_Robot7 天前
ARM+FPGA+AI船舶/工业自动化控制器EMC测试等级解析(IEC60533/IEC60945)
arm开发·人工智能·fpga开发·zynq·arm+fpga·rk3588+fpga
szxinmai主板定制专家7 天前
ZYNQ 数据采集方案踩坑总结:电力强电磁干扰、工业现场多协议适配优化
运维·人工智能·fpga开发·机器人·zynq
szxinmai主板定制专家7 天前
软硬协同!ZYNQ 高速多通道同步采集,变电站 / 工厂设备状态监测方案详解
人工智能·深度学习·机器学习·fpga开发·zynq·rk3576
szxinmai主板定制专家8 天前
告别 ARM 实时性短板:基于 ZYNQ 的电力故障录波 + 工业振动数据采集系统设计
arm开发·人工智能·fpga开发·zynq·数据采集卡·arm+fpga·rk3588+fpga
璞致电子11 天前
璞致电子PZSDR P151Q 大规模多通道软件无线电平台|ZU15EG + 四片 AD9361 8T8R 相位同步 SDR
xilinx·zynq·软件无线电·sdr·璞致电子·p151q·zu15eg
szxinmai主板定制专家13 天前
面向虚拟电厂协调控制:RK3588+FPGA+AI边缘控制器设计思路与算力评估
人工智能·嵌入式硬件·fpga开发·机器人·jetson·zynq·振动采集
szxinmai主板定制专家18 天前
基于Zynq的高精度多通道数据采集卡完整解决方案(工业振动/高速采集/边缘传输)
fpga开发·zynq·数据采集卡
吉他英雄一里20 天前
zynq入门教程
fpga开发·zynq
火花页.20 天前
【正点原子ZYNQ领航者7020】PS写ramPL读ram实验
zynq