Quartus® II Tcl 示例:自动版本号

author-image

作者

在 FPGA 设计中嵌入版本号或时间戳是很有用的。版本号或时间戳可避免当前在 FPGA 中编程的设计版本混淆不清。版本号必须在设计编译流程中自动更新才能发挥作用。此外,版本号必须存储在设计硬件中,例如内存或寄存器库中。

此 Tcl 示例介绍了生成版本号或时间戳的不同方法,以及将其存储在设计中的不同方法。它还展示了一个可用于创建 Tcl 脚本的脚本框架,此脚本可在每次编译设计时自动生成并存储版本号。最后,它展示了一个完整脚本的示例。

获取版本号

以下列表展示了如何生成版本号:

存储版本号

除了获取版本号之外,还必须将其写入设计文件。以下是如何存储版本号的示例:

脚本框架

可以根据设计流程将获取和保存版本号的方法合并在一起。从上面的示例中,选择获取版本号的方法和存储版本号的方法。将相应的程序复制到 Tcl 文件,并添加调用程序的命令。以下脚本框架展示了如何编写脚本。最后,将如下所示的赋值添加到 Quartus II 设置文件 (.qsf) 中,使脚本自动运行。

# 在此处插入获取版本号的程序
# 在此处插入存储版本号的程序
# 此行容纳脚本自动化,如下所示
foreach { flow project revision } $quartus(args) { 
   break 
} 
# 在此处调用获取版本号的程序
# 进行任何必要的版本号格式转换
# 在此处调用存储版本号的程序

脚本自动化

将以下行添加到项目的 QSF 中,使脚本在每次编译之前自动运行。用 Tcl 文件名替换 <script name>。

set_global_assignment -name PRE_FLOW_SCRIPT_FILE quartus_sh:<script name>

有关赋值和自动运行脚本的其它方法的详细信息,请参阅“脚本自动执行”

示例

以下脚本示例使用以下两个示例中的程序:

# 获取指定文件的 SVN 修订号
proc get_subversion_revision { file_name } {

    global done

    # 等待完成 svn info 命令的
    # 最大秒数
    set timeout_seconds 30

    # 包含已运行文件的文件名的 svn info 命令
    set cmd "svn info ${file_name}"

    # 尝试获取版本信息。
    # 如果无法运行此命令,则返回错误。
    # 否则,设置一个处理命令输出的文件事件。
    if { [catch {open "|$cmd"} input] } {
        return -code error $input
    } else {
        fileevent $input readable [list get_revision_info $input ]

        # 设置超时,使存储库停止运行时此程序
        # 无法挂起。
        set timeout [after [ expr { $timeout_seconds * 1000 } ]  [list set done -1] ]

        # 找到修订号或操作超时时
        # 再继续。无论哪种情况,取消超时。
        vwait done
        after cancel $timeout
    }
}

# 以上程序的帮助器程序
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
        # 使用正则表达式匹配修订号
        # 所在的行。
        if { [regexp {^Revision:\s+(\d+)\s*$} $line match revision_number] } {
            set done 1
        }
    }
}

# 在包含指定十六进制值的 Verilog 文件中创建一个寄存器库
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
    }
}

# 此行容纳脚本自动化
foreach { flow project revision } $quartus(args) { break }

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

# 调用获取文件修订号的程序,并处理任何错误
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 { 
      #  调用存储版本号的程序
       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}" 
      }
  } 
}

如果将脚本命名为 update_version.tcl,必须将以下行添加到 QSF 中:

set_global_assignment -name PRE_FLOW_SCRIPT_FILE quartus_sh:update_version.tcl