Can I uses in VHDL something similar to the C-Sourcecode-Macros __DATE__
and __TIME__
to make the compile time available in the FPGA as a kind of version time stamp?
As a >>>new-comer<<< to VHDL I want to modify the following existing code, which puts a hard coded date into a FPGA register. I always have to remember adjusting the values before compiling. It would be easier if this is done automatically. Can I also include hours/minutes/seconds?
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
ENTITY Datum2 IS
PORT
(
Day :OUT std_logic_vector(4 downto 0);
Month :OUT std_logic_vector(3 downto 0);
Year :OUT std_logic_vector(4 downto 0)
);
END Datum2 ;
ARCHITECTURE rtl OF Datum2 IS
BEGIN
-- "08.08.0013"
Day <= conv_std_logic_vector(8, 5);
Month <= conv_std_logic_vector(8, 4);
Year <= conv_std_logic_vector(13, 5);
END ARCHITECTURE rtl;
Current date and time is not directly available in VHDL, but solution is suggested below.
EDITED 2013-08-10: Added description for Altera Quartus II Tcl automatic generation.
One way to make date and time available is through an automatically generated VHDL package like the below:
library ieee;
use ieee.std_logic_1164.all;
package datetime is
-- Date information
constant YEAR_INT : integer := 2013;
constant YEAR_HEX : std_logic_vector(15 downto 0) := X"2013";
constant MONTH_INT : integer := 08;
constant MONTH_HEX : std_logic_vector(7 downto 0) := X"08";
constant DAY_INT : integer := 09;
constant DAY_HEX : std_logic_vector(7 downto 0) := X"09";
constant DATE_HEX : std_logic_vector(31 downto 0) := YEAR_HEX & MONTH_HEX & DAY_HEX;
-- Time information
constant HOUR_INT : integer := 13;
constant HOUR_HEX : std_logic_vector(7 downto 0) := X"13";
constant MINUTE_INT : integer := 06;
constant MINUTE_HEX : std_logic_vector(7 downto 0) := X"06";
constant SECOND_INT : integer := 29;
constant SECOND_HEX : std_logic_vector(7 downto 0) := X"29";
constant TIME_HEX : std_logic_vector(31 downto 0) := X"00" & HOUR_HEX & MINUTE_HEX & SECOND_HEX;
-- Miscellaneous information
constant EPOCH_INT : integer := 1376046389; -- Seconds since 1970-01-01_00:00:00
end package;
This VHDL package can be created with a Tcl script like this:
# Make datetime.vhd package from Tcl script
# Current date, time, and seconds since epoch
# Array index 0 1 2 3 4 5 6
set datetime_arr [clock format [clock seconds] -format {%Y %m %d %H %M %S %s}]
# Write VHDL package
set filename datetime.vhd
set file [open $filename w]
puts $file "library ieee;"
puts $file "use ieee.std_logic_1164.all;"
puts $file ""
puts $file "package datetime is"
puts $file " -- Date information"
puts $file " constant YEAR_INT : integer := [lindex $datetime_arr 0];"
puts $file " constant YEAR_HEX : std_logic_vector(15 downto 0) := X\"[lindex $datetime_arr 0]\";"
puts $file " constant MONTH_INT : integer := [lindex $datetime_arr 1];"
puts $file " constant MONTH_HEX : std_logic_vector(7 downto 0) := X\"[lindex $datetime_arr 1]\";"
puts $file " constant DAY_INT : integer := [lindex $datetime_arr 2];"
puts $file " constant DAY_HEX : std_logic_vector(7 downto 0) := X\"[lindex $datetime_arr 2]\";"
puts $file " constant DATE_HEX : std_logic_vector(31 downto 0) := YEAR_HEX & MONTH_HEX & DAY_HEX;"
puts $file " -- Time information"
puts $file " constant HOUR_INT : integer := [lindex $datetime_arr 3];"
puts $file " constant HOUR_HEX : std_logic_vector(7 downto 0) := X\"[lindex $datetime_arr 3]\";"
puts $file " constant MINUTE_INT : integer := [lindex $datetime_arr 4];"
puts $file " constant MINUTE_HEX : std_logic_vector(7 downto 0) := X\"[lindex $datetime_arr 4]\";"
puts $file " constant SECOND_INT : integer := [lindex $datetime_arr 5];"
puts $file " constant SECOND_HEX : std_logic_vector(7 downto 0) := X\"[lindex $datetime_arr 5]\";"
puts $file " constant TIME_HEX : std_logic_vector(31 downto 0) := X\"00\" & HOUR_HEX & MINUTE_HEX & SECOND_HEX;"
puts $file " -- Miscellaneous information"
puts $file " constant EPOCH_INT : integer := [lindex $datetime_arr 6]; -- Seconds since 1970-01-01_00:00:00"
puts $file "end package;"
close $file
In Altera Quartus II it is possible to make the flow run this script before synthesis, whereby the datetime package can be created. This is done in the .qsf file with the line below, where the script is named "make_datetime.tcl":
set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:make_datetime.tcl"
Further description of this Quartus II feature can be found in Quartus II Tcl Example: Automatic Script Execution.
The Datum2 module can then use the package like this:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity Datum2 is
port(
Day : out std_logic_vector(4 downto 0);
Month : out std_logic_vector(3 downto 0);
Year : out std_logic_vector(4 downto 0));
end Datum2;
library work;
use work.datetime;
architecture rtl of Datum2 is
begin
Day <= conv_std_logic_vector(datetime.day_int, 5);
Month <= conv_std_logic_vector(datetime.month_int, 4);
Year <= conv_std_logic_vector(datetime.year_int mod 100, 5);
end architecture rtl;
After synthesis in Quartus II the RTL viewer shows the module outputs as below:
Previously described alternative solutions are to create the VHDL package with a Bash script like this:
# Make datetime.vhd package from shell script
# Current date, time, and seconds since epoch
# Array index 0 1 2 3 4 5 6
datetime_arr=($(date +"%Y %m %d %H %M %S %s"))
# Write VHDL package
filename="datetime.vhd"
echo "library ieee;" > $filename
echo "use ieee.std_logic_1164.all;" >> $filename
echo "" >> $filename
echo "package datetime is" >> $filename
echo " -- Date information" >> $filename
echo " constant YEAR_INT : integer := ${datetime_arr[0]};" >> $filename
echo " constant YEAR_HEX : std_logic_vector(15 downto 0) := X\"${datetime_arr[0]}\";" >> $filename
echo " constant MONTH_INT : integer := ${datetime_arr[1]};" >> $filename
echo " constant MONTH_HEX : std_logic_vector(7 downto 0) := X\"${datetime_arr[1]}\";" >> $filename
echo " constant DAY_INT : integer := ${datetime_arr[2]};" >> $filename
echo " constant DAY_HEX : std_logic_vector(7 downto 0) := X\"${datetime_arr[2]}\";" >> $filename
echo " constant DATE_HEX : std_logic_vector(31 downto 0) := YEAR_HEX & MONTH_HEX & DAY_HEX;" >> $filename
echo " -- Time information" >> $filename
echo " constant HOUR_INT : integer := ${datetime_arr[3]};" >> $filename
echo " constant HOUR_HEX : std_logic_vector(7 downto 0) := X\"${datetime_arr[3]}\";" >> $filename
echo " constant MINUTE_INT : integer := ${datetime_arr[4]};" >> $filename
echo " constant MINUTE_HEX : std_logic_vector(7 downto 0) := X\"${datetime_arr[4]}\";" >> $filename
echo " constant SECOND_INT : integer := ${datetime_arr[5]};" >> $filename
echo " constant SECOND_HEX : std_logic_vector(7 downto 0) := X\"${datetime_arr[5]}\";" >> $filename
echo " constant TIME_HEX : std_logic_vector(31 downto 0) := X\"00\" & HOUR_HEX & MINUTE_HEX & SECOND_HEX;" >> $filename
echo " -- Miscellaneous information" >> $filename
echo " constant EPOCH_INT : integer := ${datetime_arr[6]}; -- Seconds since 1970-01-01_00:00:00" >> $filename
echo "end package;" >> $filename
For platform independence then a Python 3.x script like this may be used:
# Make datetime.vhd package from Python 3.x script
# Get date and time
import datetime
import time
now = datetime.datetime.now()
now_epoch_sec = int(time.time())
# Write VHDL package
file = open('datetime.vhd', 'wt')
file.write('library ieee;\n')
file.write('use ieee.std_logic_1164.all;\n')
file.write('\n')
file.write('package datetime is\n')
file.write(' -- Date information\n')
file.write(' constant YEAR_INT : integer := {};\n'.format(now.strftime('%Y')))
file.write(' constant YEAR_HEX : std_logic_vector(15 downto 0) := X\"{}\";\n'.format(now.strftime('%Y')))
file.write(' constant MONTH_INT : integer := {};\n'.format(now.strftime('%m')))
file.write(' constant MONTH_HEX : std_logic_vector(7 downto 0) := X\"{}\";\n'.format(now.strftime('%m')))
file.write(' constant DAY_INT : integer := {};\n'.format(now.strftime('%d')))
file.write(' constant DAY_HEX : std_logic_vector(7 downto 0) := X\"{}\";\n'.format(now.strftime('%d')))
file.write(' constant DATE_HEX : std_logic_vector(31 downto 0) := YEAR_HEX & MONTH_HEX & DAY_HEX;\n')
file.write(' -- Time information\n')
file.write(' constant HOUR_INT : integer := {};\n'.format(now.strftime('%H')))
file.write(' constant HOUR_HEX : std_logic_vector(7 downto 0) := X\"{}\";\n'.format(now.strftime('%H')))
file.write(' constant MINUTE_INT : integer := {};\n'.format(now.strftime('%M')))
file.write(' constant MINUTE_HEX : std_logic_vector(7 downto 0) := X\"{}\";\n'.format(now.strftime('%M')))
file.write(' constant SECOND_INT : integer := {};\n'.format(now.strftime('%S')))
file.write(' constant SECOND_HEX : std_logic_vector(7 downto 0) := X\"{}\";\n'.format(now.strftime('%S')))
file.write(' constant TIME_HEX : std_logic_vector(31 downto 0) := X\"00\" & HOUR_HEX & MINUTE_HEX & SECOND_HEX;\n')
file.write(' -- Miscellaneous information\n')
file.write(' constant EPOCH_INT : integer := {}; -- Seconds since 1970-01-01_00:00:00\n'.format(now_epoch_sec))
file.write('end package;\n')
file.close()
For presentation of date and time in 32-bit register values, a module can be like this:
library ieee;
use ieee.std_logic_1164.all;
entity tb is
end entity;
library work;
use work.datetime;
architecture sim of tb is
signal date_hex : std_logic_vector(31 downto 0);
signal time_hex : std_logic_vector(31 downto 0);
begin
date_hex <= datetime.DATE_HEX;
time_hex <= datetime.TIME_HEX;
process is begin wait; end process;
end architecture;
Waveform shown below.
The Bash or Python script approach requires integration in the build flow for automatic package generation.
EDITED 2016-08-08 with update by Damien: Description of non-Tcl script call from Altera Quartus.
To integrate a bash script (under Linux), create a Tcl wrapper script which calls the bash script as part of the process. In this example, there is a "scripts" directory which has a "call_bash.tcl" script and a "make_datetime.sh" which does the actual work:
# Useful if the script is in a subdirectory
proc getScriptDirectory {} {
set dispScriptFile [file normalize [info script]]
set scriptFolder [file dirname $dispScriptFile]
return $scriptFolder
}
set scriptDir [getScriptDirectory]
# Call the bash script which does the real work
exec $scriptDir/make_datetime_vhdl.sh
# Add a message the Altera workflow
post_message -type info "Created datetime.vhd"
Integrating into Altera's build flow can then be achieved by adding the following to the the qsf file:
set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:scripts/call_bash.tcl"
You don't need to write out the full code every time the timestamp is update. In Quartus you can use TCL to set top-level generics prior to compilation:
Pass the generics down to you timestamp entity:
entity top is
generic (day, month, year : integer);
end entity top;
architecture struct of top is
begin
timestamp_inst : entity work.timestamp generic map(day, month, year);
end architecture struct;
And then use a preflow-script to set the generics:
set_parameter -name day $someday
set_parameter -name year $someyear
# etc
I've used this approach successfully with both timestamps, git revision, build ID. I find it convenient to convert it to a word array and then accessing as a ROM. Just use your imagination.
I use a 64-bit std_logic_vector
constant.
I have a TCL script which runs just before synthesis to encode the current time into a constant in a package, which is then included in the FPGA so it can be read out in some fashion.
There's a non-autogenerated version of the same package which I use in simulations - this doesn't change, so I don't have to keep updating the tests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With