##-------------------------------------------------------------------------------
## CERN BE-CO-HT
## General Cores
## https://www.ohwr.org/projects/general-cores
##-------------------------------------------------------------------------------
##
## Tcl script to automatically generate the SDB synthesis descriptor.
##
## Must be added to your top-level Manifest.py like this:
##
## syn_post_project_cmd = "$(TCL_INTERPRETER) [this_tcl_script] " +
##                         syn_tool + " $(PROJECT_FILE)"
##
## notes:
##
## 1. $(TCL_INTERPRETER) and $(PROJECT_FILE) will be automatically
##    resolved inside the Makefile generated by hdlmake.
## 2. syn_tool is a Manifest.py variable, it should already be in your
##    tol-level Manifest.py.
## 3. [this_tcl_script] should be replaced by the full path to
##    this Tcl script. Relative paths should be with respect to
##    the location of the top-level Manifest.py.
## 4. Mind the spaces inside the quotes, before and after syn_tool.
##
##-------------------------------------------------------------------------------
## Copyright CERN 2018
##-------------------------------------------------------------------------------
## This Source Code Form is subject to the terms of the Mozilla Public License,
## version 2.0. If a copy of the MPL was not distributed with this file, You can
## obtain one at https://mozilla.org/MPL/2.0/.
##-------------------------------------------------------------------------------

# get current time
set time_now [clock seconds]

# set list of supported tools
set supported_tools [list ise]

# get synthesis tool from 1st command line argument
set syn_tool [lindex $argv 0]

# get project file from 2nd command-line argument
set project_file [lindex $argv 1]

# set file for all output
set out_file synthesis_descriptor.vhd

# short procedure for consistent reporting
proc report {level message} {
    puts "$level:$::argv0 - $message"
}

# procedure to make sure a given string has a certain length,
# necessary for embedding strings in SDB. If string is longer, it is
# trimmed, if it is shorter it is padded (by default with spaces).
# Before returning, we also replace all non-ASCII chars with "?".
#
# For the curious: https://en.wikipedia.org/wiki/Procrustes
proc str_procrustes {str limit {pad_char " "}} {
    set str_len [string length $str]
    if {[expr $str_len < $limit]} {
	set pad [string repeat $pad_char [expr $limit - $str_len]]
	set retval $str$pad
    } else {
	set retval [string range $str 0 [expr $limit - 1]]
    }
    return [regsub -all {[\u0080-\uffff]} $retval "?"]
}

#-------------------------------------------------------------------------------
# STEP 1/7: Check that the synthesis tool is supported
#-------------------------------------------------------------------------------
if {[lsearch -exact $supported_tools $syn_tool] == -1} {
    report ERROR "$syn_tool is not (yet) supported, exiting."
    exit -1
}

#-------------------------------------------------------------------------------
# STEP 2/7: Check that project file exists
#-------------------------------------------------------------------------------

if {![file exists $project_file]} {
    report ERROR "Missing file $project_file, exiting."
    exit -1
}

#-------------------------------------------------------------------------------
# STEP 3/7: Open project file
#-------------------------------------------------------------------------------

if {$syn_tool == "ise"} {
    xilinx::project open $project_file
    # dummy call to project get name to ger rid of annoying error
    # regarding list of boards for ISim
    xilinx::project get name
}
report INFO "Opened project $project_file."

#-------------------------------------------------------------------------------
# STEP 4/7: Get necessary info from synthesis tool and Git
#-------------------------------------------------------------------------------

if {$syn_tool == "ise"} {
    set inf_project [xilinx::project get name]
    # ISE does not provide a tcl command to get version.
    # Possible ways include parsing the project file (not backward compatible)
    # or Xilinx AR#35500.
    # For now we hard-code, since anyway ISE is end-of-life at version 14.7.
    set inf_tool_version [format "%08d" "147"]
}

set inf_tool_name [string toupper $syn_tool]
set inf_date [clock format $time_now -format "%A, %B %d %Y"]
set inf_date_ymd [clock format $time_now -format "%Y%m%d"]

# Git commands below have fallbacks in case something goes wrong.
if { [catch { set inf_user [exec git config --get user.name] }] } {
    set inf_user "unknown user"
}
if { [catch { set inf_commit_id [exec git log -1 --format=%H] }] } {
    set inf_commit_id "unknown commit"
}

if { [catch { set branch_name [exec git rev-parse --abbrev-ref HEAD] }] } {
    set branch_name "unknown branch"
}

if { [catch { set remote_name [exec git config --get branch.$branch_name.remote] }] } {
    set remote_name "unknown remote"
}

if { [catch { set inf_repo_url [exec git config --get remote.$remote_name.url] }] } {
    set inf_repo_url "unknown url (possibly built from a local commit/branch)"
}
if { [catch { set dirty_repo [exec git describe --all --dirty | grep -c dirty] }] } {
    set dirty_repo "0"
}

#-------------------------------------------------------------------------------
# STEP 5/7: Generate the output file
#-------------------------------------------------------------------------------

set fp [open $out_file w]

puts $fp [string repeat "-" 80]
puts $fp "-- SDB meta information for $project_file."
puts $fp "--"
puts $fp "-- This file was automatically generated by $argv0 on:"
puts $fp "-- $inf_date"
puts $fp "--"
puts $fp "-- $argv0 is part of OHWR general-cores:"
puts $fp "-- https://www.ohwr.org/projects/general-cores/wiki"
puts $fp "--"
puts $fp "-- For more information on SDB meta information, see also:"
puts $fp "-- https://www.ohwr.org/projects/sdb/wiki"
puts $fp [string repeat "-" 80]
puts $fp ""
puts $fp "library ieee;"
puts $fp "use ieee.std_logic_1164.all;"
puts $fp "use work.wishbone_pkg.all;"
puts $fp ""
puts $fp "package synthesis_descriptor is"
puts $fp ""
puts $fp "  constant c_sdb_synthesis_info : t_sdb_synthesis := ("
puts $fp "    syn_module_name  => \"[str_procrustes $inf_project 16]\","
if {$dirty_repo == "0"} {
    puts $fp "    syn_commit_id    => \"[str_procrustes $inf_commit_id 32]\","
} else {
    puts $fp "    syn_commit_id    => \"[str_procrustes $inf_commit_id 31]*\","
}
puts $fp "    syn_tool_name    => \"[str_procrustes $inf_tool_name 8]\","
puts $fp "    syn_tool_version => x\"$inf_tool_version\","
puts $fp "    syn_date         => x\"[str_procrustes $inf_date_ymd 8]\","
puts $fp "    syn_username     => \"[str_procrustes $inf_user 15]\");"
puts $fp ""
puts $fp "  constant c_sdb_repo_url : t_sdb_repo_url := ("
puts $fp "    repo_url => \"[str_procrustes $inf_repo_url 63]\");"
puts $fp ""
puts $fp "end package synthesis_descriptor;"

close $fp

#-------------------------------------------------------------------------------
# Step 6/7: Add the output file to the project
#-------------------------------------------------------------------------------

if {$syn_tool == "ise"} {
    xilinx::xfile add $out_file
}
report "INFO" "Added $out_file to $project_file."

#-------------------------------------------------------------------------------
# Step 7/7: Close project file
#-------------------------------------------------------------------------------

if {$syn_tool == "ise"} {
    xilinx::project close
}
report "INFO" "Closed project $project_file."
