TCML

Timing Chart Markup Language — テキストでタイミングチャートを記述する簡易言語

English

拡張子 .tc

1.概要

TCML (Timing Chart Markup Language) は、タイミングチャートをテキストで記述するための言語です。シンプルな ASCII 風記法で、信号の Low / High / バス / 不定値・遷移・矢印・クロック展開などを表現できます。

本実装は以下の実装・定義を参考にさせていただいております。この場での謝辞を述べさせていただきます。ありがとうございます。

詳細かつ美しい図を生成するには WaveDrom を利用すべきです。本ツールで生成される図を DataSheet に記載すべきではありません。

しかし、WaveDrom の記法は難しく、なんとなく考えながら記述するには不向きです。東北学院大学の熊谷先生、筑波大学の竹内先生らが考案してきたタイミングチャートのほうが思考段階において、圧倒的に楽です。

実際に著者が利用していた際に、こんなことが書けたらな、と数年前に思っていたことを実現するために GW を作って作られました。なお、著者本人はもうこの手の設計をしてないので使う予定がないんですがね。

2.TCMLについて

左側に信号名を、右側にレベル文字列を空白で区切って記述します。

_ は Low、~ は High、= は Bus、? は不定値を表します。下記のコードを tchart CLI に渡すと右の SVG が生成されます。

下記のコードを tchart CLI に渡すと右の SVG が生成されます。

@step 8
@slant 2
@bgcolor0 #f8f8ff
@bgcolor1 #f0f0f0

Clock   _~_~_~_~_~_~
"Data
-Data"  =<D0>====X<D1>====X<D2>====
Enable  ____~~~~____
Output  _______~~~~~____
<D0><D1><D2>ClockData-DataEnableOutput

3.行の種類

種類先頭文字説明
コメント行#無視されます。
パラメータ行@パラメータ設定 / 行ディレクティブ (@title / @skip / @clock / @->) / 信号属性 (@signal) のいずれかです。
文字書き込み行%指定座標に文字列を配置します。
タイミング記述行その他信号名とレベル文字列です。

4.信号名

有効な UTF-8 文字列を指定します。制御文字を含めることはできません (\n のみ例外)。空文字も不可です。

複数行信号名

信号名を " で囲むと、内部に改行を含めることができます。

"Data
Bus" =<D0>====X<D1>====
  • 開始の " は行頭にある必要があります。
  • 閉じの " の後には空白を挟んでレベル文字列が続きます。

エスケープ ("..." 内のみ)

シーケンス意味
\"リテラルの "
\n改行
\\リテラルの \

5.レベル記号

記号意味形状
_Low下端の単線
~High上端の単線
-HiZ中央の破線
=Bus上下 2 本のレール
?Don't care塗り潰し + 内部に直前レベル位置の線

@fontsize 14
@step 12
@slant 2

Low       ________
High      ~~~~~~~~
HiZ       --------
Bus       =<A>====
DontCare  ___?????
<A>LowHighHiZBusDontCare

同じ記号が連続する場合 (__, ~~, ?? 等)、parser は 1 つの区間としてマージします。

6.補助記号

記号意味x 進行
:Gap (1 単位の空白、信号連続性を断絶)step
XBus 値変化 (BusCross)step (cross 部 slant + body 部 step - slant、または信号行頭等で cross 省略時は body のみ step)
?Don't care マーカー (周辺 bus 区間が不定値)0
|縦線 (ガイド線)0
[ / ]ハイライト開始/終了0
@{name} / @Nアンカー0

X は cross 遷移 + body (Bus 1 単位、新値) の 2 部品。X 全体で level char 1 個ぶん = step 幅を占め、cross 部が slant、body 部が step - slant になります。前に bus 信号が無い場合 (信号行頭等) は cross が省略され body のみ (幅 step)。

? は幅 0 のマーカー。周辺の連続レベル区間を不定値領域として塗ります。

@fontsize 14
@step 12
@slant 2

Gap        ____:____
BusX       =<A>====X<B>====
Guide      _~__|__~_
Highlight  __[~~~~]__
<A><B>GapBusXGuideHighlight

7.不定値 ?

