Version management is essential in FPGA projects; otherwise, questions like “Who wrote this?” and “Which version is running on the board?” can be quite frustrating. Today, let’s discuss three common methods.

Why is Version Management Necessary for FPGAs?
-
Some bugs are caused by new logic, and without version numbers, it is difficult to trace back.
-
The version of the FPGA bitstream file is often hard to identify at a glance.
-
During software or low-level driver debugging, confusion over FPGA versions can double debugging costs.
Method 1: Automatically Generate Compilation Timestamps with Vivado (The Simplest)
Using a Tcl script, automatically write the current date and time each time a bitstream is generated.
The key steps are as follows:
Create a new file version_date.v in the project with content similar to:
parameter [31:0] FPGA_VERSION_A = 32'h20250831; // Date
parameter [31:0] FPGA_VERSION_B = 32'h000000; // Time
Write a Tcl script (e.g., update_version.tcl) to set the path in the synthesis settings, updating version_date.v each time you compile:
set file_addr "../../version_date.v"
set time_now [clock seconds]
set time0 [clock format $time_now -format "%Y%m%d"]
set time1 [clock format $time_now -format "%H%M%S"]
set f [open $file_addr w]
puts $f "parameter [31:0] FPGA_VERSION_A = 32'h$time0;"
puts $f "parameter [31:0] FPGA_VERSION_B = 32'h$time1;"
close $f
Include this version_date.v in the top-level Verilog:
assign version_date = FPGA_VERSION_A;
assign version_time = FPGA_VERSION_B;
Each time you debug on the board, read these two values in the logic or test interface to immediately know the compilation date and time.
Advantages: Fully automated, no need to write version numbers, updates on compilation
Disadvantages: File paths can easily be mistaken, especially since Windows systems are not sensitive to file paths, making this inclusion method hard to master
Method 2: Embed Compilation Timestamps in Bitstream Using USR_ACCESS (Officially Recommended)
Xilinx officially supports using the BITSTREAM.CONFIG.USR_ACCESS parameter set to TIMESTAMP during bitstream compilation, automatically writing the compilation time into the 32-bit USR_ACCESS register in the configuration bitstream (https://docs.amd.com/v/u/en-US/xapp1232-bitstream-id-with-usr_access). This value can be read in FPGA logic through the USR_ACCESS primitive.
The specific process is as follows:
In the Vivado GUI: Tools → Project Settings → Bitstream, find BITSTREAM.CONFIG.USR_ACCESS, set it to TIMESTAMP, and then regenerate the bitstream.
Instantiate the Xilinx primitive in your design, for example:
wire [31:0] version_id;
USR_ACCESSE2 usr_access_inst (
.CFGCLK(),
.DATA(version_id),
.DATAVALID()
);
Add a control register in the FPGA or read version_id through the JTAG/SPI interface.
For detailed operation instructions, refer to the Xilinx application document XAPP1232.
https://docs.amd.com/v/u/en-US/xapp1232-bitstream-id-with-usr_access
Advantages:
Automation—no need to manually maintain version numbers.
Accurate information—the bitstream carries the timestamp, so there’s no fear of forgetting to update.
Easy to view—version information can be easily read through logic or software.
Disadvantages:
Information is fixed as a timestamp, cannot embed Git Hash or semantic versioning.
Still requires self-deployed reading logic.
Method 3: Automatically Inject Build Information (Best Engineering Practice)
This modern method is suitable for CI/CD and team projects: dynamically generate HDL source files containing version numbers and build times during the build process, then merge them into the design.
// Automatically generated file build_info.v
module build_info;
localparam string GIT_HASH = "a1b2c3d";
localparam string BUILD_DATE = "2025-08-30";
endmodule
Use scripts (shell, Python, etc.) in cmake/Makefile to obtain the Git hash and time, then write it:
echo "module build_info; localparam GIT_HASH = \"$HASH\"; localparam BUILD_DATE = \"$DATE\"; endmodule" > build_info.v
Then include “build_info.v” in the main project to read the version identifier.
Advantages:
The most comprehensive information (Git commit number, date, branch, etc. can all be included).
High degree of automation, suitable for team collaboration.
Easy to synchronize with software versions.
Disadvantages:
Initial setup is complex and requires script support.
Cross-platform (Windows/Linux) script compatibility needs attention.
Comparison Table of the Three Methods
| Method | Degree of Automation | Content Information | Difficulty of Use | Recommended Scenarios |
|---|---|---|---|---|
| Automatic Timestamp version_date.v | High | Date + Time | Low | Quick start, personal projects |
| USR_ACCESS Timestamp | High | Compilation Timestamp | Medium | Xilinx platform projects |
| Automatically Inject Build Information | High | Git Hash + Time | Medium to High | Team/CI/CD projects |
Conclusion: How to Choose the Most Reliable Method?
Want to save trouble? Just rename the project, but you may face “correction costs” at any time.
Want to automatically and accurately know when this board was compiled? Choose the USR_ACCESS timestamp.
Want to synchronize with Git + software versions and plan for future CI/CD packaging? Go for the automatic injection of source code solution.
Remember this: “An FPGA without a version number is more chaotic than bits.” “An FPGA project without version management will eventually fail in debugging.”
If anyone has better methods for version management or any questions about the above methods, feel free to leave a comment for discussion~