Quartus® II Tcl Example: Automatic Version Number

author-image

By

It is useful to have a version number or time stamp embedded in your FPGA design. A version number or time stamp eliminates any confusion about which version of your design is currently programmed in the FPGA. For such a number to be useful, it must be updated automatically in the design compilation flow. Also, the number must be stored in the design hardware, such as in memory or a bank of registers.

This Tcl example describes different ways to generate a version number or time stamp, as well as different ways to store it in a design. Then, it shows a script framework you can use to create a Tcl script that generates and stores the version number automatically, every time you compile a design. Finally, it shows an example of a complete script.

Get a Number

The following list shows how you can generate a version number:

Store the Number

In addition to getting the number, you must write it in a design file. The following are examples of how to store the number:

Script Framework

You can combine methods for getting and saving a number to suit your design flow. From the examples above, choose a method to get a number and a method to store the number. Copy the appropriate procedures to a Tcl file and add commands to call the procedures. The following script framework shows how your script should be written. Finally, add an assignment (described below) to your Quartus II Settings File (.qsf ) to allow the script to run automatically.

# Insert procedure to get a number here 
# Insert procedure to store the number here 
# This line accommodates script automation, described later 
foreach { flow project revision } $quartus(args) { 
   break 
} 
# Call procedure to get a number here 
# Do any number format conversion necessary 
# Call procedure to store the number here

Script Automation

Add the following line to your project's QSF to allow the script to run automatically before every compilation. Replace <script name > with the name of your Tcl file.

set_global_assignment -name PRE_FLOW_SCRIPT_FILE quartus_sh:<script name>

Refer to Automatic Script Execution for more information about the assignment and other ways to automatically run scripts.

Example

The following script example uses procedures from the following two examples:

# Gets SVN revision number for the specified file
proc get_subversion_revision { file_name } {

    global done

    # The maximum number of seconds to wait for the svn info
    # command to complete
    set timeout_seconds 30

    # The svn info command with filename that is run
    set cmd "svn info ${file_name}"

    # Attempt to get the version information.
    # If the command can't be run, return an error.
    # Otherwise set up a file event to process the command output.
    if { [catch {open "|$cmd"} input] } {
        return -code error $input
    } else {
        fileevent $input readable [list get_revision_info $input ]

        # Set up a timeout so that the process can't hang if the
        # repository is down.
        set timeout [after [ expr { $timeout_seconds * 1000 } ]  [list set done -1] ]

        # Don't continue until the revision number is found,
        # or the operation times out. Cancel the timeout anyway.
        vwait done
        after cancel $timeout
    }
}

# Helper procedure for the above procedure
proc get_revision_info { inp  } {

    global done revision_number

    if { [eof $inp] } {
        catch {close $inp}
        set done 1
    } elseif { $done } {
        gets $inp line
    } else {
        gets $inp line
        # Use a regular expression to match the line with the
        # revision number.
        if { [regexp {^Revision:\s+(\d+)\s*$} $line match revision_number] } {
            set done 1
        }
    }
}

# Creates a register bank in a verilog file with the specified hex value
proc generate_verilog { hex_value } {

    set num_digits [string length $hex_value]
    set bit_width [expr { 4 * $num_digits } ]
    set high_index [expr { $bit_width - 1 } ]
    set reset_value [string repeat "0" $num_digits]

    if { [catch {
        set fh [open "version_reg.v" w ]
        puts $fh "module version_reg (clock, reset, data_out);"
        puts $fh "    input clock;"
        puts $fh "    input reset;"
        puts $fh "    output \[$high_index:0\] data_out;"
        puts $fh "    reg \[$high_index:0\] data_out;"
        puts $fh "    always @ (posedge clock or negedge reset) begin"
        puts $fh "        if (!reset)"
        puts $fh "            data_out <= ${bit_width}'h${reset_value};"
        puts $fh "        else"
        puts $fh "            data_out <= ${bit_width}'h${hex_value};"
        puts $fh "    end"
        puts $fh "endmodule"
        close $fh
    } res ] } {
        return -code error $res
    } else {
        return 1
    }
}

# This line accommodates script automation
foreach { flow project revision } $quartus(args) { break }

set file_name ${project}.qpf
set done 0
set revision_number ""

# Call procedure to get file revision number and handle any errors
if { [catch { get_subversion_revision $file_name } msg] } {
   post_message -type critical_warning "Couldn't run command to get revision number. $msg" 
} else { 
   if { -1 == $done }  { 
      post_message -type critical_warning "Timeout getting revision number." 
   } elseif {[string equal "" $revision_number] } { 
      post_message -type critical_warning "Couldn't find revision number in output of svn info $file_name." 
   } else { 
      #  Call procedure to store the number 
       if { [catch { generate_verilog $revision_number } res] } { 
         post_message -type critical_warning \ "Couldn't generate Verilog file. $res" 
      } else { 
         post_message "Successfully updated version number to\ version 0x${revision_number}" 
      }
  } 
}

If you name the script update_version.tcl, you must add the following line to your QSF:

set_global_assignment -name PRE_FLOW_SCRIPT_FILE quartus_sh:update_version.tcl