領域内の線位置は 直前のレベルから決定されます (parser 段階で解決)。

直前アンカー領域内の線
_ Low下端
~ High上端
- HiZ中央
= Busbus 包絡 (上下 2 本)
Xbus 包絡

Bus 内 ? の塗り形状

Bus 文脈の ? (=?= 等) は、前後の波形境界に応じた多角形で塗られます。signal_box の全高にはみ出すことはありません。

@fontsize 14
@step 12
@slant 3

@title "DontCareAlongBus shapes"

# `@dontcare_color` を途中で書き換えることでハッチ色は行ごとに切り替えられる。
# (デフォルト #bbb / #c00 / #06c / #080 の 4 色を順に使う例)

# デフォルト色 (#bbb)
=?=         ====?====

@dontcare_color #c00
# 両側 Low: /=\ 形状 (赤)
_=?=_       ____====?====____

# 両側 High: \=/ 形状 (赤)
~=?=~       ~~~~====?====~~~~

@dontcare_color #06c
# 片側のみ Low (左斜辺、右垂直) (青)
_=?=        ____====?====

# 片側のみ Low (左垂直、右斜辺) (青)
=?=_        ====?====____

@dontcare_color #080
# 片側 High / 片側 Low (混在) (緑)
~=?=_       ~~~~====?====____

# HiZ 絡み: 左 HiZ、右 Bus continue (緑)
-=?=        ----====?====

# Bus + ラベル付き (緑)
=?=L        ==<A>==?====

# Bus (緑)
=?=X        ==X==?==X==
<A>=?=_=?=_~=?=~_=?==?=_~=?=_-=?==?=L=?=XDontCareAlongBus shapes

? のエラー条件

  • 信号行が ? で始まる、あるいは : | [ ] @{...} @N のような透過要素のみが先行する場合は ParseError::DontCareWithoutAnchor となります。
  • 例: foo ?==bar ???baz ?_~qux :?_~quux @{a}?_~ はすべてエラーです。

X / X? パターン

パターン解釈
=X=Bus(1) + X(cross + body, 新値) + Bus(1)
=X?Bus(1) + X(cross + body) + ? (X body が dontcare)
=X?=? 領域 = X body + 後 = (2 単位ぶん)
=?X=? 領域 = 前 = (1 単位)、X body + 後 = は新値 Bus
=X?X=? 領域 = X1 body 1 単位、polygon は X1 cross 中点 〜 X2 cross 中点 (六角形)
~X_High + BusOpen + Bus(1, X body) + BusClose + Low (X 隣接 non-bus でも valid)
XXXX信号行頭の連続 X。1 つ目は cross 省略、2 つ目以降は通常の BusCross
?X=エラー (先頭 ?)

8.レベル文字列中のラベル <...>

レベル区間や遷移にテキストラベルを付与します。

@step 10
@slant 2

BusLabel   =<A>====X<B>====X<C>====
LowLabel   ____<L>____
HighLabel  ~~~~<H>~~~~
HiZLabel   ----<Z>----
<A><B><C><L><H><Z>BusLabelLowLabelHighLabelHiZLabel
  • ラベル文字列に制御文字 (\n を除く) は含められません。
  • < 自体を表示したい場合は \<>\>\\\ でエスケープします。

9.アンカー @{name} と矢印 @->

波形上の特定の点を アンカー として 0 幅マーカーで記録し、@-> 行で 2 つのアンカーを矢印として結びます。アンカー単独では描画されず、矢印の端点として参照されたときに線が引かれます。

最小例

@fontsize 14
@step 10
@slant 2

Req   ___@{s}~~~~~~
Ack   ________@{a}~~~

@-> (@{s}, @{a}, red) request
ReqAckrequest

アンカーの規則

  • @{name} または @N (1 以上の整数) で指定します。0 幅マーカーで x 進行に影響しません。
  • 名前付きと番号付きは別の名前空間として扱われます (@{1}@1 は別物です)。
  • 同一 ID を重複定義すると ParseError::DuplicateAnchor となります。
  • AnchorName の文字種は [A-Za-z_][A-Za-z0-9_-]* です。
  • ? の解決ルックアップではアンカーは透過しません (アンカー単独では ? の直前レベルにはなりません)。

