新手入门 Makefile:FPGA 项目实战教程(三)

系列文章目录
1、VMware Workstation Pro安装指南:详细步骤与配置选项说明
2、VMware 下 Ubuntu 操作系统下载与安装指南
3、基于 Ubuntu 的 Linux 系统中 Vivado 2020.1 下载安装教程
4、利用 Makefile 高效启动 VIVADO 软件:深入解析与实践
5、新手入门 Makefile:FPGA 项目实战教程(一)
6、新手入门 Makefile:FPGA 项目实战教程(二)
引言
在上一篇6、新手入门 Makefile:FPGA 项目实战教程(二)文章中,介绍了大概的项目结构和基础知识内容,接下来这一章我们将结合实际的工程。
文章目录
- [新手入门 Makefile:FPGA 项目实战教程(三)](#新手入门 Makefile:FPGA 项目实战教程(三))
- 系列文章目录
-
- 引言
- [五、FPGA 项目实战:LED 闪烁示例](#五、FPGA 项目实战:LED 闪烁示例)
-
- [5.1 项目需求和设计概述](#5.1 项目需求和设计概述)
- [5.2 编写 RTL 代码](#5.2 编写 RTL 代码)
- [5.3 编写约束文件](#5.3 编写约束文件)
- [5.4 编写 Makefile](#5.4 编写 Makefile)
- [5.5 编写 Tcl 脚本](#5.5 编写 Tcl 脚本)
- [5.6 实战操作步骤](#5.6 实战操作步骤)
- 准备工作
- [执行 Makefile](#执行 Makefile)
- 清理项目
- [5.7 以太网接口项目(高级案例)](#5.7 以太网接口项目(高级案例))
-
- [5.7.1 项目结构](#5.7.1 项目结构)
- [5.7.2 Makefile 特点](#5.7.2 Makefile 特点)
- 未完待续。。。。
五、FPGA 项目实战:LED 闪烁示例
5.1 项目需求和设计概述
我们将通过一个简单的 LED 闪烁项目来演示如何使用 Makefile 进行 FPGA 项目开发。这个项目的目标是在 FPGA 开发板上实现一个 LED 以约 1Hz 的频率闪烁。
设计概述:
-
使用 25 位计数器对系统时钟进行分频。
-
当计数器达到最大值时,翻转 LED 输出状态。
-
系统时钟频率假设为 100MHz,因此计数器最大值设为 25,000,000(产生约 1Hz 的闪烁频率)。
5.2 编写 RTL 代码
创建文件src/rtl/top.v
,内容如下:
module top (
input clk,
output reg led
);
reg [24:0] counter;
always @(posedge clk) begin
if (counter == 25'd25000000) begin
counter <= 25'd0;
led <= ~led;
end else begin
counter <= counter + 1'b1;
end
end
endmodule
这个模块实现了一个简单的 LED 闪烁功能,使用 25 位计数器对系统时钟进行分频,产生约 1Hz 的闪烁频率。
5.3 编写约束文件
创建文件src/constraints/led.xdc
,内容如下:
# 时钟约束
create_clock -name clk -period 10.000 [get_ports clk]
set_property PACKAGE_PIN R4 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
# LED输出约束
set_property PACKAGE_PIN U18 [get_ports led]
set_property IOSTANDARD LVCMOS33 [get_ports led]
这些约束指定了时钟频率和 LED 输出的物理引脚。请根据实际使用的 FPGA 开发板调整PACKAGE_PIN
的值。
5.4 编写 Makefile
创建文件scripts/Makefile
,内容如下:
# Makefile for Vivado automation
PROJECT_NAME ?= led_blink
VIVADO_VERSION ?= 2020.1
VIVADO_PATH ?= /tools/Xilinx/Vivado/2020.1/bin
BUILD_TCL ?= /home/zfj/fpga_project/led_blink/build_project.tcl
all:
@echo "Starting Vivado batch mode..."
@mkdir -p build && cd build && ${VIVADO_PATH}/vivado -mode batch -source ${BUILD_TCL} -tclargs $(PROJECT_NAME)
@echo "Bitstream generation completed. Output files in build/ directory."
clean:
@echo "Cleaning project files..."
@rm -rf build .Xil
@rm -f ${PROJECT_NAME}.bit
@echo "Clean complete."
.PHONY: all clean
变量定义
-
PROJECT_NAME
:定义项目名称,默认值为led_blink
。这里使用了?=
条件赋值运算符,表示如果该变量未被定义过,则使用后面的值进行赋值。 -
VIVADO_VERSION
:指定 Vivado 版本,默认值为2020.1
。 -
VIVADO_PATH
:指定 Vivado 安装路径下的bin
目录,默认值为/tools/Xilinx/Vivado/2020.1/bin
。 -
BUILD_TCL
:指定 Vivado 项目构建脚本的路径,默认值为/home/zfj/fpga_project/led_blink/build_project.tcl
。
目标定义
-
all
目标:-
首先输出提示信息
Starting Vivado batch mode...
,表示开始 Vivado 批处理模式。 -
使用
mkdir -p build
创建build
目录(-p
选项表示如果父目录不存在则一并创建),然后进入build
目录。 -
执行
${VIVADO_PATH}/vivado -mode batch -source ${BUILD_TCL} -tclargs $(PROJECT_NAME)
命令,以批处理模式运行 Vivado,并指定构建脚本${BUILD_TCL}
以及传递项目名称参数$(PROJECT_NAME)
。这一步会根据构建脚本的内容创建项目、添加源文件、约束文件,进行综合、实现等操作,最终生成比特流文件。 -
最后输出提示信息
Bitstream generation completed. Output files in build/ directory.
,表示比特流文件生成完成,输出文件在build
目录中。
-
-
clean
目标:-
输出提示信息
Cleaning project files...
,表示开始清理项目文件。 -
使用
rm -rf build
删除build
目录及其所有内容(-rf
选项表示强制递归删除),rm -rf .Xil
删除.Xil
目录(该目录通常包含 Vivado 相关的缓存文件),rm -f ${PROJECT_NAME}.bit
删除生成的比特流文件(-f
选项表示强制删除,不提示)。 -
最后输出提示信息
Clean complete.
,表示清理完成。
-
5.5 编写 Tcl 脚本
创建文件scripts/build_project.tcl
,内容如下:
# /home/zfj/fpga_project/led_blink/build_project.tcl
set project_name [lindex $argv 0]
# 设置项目路径
set project_dir [file normalize [file dirname [info script]]]
set src_dir ${project_dir}/src
set constr_dir ${project_dir}/constraints
# 创建项目在build目录
create_project -force $project_name ${project_dir}/build -part xc7z020clg400-1
set_property target_language Verilog [current_project]
# 添加源文件
add_files -norecurse [glob -nocomplain ${src_dir}/*.v ${src_dir}/*.vhd]
# 添加约束文件
if {[file exists ${constr_dir}/top.xdc]} {
add_files -fileset constrs_1 -norecurse ${constr_dir}/top.xdc
} else {
puts "WARNING: Constraint file not found at ${constr_dir}/top.xdc"
}
# 设置实现策略
set_property strategy Performance_Explore [get_runs impl_1]
# 运行综合与实现
launch_runs synth_1
wait_on_run synth_1
launch_runs impl_1 -to_step write_bitstream
wait_on_run impl_1
# 生成报告
open_run impl_1
report_utilization -file ${project_dir}/build/${project_name}_utilization.rpt
report_timing_summary -file ${project_dir}/build/${project_name}_timing.rpt
# 导出比特流
set bitstream_path ${project_dir}/build/${project_name}.runs/impl_1/*.bit
if {[llength [glob -nocomplain $bitstream_path]] > 0} {
file copy -force [lindex [glob $bitstream_path] 0] ${project_dir}/${project_name}.bit
puts "Bitstream copied to: ${project_dir}/${project_name}.bit"
} else {
puts "ERROR: Bitstream file not found!"
}
# 关闭项目
close_project
exit
变量设置
-
project_name
:通过[lindex $argv 0]
获取命令行传递的第一个参数,作为项目名称。 -
project_dir
:通过[file normalize [file dirname [info script]]]
获取当前脚本所在目录的规范化路径,作为项目目录。 -
src_dir
:定义源文件目录为${project_dir}/src
。 -
constr_dir
:定义约束文件目录为${project_dir}/constraints
。
项目创建与设置
-
create_project -force $project_name ${project_dir}/build -part xc7z020clg400 - 1
:在${project_dir}/build
目录下创建名为$project_name
的项目,并指定芯片型号为xc7z020clg400 - 1
。-force
选项表示如果项目已存在则强制覆盖。 -
set_property target_language Verilog [current_project]
:设置当前项目的目标语言为 Verilog。
文件添加
-
add_files -norecurse [glob -nocomplain ${src_dir}/*.v ${src_dir}/*.vhd]
:添加src_dir
目录下所有的.v
和.vhd
文件作为源文件,-norecurse
表示不递归查找子目录,[glob -nocomplain ${src_dir}/*.v ${src_dir}/*.vhd]
使用glob
命令匹配指定的文件。 -
if {[file exists ${constr_dir}/top.xdc]} {... } else {... }
:检查${constr_dir}
目录下是否存在top.xdc
约束文件,如果存在则使用add_files -fileset constrs_1 -norecurse ${constr_dir}/top.xdc
将其添加到constrs_1
文件集中,否则输出警告信息。
综合与实现
-
set_property strategy Performance_Explore [get_runs impl_1]
:设置impl_1
运行的实现策略为Performance_Explore
,用于优化性能。 -
launch_runs synth_1
:启动综合过程,生成网表文件。 -
wait_on_run synth_1
:等待综合过程完成。 -
launch_runs impl_1 -to_step write_bitstream
:启动实现过程,直到write_bitstream
步骤,即生成比特流文件。 -
wait_on_run impl_1
:等待实现过程完成。
报告生成与比特流导出
-
open_run impl_1
:打开impl_1
运行结果。 -
report_utilization -file ${project_dir}/build/${project_name}_utilization.rpt
:生成资源利用率报告,并保存到${project_dir}/build/${project_name}_utilization.rpt
文件中。 -
report_timing_summary -file ${project_dir}/build/${project_name}_timing.rpt
:生成时序总结报告,并保存到${project_dir}/build/${project_name}_timing.rpt
文件中。 -
set bitstream_path ${project_dir}/build/${project_name}.runs/impl_1/*.bit
:设置比特流文件路径。 -
if {[llength [glob -nocomplain $bitstream_path]] > 0} {... } else {... }
:检查是否生成了比特流文件,如果生成了则将其复制到项目目录下,并命名为${project_dir}/${project_name}.bit
,同时输出提示信息;如果未找到比特流文件,则输出错误信息。
项目关闭
-
close_project
:关闭当前项目。 -
exit
:退出 TCL 脚本。
5.6 实战操作步骤
准备工作
确保已经安装了 Vivado 软件,并且其版本与 Makefile 中指定的VIVADO_VERSION
一致。同时,将 FPGA 项目的源文件、约束文件等按照上述项目结构放置在相应目录下。
执行 Makefile
-
打开终端,进入 FPGA 项目所在目录。
-
执行
make all PROJECT_NAME=led_blink
命令,Makefile 会按照定义的规则,调用 Vivado 以批处理模式运行build_project.tcl
脚本,完成项目的创建、源文件添加、约束文件添加、综合、实现、报告生成以及比特流导出等一系列操作。在执行过程中,终端会输出相应的提示信息,可以根据这些信息了解构建过程的进展情况。
-
如果构建过程中出现错误,根据错误提示检查源文件、约束文件以及 Makefile 和
build_project.tcl
脚本的配置是否正确。常见的错误可能包括文件路径错误、语法错误、芯片型号不匹配等。 -
bit流文件的生成:
清理项目
当需要重新构建项目或者清理生成的文件时,可以执行make clean
命令。该命令会删除build
目录及其所有内容、.Xil
目录以及生成的比特流文件,确保项目处于初始状态,以便进行下一次构建。
生成比特流后,可以使用 Vivado 的硬件管理器将其下载到 FPGA 板卡上,观察 LED 是否开始闪烁。
5.7 以太网接口项目(高级案例)
对于更复杂的项目,例如以太网接口设计,可以采用类似的方法进行 Makefile 和 Tcl 脚本的编写。
5.7.1 项目结构
以太网接口项目的结构如下:
ethernet/
├── src/
│ ├── rtl/
│ │ ├── eth_mac.v # 以太网MAC模块
│ │ └── eth_top.v # 顶层模块
│ ├── constraints/
│ │ └── eth.xdc # 约束文件
│ └── ip/
│ └── clk_wiz.xci # 时钟IP核
├── scripts/
│ ├── build_project.tcl # Tcl脚本
│ └── Makefile # Makefile文件
└── output/ # 生成的输出文件
5.7.2 Makefile 特点
以太网接口项目的 Makefile 与 LED 项目类似,但可能需要更多的目标和更复杂的 Tcl 脚本,以处理 IP 核和更复杂的约束:
# 以太网接口项目的Makefile可能包含以下目标:
# 生成IP核
ipgen:
@echo "Generating IP cores..."
$(VIVADO) -mode batch -source $(TCL_SCRIPT) ipgen
# 综合设计
synth: ipgen
@echo "Synthesizing design..."
$(VIVADO) -mode batch -source $(TCL_SCRIPT) synth
# 实现设计
impl: synth
@echo "Implementing design..."
$(VIVADO) -mode batch -source $(TCL_SCRIPT) impl
在这个例子中,ipgen
目标用于生成 IP 核,synth
目标依赖于ipgen
,确保在综合前已经生成了必要的 IP 核。
未完待续。。。。
