文章目录
【1】创建一个新的IP核
- 1.1 打开Vivado->点击【Tasks->Manage IP->New IP Location】->弹出窗口
- 1.2 【Next】->设置IP属性->【Finish】->【OK】
- 【IP Location】:E:/work/fpga/custom_ip
- 【Part】为开发板型号,可重新指定
- 1.3【菜单栏/Tools/Create and Package New IP...】->【Next】->选中【Create a new AXI4 Periperal】->【Next】->设置IP核细节,只需改下名->【Next】
- 我的IP核【Name】breath_led_ip
- 1.4 设置AXI接口【Next】->【Finish】
- 将【Name】改为S0_AXI
- 1.3 右键【User Repository->AXI Peripheral->breath_led_ip_v1.0】->【Edit in IP Packager】->【OK】
至此,会打开一个新的工程。
【Add Interfaces】窗口中一些参数的解释。
- 【Interface Tpye】接口类型
- 【Lite】简化版的 AXI4 接口,用于较少数据量的存储映射通信
- 【Full】高性能存储映射接口,用于较多数据量的存储映射通信
- 【Stream】用于高速数据流传输,非存储映射接口。
- 【Interface Mode】接口模式):有 Slave(从机)和 Master(主机)两种模式可选
【2】实现功能
这步比较轻车熟路了,右键【Design Sources】->【Add Sources】->【Add or create design sources】->【Next】->【Create Files】->填入文件名和路径->【Finish】->【OK】->【Yes】
- 【File name】breath_led
- 【File locaion】 项目路径\ip_repo\breath_led_ip_1.0\hdl
打开【breath_led.v】文件,编辑代码如下。
v
module breath_led(
input sys_clk , //系统时钟 50MHz
input sys_rst_n , //系统复位,低电平有效
input sw_ctrl , //呼吸灯开关控制信号 1:亮 0:灭
input set_en , //设置呼吸灯频率设置使能信号
input [9:0] set_freq_step , //设置呼吸灯频率变化步长
output led //LED灯
);
//parameter define
parameter START_FREQ_STEP = 10'd1; //设置频率步长初始值
parameter CNT_2US_MAX = 7'd100;
parameter CNT_2MS_MAX = 10'd1000;
parameter CNT_2S_MAX = 10'd1000;
//reg define
reg [6:0] cnt_2us;
reg [9:0] cnt_2ms;
reg [9:0] cnt_2s;
reg inc_dec_flag; //亮度递增/递减 0:递增 1:递减
reg [9:0] freq_step ; //呼吸灯频率间隔步长
reg led_t ;
assign led = led_t & sw_ctrl;
//设置频率间隔,频率步长值在1-10之间
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'd10)
freq_step <= 10'd10;
else
freq_step <= set_freq_step;
end
end
//cnt_2us:计数2us
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2us <= 7'b0;
else if(cnt_2us == (CNT_2US_MAX - 7'b1 ))
cnt_2us <= 7'b0;
else
cnt_2us <= cnt_2us + 7'b1;
end
//cnt_2ms:计数2ms
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2ms <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2ms <= 10'b0;
else if(cnt_2us == CNT_2US_MAX - 7'b1)
cnt_2ms <= cnt_2ms + 10'b1;
else
cnt_2ms <= cnt_2ms;
end
//cnt_2s:计数2s
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2s <= 10'b0;
else if(cnt_2s >= (CNT_2S_MAX - 10'b1) && cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= cnt_2s + freq_step;
else
cnt_2s <= cnt_2s;
end
//inc_dec_flag为低电平,led灯由暗变亮,inc_dec_flag为高电平,led灯由亮变暗
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
inc_dec_flag <= 1'b0;
else if(cnt_2s >= (CNT_2S_MAX - 10'b1) && cnt_2ms ==( CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1))
inc_dec_flag <= ~inc_dec_flag;
else
inc_dec_flag <= inc_dec_flag;
end
//led:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led_t <= 1'b0;
else if((inc_dec_flag == 1'b1 && cnt_2ms >= cnt_2s) || (inc_dec_flag == 1'b0 && cnt_2ms <= cnt_2s))
led_t <= 1'b1;
else
led_t<= 1'b0;
end
endmodule
【3】编辑IP核
由于【breath_led.v】文件未被引用,故而被归入【Unreferenced】文件夹下。接下来修改其顶层代码。
- 3.1 双击【Design Sources/breath_led_ip_v1_0】,编辑其代码。
- 在参数设置块中添加
parameter START_FREQ_STEP = 10'd1,
- 在输入输出块中添加
output led,
- 在实例化模块的语句中添加
.START_FREQ_STEP(START_FREQ_STEP),
- 在实例化初始化语句中添加
.led(led),
- 在参数设置块中添加
- 3.2 双击【breath_led_ip_v1_0】下方的【breath_led_ip_v1_0_S0_AXI_inst】,编辑其代码
-
在参数设置块中添加
parameter START_FREQ_STEP = 10'd1,
-
在输入输出块中添加
output led,
-
在代码结尾,对breath_led.v文件进行实例化,即在【endmoule】上方Add User logic here的提示下,添加
vbreath_led #( .START_FREQ_STEP(START_FREQ_STEP) ) u_breath_led( .sys_clk (S_AXI_ACLK), .sys_rst_n (S_AXI_ARESETN), .sw_ctrl (slv_reg0[0]), .set_en (slv_reg1[31]), .set_freq_step (slv_reg1[9:0]), .led (led) );
-
至此,我们刚刚创建的【breath_led.v】文件就会被纳入【breath_led_ip_v1_0_S0_AXI_inst】下方。
【4】IP封装
-
4.1 【Run Synthesis】->如果提示保存,则保存->【OK】->完成后点【Cancel】
-
4.2 双击【Design Sources->IP-XACT->component.xml】,进行IP封装
-
4.3 进入【Compatibility】->【➕】->Add Family Explicity->勾选zynq(Zynq-7000),Life-Cycle选择Pre-Production->【OK】
-
4.4 进入【File Groups】->【Merge Changes from Groups Wizard】
-
4.5 进入【Customization Parameters】->Merge Changes from Customization Parameters Wizard->右键【Hidden Parameters/START_FREQ_STEP】->【Edit Parameter】,按下面的设置,设置完成后点击【OK】
- 勾选【Visible in Customization GUI】
- Format设为【long】
- 勾选【Specify Range】,Type设为【Range of integers】,最小值和最大值设为1到10。
- 【Default Value】设为1
-
4.6 进入【Review and Package】->【IP has been modified】->【Re-Package IP】->【Yes】
至此,该工程会被关闭,在【...\drivers\breath_led_ip_v1_0\src】路径中,会生成C语言的源文件、头文件和makefile文件。修改makefile文件,在其libs段的上方,添加
c
OBJECTS = $(addsuffix .o, $(basename $(wildcard *.c)))
ASSEMBLY_OBJECTS = $(addsuffix .o, $(basename $(wildcard *.S)))
将make clean上方的代码修改为
c
$(ARCHIVER) -r ${RELEASEDIR}/${LIB} ${OBJECTS} ${ASSEMBLY_OBJECTS}
修改其clean段代码为
c
rm -rf ${OBJECTS} ${ASSEMBLY_OBJECTS}
【5】创建Vivado工程
-
5.1 新建一个Vivado工程,名字为【user_led】,路径与custom_ip一致,选择你用的开发板->【菜单栏/Tools/Setting】
-
5.2 进入【IP/Repository】->【➕】->找到刚刚创建的IP核路径,文件夹【breath_led_ip_1.0】->【Select】->【OK】
-
5.3 【Create Block Design】,名字设为system->【➕】->搜索zynq并添加->双击模块进入编辑界面->,添加 UART 控制器(MIO14 和 MIO15)、修改 DDR3 控制器配置->Run Block Automation
-
5.4 【➕】->搜索led->添加【breath_led_ip_v1.0】->双击可修改其配置
-
5.5 【Run Connection Automation】自动连线->【OK】
-
5.6 右键【breath_led_ip_0】的【led】引脚->【Make External】->将引出的led_0改为【led】
【6】调用与验证
接下来要做的就没有什么好说的了,下面只列出步骤
- 6.1 【Generate Output Products】->【Create HDL Wrapper】
- 6.2 【Open Elaborated Design】->【I/O Planning Layout】->led分配为J16,LVCMOS33
- 6.2 【Generate Bitstream】->【Export Hardware】
- 注意:如果菜单栏下方有蓝色的【ELABORATED...】,需要将其x掉。
- 6.3 创建vitis项目
c
#include "stdio.h"
#include "xparameters.h"
#include "xil_printf.h"
#include "breath_led_ip.h"
#include "xil_io.h"
#include "sleep.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
//main函数
int main()
{
int freq_flag; //定义频率状态,用于循环改变呼吸灯的呼吸频率
int led_state; //定义LED灯的状态
xil_printf("LED User IP Test!\r\n");
while(1){
//根据freq_flag的标志位,切换呼吸灯的频率
if(freq_flag == 0){
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,0x80000001);
freq_flag = 1;
}
else{
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,0x80000004);
freq_flag = 0;
}
//获取LED当前开关状态 1:打开 0:关闭
led_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR,LED_IP_REG0);
//如果开关关闭,打开呼吸灯
if(led_state == 0){
BREATH_LED_IP_mWriteReg (LED_IP_BASEADDR, LED_IP_REG0, 1);
xil_printf("Breath LED ON\r\n");
}
sleep(8);
//获取LED当前开关状态 1:打开 0:关闭
led_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR,LED_IP_REG0);
//如果开关打开,关闭呼吸灯
if(led_state == 1){
BREATH_LED_IP_mWriteReg (LED_IP_BASEADDR, LED_IP_REG0, 0);
xil_printf("Breath LED OFF\r\n");
}
sleep(1);
}
}