矢印の書式

@-> (<始端>, <終端> [, <属性>, ...]) [<テキスト>]

カテゴリ判別
red, #f0f, #ff8800Color::parse 成功
太さ2, 2px, 1.5px数値 (単位 px 省略可)
線種solid, dashed, dottedキーワード
矢印頭head=end, head=both, head=nonehead= プレフィクス

デフォルトは色 = signal_color、太さ = 1px、線種 = solid、矢印頭 = end です。属性順は不問で、同じカテゴリの重複は ParseError::DuplicateArrowAttribute となります。

色・太さ・線種・前方参照を組み合わせた例

@step 10
@slant 2
@bgcolor0 #f8f8ff
@bgcolor1 #f0f0f0

Req     ___@{s}~~~~@{e}___
Ack     _______@{a}~~~~

@-> (@{s}, @{a}) request
@-> (@{e}, @{a}, dashed) ack

Bus     =<A>====@1X<B>====@2X<C>====@3
Flag    ____@4~~~~@5___

@-> (@1, @4, red, 2px) A
@-> (@2, @5, blue, head=both) B
@-> (@3, @4, green, dotted) forward ref

Data    __@{d1}~~~~@{d2}___
Out     ___@{o1}~~~~@{o2}__

@-> (@{d1}, @{o1}, #ff8800, solid) start
@-> (@{d2}, @{o2}, dashed, head=none) end
<A><B><C>ReqAckBusFlagDataOutrequestackABforward refstartend
  • 前方参照が可能です。@-> 行は TCML 中の任意の位置に書くことができます。
  • ラベル等のスタイル (font, signal_color 等) は @-> 行を記述した位置のローカル設定が適用されます。
  • 未定義のアンカーを参照すると ParseError::UndefinedAnchor となります。
  • 矢印同士の重なり回避は行いません (作者の責任となります)。

10.パラメータ

書式は @<パラメータ名> <値> です。名前は大文字小文字を区別せず、ハイフンとアンダースコアは等価に扱われます (@fontsize@font-size@FONT_SIZE はすべて同じです)。

グローバルパラメータ (途中変更不可)

名前デフォルト説明
fontsize14フォントサイズ (px)。レイアウトの基準となります。
lineheight1.2波形高さの係数 (= fontsize × lineheight)。
capwidth0信号名欄の幅 (px)。0 のときは自動計算されます。
namepad8信号名右端と波形左端の余白 (px)。
scale1.0SVG 全体のスケール係数。
page-margin10チャート四方の固定余白 (px)。
bgcolor0none偶数行の背景色。
bgcolor1none奇数行の背景色。

ローカルパラメータ (途中変更可、それ以降に適用)

名前デフォルト説明
step10level char 1 個ぶんの x 進行幅 (px)。直前に遷移ありの場合、その遷移は本 step 幅の先頭 slant 部分として描画される。step <= slant はパースエラー。
slant2遷移幅 (px)。0 で垂直エッジになります。SingleEdge / BusOpen / BusClose / BusCross すべてに適用。
h_space4.0信号行の上下余白合計 (px)。旧名 signal_gap も同義として受理。
fontsans-serifフォントファミリー (空白入りは " で囲みます。カンマ区切りでフォールバック順を指定できます)。
signal_colorblack信号線の色。
signal_width1信号線の幅 (px)。
guide_colorred縦線の色。
guide_width0.6縦線の幅 (px)。
bgnone次の 1 行の背景色 (ローカル上書き)。
highlight_stylefill="#ff8" stroke="none"ハイライト矩形のスタイル。
dontcare_color#bbb? ハッチ線色。@dontcare_color #c00 のように単一の色値を指定するとそれ以降の行の色が切り替わる (途中で再宣言可)。
titlealigncenter@title の横揃え (center / left / right)。
clockmark_position0.5クロック三角形マーカーの頂点位置 (線方向比 0.0..=1.0)。
clockmark_height5クロック三角形マーカーの高さ (px)。
clockmark_width4クロック三角形マーカーの底辺の幅 (px)。
clockmark_colorsignal_color 継承クロック三角形マーカーの塗り色。未指定なら現在有効な signal_color を継承する。
overline_gap2信号名上線とテキスト cap-top の隙間 (px)。
overline_thickness1信号名上線の太さ (px)。

11.背景色

偶奇で塗り分ける @bgcolor0 / @bgcolor1 (グローバル) と、次の 1 行だけを上書きする @bg (ローカル) があります。

@fontsize 14
@step 10
@bgcolor0 #eef6ff
@bgcolor1 #fff4ee

A    _~_~_~_~
B    ~_~_~_~_

@bg #ffe4cc
Local _~_~_~_~

After _~_~_~_~
ABLocalAfter
  • @bg は次の 1 行 (Signal / Skip / Title) を消費したらリセットされます (@bg none で破棄もできます)。
  • @bg 指定行では bgcolor0/1 を重ね描きしません。
  • Skip 行と Title 行は bgcolor0/1 の偶奇カウントから除外されます。

@highlight_style / @dontcare_color

@highlight_style fill="#8f8" stroke="green" stroke-width="1"
@dontcare_color #c00

@highlight_style は SVG 属性を key="value" 形式で空白区切り指定します。@dontcare_color は単一の色値 (@bgcolor0 等と同じ書式) を取り、ハッチ線色を切り替えます。

12.@skip — 空白行

@step 10
@slant 2
@bgcolor0 #f8f8ff
@bgcolor1 #f0f0f0

Clock   _~_~_~_~_~_~

@skip(1)

Data    =<A>====X<B>====

@skip(2)

Control ____~~~~____

@skip(0.5)

Flag    ~~____~~____

@skip(20px)

Out     _~~~~___~~~~
<A><B>ClockDataControlFlagOut
  • 単位なしの数値は lh (line-height 単位) として解釈されます。
  • 数値 + px でピクセル指定もできます。
  • 負値やパース不能のときは ParseError::InvalidSkipAmount となります。
  • 0 は許容しますが SkipRow は生成されません。

13.@title — タイトル行

@step 10
@slant 2
@bgcolor0 #f8f8ff
@bgcolor1 #f0f0f0

@title "Default Center Title"

A     _~_~_~_~

@titlealign left
@title "Left Aligned"

B     _~_~_~_~

@titlealign right
@title "Right Aligned"

C     _~_~_~_~

@titlealign center
@title "Back to Center"

D     _~_~_~_~
ABCDDefault Center TitleLeft AlignedRight AlignedBack to Center
  • 引数文字列をタイトル行として描画します。複数行は "..." で引用します (信号名と同じエスケープ規則です)。
  • 1 ファイル中に複数回出現させることができます。
  • Title 行は bgcolor0/1 の偶奇カウントから除外されます。
  • @titlealigncenter / left / right から選び、設定後に出現するすべての @title に適用されます。

14.@clock — クロック自動展開

直後の信号行をクロック信号として展開します。本体が空または部分的なら、最後の状態から繰り返し展開し、立ち上がり / 立ち下がりに三角形マーカーを自動挿入します。

@fontsize 14
@step 10
@slant 2

@clock(pos)
ClkPos  _~_~_~

@clock(neg)
ClkNeg  _~_~_~

@clock(both)
ClkBoth _~_~_~

@clock(pos, _=2, ~=1)
ClkWide
ClkPosClkNegClkBothClkWide

書式: @clock(<edge> [, _=<n>] [, ~=<n>] [, start=<low|high>] [, mark_position=<f32>] [, mark_height=<px>] [, mark_width=<px>] [, mark_color=<color>])

属性説明
edgepos / neg / both / none三角形マーカーの対象 (必須)。
_=<n>正整数Low の単位時間数 (省略時 1)。
~=<n>正整数High の単位時間数 (省略時 1)。
startlow / high開始相 (省略時 low)。
mark_position0.0..=1.0三角形の頂点位置。
mark_height正値三角形の高さ。
mark_width正値三角形の底辺の幅。
mark_color塗り色 (省略時 signal_color を継承)。
  • 属性順は不問です。属性キー名は大文字小文字を区別せず、-_ は等価に扱われます。
  • @-> 矢印とは 完全に別系統 です。clock 由来のマーカーは @-> の Arrow には混入しません。

15.@signal — 信号属性

直後の信号行に属性を適用します (1 回限りで、適用後にリセットされます)。現在は overline のみ提供しています。

@step 10
@slant 2
@bgcolor0 #f8f8ff
@bgcolor1 #f0f0f0

@signal(overline)
nReset    ~~~~__~~~~

@signal(overline)
nWrite    ~~__~~__~~

Enable    ____~~~~____

@signal(overline)
"nChip
Enable"   ~~~~____~~~~

Out       __~~~~____~~
nResetnWriteEnablenChipEnableOut
  • overline: 信号名に上線 (負論理表記) を引きます。複数行信号名では 最上行のみに 1 本、幅は全行のうち最長行に合わせます。
  • 位置・太さは @overline_gap / @overline_thickness で制御します。SVG 出力は text-decoration 属性ではなく独立した <line> 要素として描画されます。

16.% — 文字書き込み行

% <x座標> <y座標> <文字列>

指定座標 (px) にテキストを overlay として配置します。座標はチャート左上を原点とします。

17.key=value 規則

TCML 中で = を使う属性記述 (@clock(...) の各オプション、@->head=@highlight_style 等) はすべて同じ規則に従います。

  • = の前後の空白は任意です。key=value / key =value / key= value / key = value はすべて等価です。
  • キー名・値はそれぞれ両端の空白を除いた上で評価されます。
  • 値に空白を含めたい場合は "..." で囲みます。

18.エラー一覧

エラーは行番号・列番号付きでレポートされます。

エラー原因
ParseError::DontCareWithoutAnchor? の直前にレベルアンカーが存在しない。
ParseError::DanglingBusTransitionX の前後が Bus / ? でない、または X が単独で先頭にある。
ParseError::InvalidSkipAmount@skip の引数が負値またはパース不能。
ParseError::DuplicateAnchor同一 AnchorId が複数回定義された。
ParseError::UndefinedAnchor@-> が未定義のアンカーを参照した。
ParseError::DuplicateArrowAttribute@-> の同カテゴリ属性が複数指定された。
ParseError::UnknownArrowAttribute@-> で判別不能な属性トークン。
ParseError::UnknownParameter未知の @<name> パラメータ。
ParseError::InvalidColorColor::parse 失敗。
ParseError::InvalidSignalName信号名が制御文字を含む、または空。
ParseError::InvalidLevelChar未知のレベル文字。
ParseError::UnclosedHighlight[] で閉じていない。
ParseError::UnopenedHighlightEnd] に対応する [ がない。
ParseError::UnclosedQuote"..." が閉じていない。
ParseError::UnclosedLabel<...> が閉じていない。
ParseError::ClockBodyConflict@clock 直後の信号行に展開と矛盾する内容がある。

19.WaveDrom への引き継ぎ

本ツール (tchart) は、テキスト編集中にタイミングチャートを思考とともに書き留めるための簡便な道具です。仕上げの図をデータシート・発表資料・論文といった公の場に載せる段階では、本格的なタイミングチャート描画ツールである WaveDrom をお使いください。本ツールは皆さまの最終的な発表の場までご一緒できる真の仲間ではありません。

ここまでの作業が無駄になることはありません。tchart wavedrom サブコマンドで .tc ファイルを WaveJSON 形式に変換できます。完全互換ではなく、WaveDrom が描画可能な要素 (信号レベル・Bus データ・クロック・アンカー/矢印など) のみを近似マッピングし、WaveDrom 側に対応物のないスタイル系 (背景色・フォント・上線・ハイライト等) は黙って落とします。TCML での思考をそのまま WaveDrom の世界に持ち込んで、続きの旅をお進めください。

tchart wavedrom chart.tc            # → chart.json
tchart wavedrom chart.tc -o out.json

変換例として、以下に 3 ケースを掲載しています。各ケースで TCML 入力・tchart svg による TCML 描画結果・tchart wavedrom による WaveJSON 出力・wavedrom-cli による WaveDrom 描画結果を並べて比較できるようにしています。WaveJSON 生成元は tchart-core/examples/wavedrom_help_demo.rs です。

ケース 1: Gap (:) で連続性を切る

TCML の Gap : は 1 unit 空白で連続性を断絶します。WaveDrom の | (直前 level を 1 unit 延長しつつ視覚的 break を描く) にマップされます。完全に同じ絵にはなりませんが、連続性が切れるというセマンティクスは保存されます。

TCML 入力

@title 連続性の断絶
sig1   ~_~_:~_~_
sig2   ====:====

TCML 描画結果 (tchart svg)

@title 連続性の断絶 sig1 ~_~_:~_~_ sig2 ====:==== sig1sig2連続性の断絶

WaveJSON 出力 (tchart wavedrom)

{
  "head": { "text": "連続性の断絶" },
  "signal": [
    { "name": "sig1", "wave": "1010|1010" },
    { "name": "sig2", "wave": "=...|=..." }
  ]
}

WaveDrom 描画結果 (wavedrom-cli)

連続性の断絶sig1sig2

ケース 2: バス値の切替 (X = BusCross)

X を挟むとパーサがバス区間をそこで分割するので、WaveDrom 出力でも別セグメント (=) になり、それぞれに対応する data エントリが生成されます。X の位置で値が変わる典型的なバスのタイミングを描けます。

TCML 入力

@title バス値の切替
clk    ~_~_~_~_
data   ==A=X=B=X=C

TCML 描画結果 (tchart svg)

@title バス値の切替 clk ~_~_~_~_ data ==A=X=B=X=C ABCclkdataバス値の切替

WaveJSON 出力 (tchart wavedrom)

{
  "head": { "text": "バス値の切替" },
  "signal": [
    { "name": "clk",  "wave": "10101010" },
    { "name": "data", "wave": "=..=..=.",
      "data": ["A", "B", "C"] }
  ]
}

WaveDrom 描画結果 (wavedrom-cli)

バス値の切替clkdataABC

ケース 3: アンカー @{name} と信号間の矢印 @->

波形中に埋め込んだ 0 幅アンカー (@{request} 等) を始端・終端として @-> で矢印を引きます。複数アンカーを別の信号に置けば、信号間をまたぐ依存関係 (例: request → ack → complete) を矢印で示せます。WaveDrom 出力では各信号の node プロパティ (波形と同じ長さの文字列、アンカー位置に文字、それ以外は .) と top-level edge 配列にマップされます。

変換時に欠落する情報があります。 WaveDrom の node 識別子は 1 文字 (az, AZ の最大 52 個) なので、TCML 側で付けた英字交じりのアンカー名 (例: @{request}) は 出現順に通し番号化された単一文字 (a など) に置き換わり、元の名前は復元できません。アンカー数が 52 を超える場合は超過分の矢印が落とされ、stderr に警告が 1 行出力されます。矢印の色・太さ・線種の細部も WaveDrom 側に対応物がないため落とされ、矢印の存在・端点・ラベルのみが保存されます。

TCML 入力

@title 信号間にまたがる矢印
clk    ~_~_~_~_
req    _@{request}~~~~~~_
ack    ___@{ack_received}~~~~_
done   ______@{complete}~_
@-> (@{request}, @{ack_received}) ack
@-> (@{ack_received}, @{complete}) done

TCML 描画結果 (tchart svg)

@title 信号間にまたがる矢印 clk ~_~_~_~_ req _@{request}~~~~~~_ ack ___@{ack_received}~~~~_ done ______@{complete}~_ @-> (@{request}, @{ack_received}) ack @-> (@{ack_received}, @{complete}) done clkreqackdone信号間にまたがる矢印ackdone

WaveJSON 出力 (tchart wavedrom)

{
  "head": { "text": "信号間にまたがる矢印" },
  "signal": [
    { "name": "clk",  "wave": "10101010" },
    { "name": "req",  "wave": "01.....0", "node": ".a......" },
    { "name": "ack",  "wave": "0..1...0", "node": "...b...." },
    { "name": "done", "wave": "0.....10", "node": "......c." }
  ],
  "edge": ["a->b ack", "b->c done"]
}

WaveDrom 描画結果 (wavedrom-cli)

信号間にまたがる矢印clkreqackdoneackdoneabc