OpenOCDのTcl
出典: Wikimura
ここでは、Tcl Crash Courseの内容を、自分が理解できるよう(かなり適当に)訳したもの書いておきます。省略もかなりあります。
目次 |
Tcl Crash Course
Tclスクリプトがどう動くかについての考え方を示す。 JIM-Tclを使って便利な事をするために理解したい、新しいコマンドを追加したいというOpenOCDユーザ向け。
ルール
- Tclではすべてが文字列ということを念頭に置くこと。
- Tclには制御フローはない、コマンドだけがある...FORループやIF文もあるが、制御フローではない。Tcl上ではあくまでコマンド。(制御フロー:プログラムの流れを変えるもの)
- Tclではすべての結果は文字列(結果がない=空の文字列)
Tcl Quoting Operators
Tclスクリプトでは、2つの重要な時期?がある。これらの違いは微妙らしい。それは、
- パース時間: Parsing time
- 評価時間: Evaluating time
これらは、「括られたもの」がどのように機能するかということ。 Tclでは「括り」の構造が3種類用意されていて、[大括弧]、{中括弧}、"二重引用符"がある。
なお以降に出てくる、ドル記号($)から始まるものは変数。変数はsetコマンドで値を与えられる。 VARNAMEという変数に、VALUEという文字列を割り当てるなら、以下のように書く。
set VARNAME VALUE
[大括弧]
[大括弧]コマンドは置換。UNIXシェルでいう[back-ticks]に良く似ている。 この結果はちょうど1つの文字列となる。以下の2つのコマンドは等価。
# bash example X=`date` echo "The Date is: $X" # Tcl example set X [date] puts "The Date is: $X" コマンドの意味: dateコマンドの結果を変数Xに代入する 「The Date is: ''変数Xの内容''」を表示する
ちなみに[back-ticks]とは、シェル上で以下のように打つこと。この場合、back-ticked-commandの結果がsome-commandの引数として与えられる。
>some-command `back-ticked-command`
"二重引用符"
"二重引用符"は単なる文字列。変数や[大括弧]はその場で展開され、結果は1つの文字列になる。
set x "Dinner" puts "It is now \"[date]\", $x is in 1 hour" コマンドの意味: 変数xに文字列Dinnerを代入 「It is now "dateコマンドの結果", 変数xの内容 in 1 hour」を表示する
{中括弧}
{中括弧}は変数や[大括弧]をパースし、展開や実行は行わない。Bashでいう'引用符'に似ているが、ネストできるという点で異なる。
良く分からないけど...プログラムのブロック的なもの? 括弧の内側から実行されていくことになるのでは?以下のようになるのではないか...
set x { proc1 { proc2 "arg"}}
{ {文字列argを引数としてproc2を実行した結果}を引数としてproc1を実行した結果}を変数Xに代入する
コマンド実行
制御文はない。制御分に似たコマンドがある。 コマンドは以下のように実行される。(C言語でいうところの、コマンドライン引数の数argcと、コマンドライン引数のポインタの配列argv[]で説明している)
- コマンドライン引数をパースし、argcとargvを用意する
- argv[0]で与えられた名前を持つコマンドを、argcとargvを引数として実行する
- 以上を終わるまで繰り返す
コマンド[proc]は、パースすると3つのコマンドライン上でのパラメータになる。コマンド名、パラメータリスト、機能の本体という3つ。イメージとしては、PROCというコマンドは、パラメータのLISTと本体であるBODYをセットで参照テーブル上にあるということになるらしい。 疑似コードでは以下のようになる。
for(;;){
ReadAndParse( &argc, &argv );
cmdPtr = LookupCommand( argv[0] );
(*cmdPtr->Execute)( argc, argv );
}
FORコマンド
FORコマンドはいちばん興味深い。 TclではFORコマンドはCのように実装される。しかし、全く別物。
FORコマンドを含む文字列がパースされると、パーサは5つのパラメータ文字列を生成する。
- FORという文字列
- 開始文字列
- 判定式
- 「次へ」の文字列
- 本体の文字列
set sum 0
for {set i 1} {$i <= 10} {incr i} {
set sum [expr $sum + $i]
}
puts $sum
パース結果:
for: for
開始: set i 1
判定: $i <= 10
次へ: incr i
本体: set sum [expr $sum $i]
意味:
変数sumに文字列0を代入。
for
開始:変数iに文字列1を代入
判定:変数iが10以下
次へ:変数iを1つ増やす
本体:変数sumに、変数sumと変数iを加えたものを入れる
Cでは:
sum = 0;
for( i=1; i<=10; i++){
sum = sum + 1;
}
OpenOCD Tcl Usage
source and find commands
たくさん設定ファイルがあるとき: source [find FILENAME]
- FILENAMEを引数としてfindコマンドが実行され、与えられたファイルのフルパスが文字列で返される
- findコマンドの結果を引数としてsourceコマンドが実行され、引数で指定されたファイルを読み込み+スクリプトとして実行する
format command
あちこちで使う: set x 6 set y 7 puts [format "The answer: %d" [expr $x * $y]]
Tclではprintfに似たコマンドはないため、代わりにformatがある。これはsprintfに似ている。例では、
- 変数xに6を代入
- 変数yに7を代入
- 内側の[大括弧]が実行される
- 変数x,yを書けた結果が文字列になる
- 外側の[大括弧]が実行される
- 1つ目の引数に書式、2つ目の引数に書式に入れる文字列を引数としてformatコマンドが実行される
- 文字列としてformatした結果が表示される
Body or Inlined Text
ターゲットスクリプトで良く使う:
proc someproc {} {
... multiple lines of stuff ...
}
$_TARGETNAME configure -event FOO someproc
以下いくつか例があったが省略
- $_TARGETNAMEはOpenOCDの変数。$_TARGETNAME最後に生成されたターゲット(targetコマンドで設定された、その時点での最新のtargetということ)を表す。パースによってこれは文字列に置き換えられる。
- -eventへの2つ目のパラメータ「someproc」はTCLBODYと言い、以下のような例がある。
- TCLBODYは単純な文字列で、proc名になる
- TCLBODYはセミコロンで区切られたいくつかの単純なコマンド
- TCLBODYは複数行の{中括弧}で括られた文字列
- TCLBODYは変数を含む文字列で、展開される
- ターゲットイベント(?)FOOが、TCLBODYが評価された後行われる。
Global Variables
自身のprocを書いているときに使うはず:
proc myproc { } {
set y 0 #Local variable Y
global x #Global variable X
puts [format "X=%d, Y=%d" $x $y]
PROCの中では、グローバル変数にアクセスする際に必ずGLOBALを使わないといけない。
Other Tcl Hacks
Dynamic variable creation
動的に変数の塊を作る。
for { set x 0 } { $x < 32 } { set x [expr $x + 1]} {
# Create var name
set vn [format "BIT%d" $x]
# Make it a global
global $vn
# Set it.
set $vn [expr (1 << $x)]
}
Dynamic proc/command creation
# One "X" function - 5 uart functions.
foreach who {A B C D E}
proc [format "show_uart%c" $who] { } "show_UARTx $who"
}
メモ
STM32用の設定ファイル[stm32.cfg]の内部を見ると、Low/Mid/High-densityごとに分岐するような記述がある。 openocdに与える際に引数をセットしてやれば、ターゲットに応じたTAPが生成されるのかもしれない。

