FPGA デザインにバージョンナンバーやタイムスタンプを埋め込んでおくと便利です。バージョンナンバーやタイムスタンプがあれば、どのバージョンのデザインが現在 FPGA にプログラムされているのか、混乱することはありません。この種のナンバーを有効に使うためには、デザイン・コンパイル・フロー内で自動更新を行う必要があります。また、ナンバーは、メモリーやレジスターバンクなどのデザイン・ハードウェアに格納する必要があります。
この Tcl の例では、バージョンナンバーやタイムスタンプを生成するさまざまな方法と、それをデザインに保存するさまざまな方法を説明します。そして、デザインをコンパイルするたびに、バージョンナンバーを自動的に生成して格納する Tcl スクリプトを作成するために使用可能なスクリプト・フレームワークを紹介します。最後に、完全なスクリプトの例を示します。
ナンバーの取得
以下のリストは、バージョンナンバーを生成する方法を示します。
- タイムスタンプの生成
- バージョン管理ソフトウェアのリビジョンナンバーの読み取り。この例では、SVN を使用します。
- HDL ファイルに格納されているナンバーのインクリメント
ナンバーの格納
ナンバーを取得するだけでなく、デザインファイルに書き込む必要があります。以下に、ナンバーの格納方法の例を示します。
スクリプト・フレームワーク
ナンバーの取得や保存の方法は、デザインフローに合わせて組み合わせることができます。上記の例から、ナンバーを取得する方法と、ナンバーを保存する方法を選択します。適切なプロシージャーを Tcl ファイルにコピーし、プロシージャーを呼び出すためのコマンドを追加します。以下のスクリプト・フレームワークは、スクリプトをどのように記述すべきかを示します。最後に、Quartus II 設定ファイル (.qsf) に、後述するアサインメントを追加して、スクリプトが自動的に実行されるようにします。
# ここにナンバーを取得するプロシージャーを挿入します # ここにナンバーを格納するプロシージャーを挿入します # この行は、後述するスクリプト・オートメーションに対応します foreach { flow project revision } $quartus(args) { break } # ここでナンバーを取得するプロシージャーを呼び出します # 必要なナンバー・フォーマット変換を行います # プロシージャーを呼び出して、ここにナンバーを格納します
スクリプト・オートメーション
プロジェクトの QSF に以下の行を追加して、毎回のコンパイルの前にスクリプトが自動的に実行されるようにします。<スクリプト名> を Tcl ファイルの名前に置き換えます。
set_global_assignment -name PRE_FLOW_SCRIPT_FILE quartus_sh:<スクリプト名>
アサインメントや、スクリプトを自動的に実行する他の方法については、自動スクリプト・エグゼキューションを参照してください。
例
次のスクリプト例では、以下の 2 つの例のプロシージャーを使用します。
- ナンバーの取得: バージョン管理ソフトウェアのリビジョンナンバーの読み取り
- ナンバーの格納: レジスターのバンクを備えた Verilog ファイル
# 指定したファイルの SVN リビジョンナンバーを取得します proc get_subversion_revision { file_name } { global done # svn 情報が表示されるまでの最大秒数 # 完了するコマンド set timeout_seconds 30 # svn 情報コマンドをファイル名を指定して実行します 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 } } } # 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 } } # この行は、スクリプト・オートメーションに対応します 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