【正点原子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主板定制专家6 天前
基于 RK3588+RK1820/28 算力卡的国产工控机,适用于机器视觉,机器人等边缘算力场景
人工智能·fpga开发·zynq·控制器·mpsoc·半导体设备
努力搬砖的小H10 天前
FPGA应用技巧之Block Design基本使用方法
功能测试·fpga开发·zynq·block design
szxinmai主板定制专家1 个月前
基于 RK3588 + Xilinx Kintex-7 FPGA 异构工业主控板设计方案
arm开发·人工智能·嵌入式硬件·fpga开发·zynq
szxinmai主板定制专家1 个月前
Zynq MPSoC实现半导体真空腔体状态监测|FPGA多传感器时序同步采集方案
fpga开发·zynq·控制器·mpsoc·半导体设备·fpga同步
szxinmai主板定制专家1 个月前
Zynq MPSoC半导体高精度运动控制方案|FPGA硬件插补实现亚微米级伺服实时控制
人工智能·嵌入式硬件·fpga开发·zynq·运动控制·ethercat
燕卫博1 个月前
借助AI的力量,将 ZYNQ 开发板改造成一个虚拟专用网络网关
人工智能·串口·嵌入式·zynq·reasonix·ser2mcp
szxinmai主板定制专家1 个月前
基于Zynq MPSoC的多路GMSL相机同步采集与低延迟图像预处理系统|车载视觉高速解决方案
人工智能·zynq·gmsl·rk3588+fpga·rk3576+fpga
szxinmai主板定制专家1 个月前
ZYNQ高速数据采集卡设计:1M采样率实现IEPE传感器信号采集
人工智能·数据采集·zynq·rk3588+fpga·rk3576+fpga·振动采集
Echo_cy_2 个月前
基于ZYNQ-7000的Ethernet驱动配置
linux·驱动开发·嵌入式硬件·fpga开发·ethernet·zynq