OpenOCDのTcl

出典: Wikimura

ここでは、Tcl Crash Courseの内容を、自分が理解できるよう(かなり適当に)訳したもの書いておきます。省略もかなりあります。


目次

Tcl Crash Course

Tclスクリプトがどう動くかについての考え方を示す。 JIM-Tclを使って便利な事をするために理解したい、新しいコマンドを追加したいというOpenOCDユーザ向け。

ルール

  1. Tclではすべてが文字列ということを念頭に置くこと。
  2. Tclには制御フローはない、コマンドだけがある...FORループやIF文もあるが、制御フローではない。Tcl上ではあくまでコマンド。(制御フロー:プログラムの流れを変えるもの)
  3. Tclではすべての結果は文字列(結果がない=空の文字列)

Tcl Quoting Operators

Tclスクリプトでは、2つの重要な時期?がある。これらの違いは微妙らしい。それは、

  1. パース時間: Parsing time
  2. 評価時間: 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[]で説明している)

  1. コマンドライン引数をパースし、argcとargvを用意する
  2. argv[0]で与えられた名前を持つコマンドを、argcとargvを引数として実行する
  3. 以上を終わるまで繰り返す

コマンド[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つのパラメータ文字列を生成する。

  1. FORという文字列
  2. 開始文字列
  3. 判定式
  4. 「次へ」の文字列
  5. 本体の文字列
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]
  1. FILENAMEを引数としてfindコマンドが実行され、与えられたファイルのフルパスが文字列で返される
  2. findコマンドの結果を引数としてsourceコマンドが実行され、引数で指定されたファイルを読み込み+スクリプトとして実行する

format command

あちこちで使う:
set x 6
set y 7
puts [format "The answer: %d" [expr $x * $y]]

Tclではprintfに似たコマンドはないため、代わりにformatがある。これはsprintfに似ている。例では、

  1. 変数xに6を代入
  2. 変数yに7を代入
  3. 内側の[大括弧]が実行される
    1. 変数x,yを書けた結果が文字列になる
  4. 外側の[大括弧]が実行される
    1. 1つ目の引数に書式、2つ目の引数に書式に入れる文字列を引数としてformatコマンドが実行される
  5. 文字列としてformatした結果が表示される


Body or Inlined Text

ターゲットスクリプトで良く使う:
proc someproc {} {
    ... multiple lines of stuff ...
}
$_TARGETNAME configure -event FOO someproc
以下いくつか例があったが省略
  1. $_TARGETNAMEはOpenOCDの変数。$_TARGETNAME最後に生成されたターゲット(targetコマンドで設定された、その時点での最新のtargetということ)を表す。パースによってこれは文字列に置き換えられる。
  2. -eventへの2つ目のパラメータ「someproc」はTCLBODYと言い、以下のような例がある。
    1. TCLBODYは単純な文字列で、proc名になる
    2. TCLBODYはセミコロンで区切られたいくつかの単純なコマンド
    3. TCLBODYは複数行の{中括弧}で括られた文字列
    4. TCLBODYは変数を含む文字列で、展開される
  3. ターゲットイベント(?)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が生成されるのかもしれない。

関連

個人用ツール