Command Tutorial & Reference

Explore svw commands by task. Each entry includes a ready-to-use example and, where available, a recorded demonstration.

Waveform Files

:open

<file…> - open waveform file(s); multiple files join the open-file list (:files / :active)

Try it

printf 'addall\nopen tutorial_demo_b.vcd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :open demo

:files

- list open files (:active to switch)

Try it

printf 'open tutorial_demo_b.vcd capture_03.fsdb capture_04.fsdb capture_05.fsdb capture_06.fsdb capture_07.fsdb capture_08.fsdb capture_09.fsdb capture_10.fsdb capture_11.fsdb capture_12.fsdb capture_13.fsdb capture_14.fsdb capture_15.fsdb capture_16.fsdb capture_17.fsdb capture_18.fsdb capture_19.fsdb capture_20.fsdb capture_21.fsdb capture_22.fsdb capture_23.fsdb capture_24.fsdb capture_25.fsdb capture_26.fsdb capture_27.fsdb capture_28.fsdb capture_29.fsdb capture_30.fsdb capture_31.fsdb capture_32.fsdb capture_33.fsdb capture_34.fsdb capture_35.fsdb capture_36.fsdb capture_37.fsdb capture_38.fsdb capture_39.fsdb capture_40.fsdb capture_41.fsdb\nfiles\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :files demo

:active

<n> - switch the active file by number (Tab lazily lists open files); selecting the current file is not an undo step

Try it

printf 'open tutorial_demo_b.vcd\nactive \n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :active demo

:reload

- reload the current waveform file

Try it

printf 'addall\nreload\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :reload demo

:refresh

- refresh a growing KBX capture without reopening or losing the view; the external data snapshot is explicit and does not consume Ctrl+Z model history

Try it

printf 'addall\nrefresh\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :refresh demo

:warnings

- list the parse warnings of the open waveform (VCD parser diagnostics with line numbers) in the report overlay

Try it

printf 'warnings\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_warnings.vcd"
recorded demo — :warnings demo

Signal Discovery

:add

<path…> - add signals to the display list ('*' glob)

Try it

printf 'add top.clk top.instruction_bus\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :add demo

:importlist

<file> - transactionally add signal paths and '*' globs from a bounded text file; blank lines and '#' comments are ignored

Try it

printf 'delall\nimportlist tutorial_signals.txt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :importlist demo

:addall

[scope] - add all signals under scope (default: root)

Try it

printf 'addall\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :addall demo

:find

<regexp> [in <scope>] - add all matching signals (POSIX regex, smart-case / :set ic)

Try it

printf 'delall\nfind pipeline_stage_0[0-5]\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :find demo

:scope

<path> [<t0> <t1>] - open a scope's signals in the Telescope picker without changing hierarchy state or consuming Ctrl+Z history; preview the current wave-panel interval by default or an explicit time range

Try it

printf 'addall\ngoto 400ns\nzoom in\nscope top\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :scope demo

:scope range

scope <path> <t0> <t1>

Try it

printf 'scope top 200ns 500ns\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :scope range demo

:tree

[scope] - lazy design browser: optional [scope] becomes the subtree root (Tab completes scope paths); only the first visible page is loaded until j/k reaches its frontier, Enter folds/expands a scope or adds/locates and flashes a signal, gg/G top/bottom, q/Esc close; headless prints the selected subtree as indented text (+ collapsed / - expanded)

Try it

printf 'delall\npanelwidth 64\ntree top.cpu\ntree top.cpu\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tree demo

:tree no-argument

[scope] - lazy design browser: optional [scope] becomes the subtree root (Tab completes scope paths); only the first visible page is loaded until j/k reaches its frontier, Enter folds/expands a scope or adds/locates and flashes a signal, gg/G top/bottom, q/Esc close; headless prints the selected subtree as indented text (+ collapsed / - expanded)

Try it

printf 'delall\ntree\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tree no-argument demo

Signal Panel

:delall

- delete all rows from the display list

Try it

printf 'addall\ndelall\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delall demo

:del

{id…} - delete display rows (1-based index, name, or g:group for the whole group)

Try it

printf 'addall\ndel 1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :del demo

:d

{id…} - alias for :del

Try it

printf 'addall\nd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :d demo

:yank

[id] - copy a row to the clipboard, no delete (g:group yanks the whole group block); the clipboard survives Ctrl+Z and yanking does not consume undo history

Try it

printf 'addall\nyank\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :yank demo

:detail

[id] - open the Signal Detail float of a row (the K key); headless prints the detail lines

Try it

printf 'design index --top tutorial_packed tutorial_packed.sv\nadd tutorial_packed.packet\ndetail\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_packed.vcd"
recorded demo — :detail demo

:copypath

[id] - copy a row's full hierarchical path to the clipboard (OSC 52); headless prints the path

Try it

printf 'addall\ncopypath 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :copypath demo

:move

{id} <pos> - move a row (or a g:group / retained :expand block) so it shows row number <pos>; selecting an expanded child moves its parent and complete child block; an already-positioned row is not an undo step

Try it

printf 'addall\nmove 1 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :move demo

:move group

move g:<group> <pos>

Try it

printf 'addall\ngroup 1-5 sys\nmove g:sys 3\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :move group demo

:move expanded

{id} <pos> - move a row (or a g:group / retained :expand block) so it shows row number <pos>; selecting an expanded child moves its parent and complete child block; an already-positioned row is not an undo step

Try it

printf 'delall\nadd top.clk top.instruction_bus top.reset_n\nexpand 2\npanelwidth 64\nmove 3 19\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :move expanded demo

:blank

- insert a blank separator row after the selection

Try it

printf 'addall\nblank\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :blank demo

:comment

<text> - insert a comment row after the selection

Try it

printf 'addall\ncomment 1 main-clock\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :comment demo

:alias

{id} <name> - rename a trace (alias)

Try it

printf 'addall\nalias 1 clock\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :alias demo

:unalias

{id} - remove a trace alias

Try it

printf 'addall\nalias 1 clock\nunalias 1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unalias demo

:hl

<regexp> - highlight matching displayed traces

Try it

printf 'addall\nhl reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :hl demo

:unhl

<regexp> - remove highlight from matching traces

Try it

printf 'hl reset_n\nunhl reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unhl demo

:sort alpha

alpha|sig|reverse - sort the display list; a group block moves as one unit (alpha: header name; sig: first member's definition order) and its members reorder inside the block; blank/comment rows keep their alpha|sig slots

Try it

printf 'addall\ngroup 1,top.reset_n,/^top[.]cpu[.]pipeline_stage_0[0-2].*/ ahb\nsort alpha\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :sort alpha demo

:sort reverse

alpha|sig|reverse - sort the display list; a group block moves as one unit (alpha: header name; sig: first member's definition order) and its members reorder inside the block; blank/comment rows keep their alpha|sig slots

Try it

printf 'addall\ngroup 1,top.reset_n,/^top[.]cpu[.]pipeline_stage_0[0-2].*/ sys\nsort reverse\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :sort reverse demo

:filter

<pattern>|on|off - hide display rows not matching <pattern> (/regexp/ or plain substring over alias/full/leaf names); 'off' hides nothing, 'on' restores the last pattern, and a group header survives while its name or any member matches; no argument clears

Try it

printf 'addall\nfilter /pipeline_stage_0[0-5]/\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :filter demo

:filter on

filter on

Try it

printf 'addall\nfilter /pipeline_stage_0[0-5]/\nfilter off\nfilter on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :filter on demo

:filter off

filter off

Try it

printf 'addall\nfilter /pipeline_stage_0[0-5]/\nfilter off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :filter off demo

:tmark

[{id…}|clear] - toggle batch marks (v / Shift+Enter): no argument toggles the selected signal row, selectors toggle every matched signal row, 'clear' removes all marks (Esc) and is not an undo step when empty; :group without a selector consumes the marked set

Try it

printf 'tmark 1,3\ngroup picked\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tmark demo

:tmark clear

tmark clear

Try it

printf 'addall\ntmark 1,3\ntmark clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tmark clear demo

:group

<ids,names,/regexp/> <name> | group <name> | group rename <name> | group combine <name> - move selected signals into one bounded collapsible group; 'rename' changes the selected or enclosing group and repeating its current name is not an undo step; 'combine' concatenates a named group's bit-vector signals into one vector whose :expand restores the group

Try it

printf 'addall\ngroup 1,top.reset_n,/^top[.]cpu[.]pipeline_stage_0[0-2].*/ sys\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :group demo

:group rename

group rename <name>

Try it

printf 'addall\ngroup 1-3 sys\ngroup rename renamed system\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :group rename demo

:group combine

group combine <name>

Try it

printf 'addall\ngroup 1-3 sys\ngroup combine sys\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :group combine demo

:ungroup

- remove the enclosing group header

Try it

printf 'group 1-2 sys\nungroup\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :ungroup demo

Signal Format & Derived Signals

:loadalias

<file> - load enum value aliases for the selected signal from a file; reloading identical aliases from the same file is not an undo step

Try it

printf 'addall\nloadalias tutorial_aliases.txt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :loadalias demo

:aliasval

{id} <value> <name> [color] | aliasval {id} del <value> - edit one value alias of a display signal in-app: value as hex/dec/verilog literal; the optional palette color paints that value in the bus band and value column; coexists with :loadalias entries (later wins) and is saved with the session; repeating the same name/color is not an undo step

Try it

printf 'addall\naliasval top.instruction_bus 0x0001 FETCH\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :aliasval demo

:fmt

hex|dec|signed|bin|oct|ascii|char|time|real|float32|float64|real2bits | invert|reverse|rjustify|popcnt|msgbar on|off | shift <n>|off | fill0|fill1|filloff - data format of the selected trace; float32/float64 decode the bit pattern as IEEE754 and require an exact 32/64-bit vector; hex/oct/dec/signed are exact at any width; repeating the active format is not an undo step

Try it

printf 'delall\nadd top.radix3\nfmt bin\nfmt hex\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_radix.vcd"
recorded demo — :fmt demo

:fmt float

hex|dec|signed|bin|oct|ascii|char|time|real|float32|float64|real2bits | invert|reverse|rjustify|popcnt|msgbar on|off | shift <n>|off | fill0|fill1|filloff - data format of the selected trace; float32/float64 decode the bit pattern as IEEE754 and require an exact 32/64-bit vector; hex/oct/dec/signed are exact at any width; repeating the active format is not an undo step

Try it

printf 'addall\ngoto 35\nfmt float32\n2\nfmt float64\n3\nfmt dec\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_float.vcd"
recorded demo — :fmt float demo

:analog step

analog step

Try it

printf 'addall\ndel 3\ndel 3\ndel 3\nanalog off\nanalog step\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :analog step demo

:analog interp

analog interp

Try it

printf 'addall\ndel 3\ndel 3\ndel 3\nanalog interp\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :analog interp demo

:analog off

analog off

Try it

printf 'addall\ndel 3\ndel 3\ndel 3\nanalog step\nanalog off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :analog off demo

:expand

{id} - retain a vector parent and show its bits on a connected │/├─/└─ tree; Enter on the parent folds/opens them; repeating :expand on the already-open selected vector is not an undo step; a vector made by :group combine restores its original group

Try it

printf 'addall\nexpand 3\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expand demo

:expand group

expand <combined-group-vector>

Try it

printf 'addall\ngroup 1-3 sys\ngroup combine sys\nexpand\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expand group demo

:combine

{id…} - combine adjacent standalone bits into a vector; selecting a complete :expand child block restores its retained parent

Try it

printf 'addall\ncombine 1 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :combine demo

:expr and

expr <a> & <b>

Try it

printf 'addall\nexpr top.clk & top.reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr and demo

:expr unary

expr ~<sig>

Try it

printf 'addall\nexpr ~top.reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr unary demo

:expr reduce

expr &<sig>

Try it

printf 'addall\nexpr &top.state\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr reduce demo

:expr arith

expr <a> + <b>

Try it

printf 'addall\nexpr top.state + 1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr arith demo

:expr shift

expr <a> << <n>

Try it

printf 'addall\nexpr top.state << 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr shift demo

:expr compare

expr <a> >= <b>

Try it

printf 'addall\nexpr top.state >= 4\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr compare demo

:expr ternary

expr <cond> ? <a> : <b>

Try it

printf 'addall\nexpr top.clk ? top.state : 0\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr ternary demo

:expr bitsel

expr <sig>[msb:lsb]

Try it

printf 'addall\nexpr top.instruction_bus[3:0]\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr bitsel demo

:expr concat

expr {a, b}

Try it

printf 'addall\nexpr {top.clk, top.state[2:0]}\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr concat demo

:expr counter

expr counter(<sig>, rise|fall|any)

Try it

printf 'addall\nexpr counter(top.clk, rise)\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr counter demo

:expr timeshift

expr <sig> ->> <n>

Try it

printf 'addall\nexpr top.clk ->> 20\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr timeshift demo

:bus

create <name> {expr, sig[msb:lsb], …} | bus edit {id} {…} - virtual bus: splice signals or expressions (repeatable, bit-selectable) into one row; repeating an unchanged edit is not an undo step; saved with the session

Try it

printf 'addall\nbus create nibble {top.instruction_bus[3:0]}\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :bus demo

:subbus

{id} <width> - split a vector into ceil(N/width) sub-bus rows inserted after it (expand as sub-bus); the last row takes the remainder and the declared msb/lsb direction is honored

Try it

printf 'addall\nsubbus 3 4\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :subbus demo

:subbus expanded

{id} <width> - split a vector into ceil(N/width) sub-bus rows inserted after it (expand as sub-bus); the last row takes the remainder and the declared msb/lsb direction is honored

Try it

printf 'delall\nadd top.instruction_bus\nexpand 1\npanelwidth 64\nsubbus 1 4\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :subbus expanded demo

:counter

{id} rise|fall|any [name] - virtual counter signal: +1 per matching edge of the source, rendered as a 32-bit bus, saved with the session

Try it

printf 'addall\ncounter 1 any\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :counter demo

:savealias

<file> - export the selected signal's value aliases (:loadalias/:aliasval) as an alias file (:loadalias compatible); colors are not part of the file format

Try it

printf 'addall\nsavealias out_aliases.txt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :savealias demo

:color

<name>|normal|cycle|<0-255> - set selected trace color (theme name or xterm-256 index); 'cycle' rotates the palette (green/yellow/red/blue/magenta/cyan/orange/violet) over all traces; no argument opens the palette picker float; an unchanged color assignment is not an undo step

Try it

printf 'addall\ncolor red\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :color demo

:goto

<time|A-Z> - move the left edge / cursor to a time or named marker; repeating the active target is not an undo step

Try it

printf 'addall\ngoto 800\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :goto demo

:zoom in

zoom in

Try it

printf 'addall\ngoto 400\nzoom in\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom in demo

:zoom out

zoom out

Try it

printf 'addall\ngoto 400\nzoom in\nzoom in\nzoom in\nzoom out\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom out demo

:zoom full

in|out|full|fit|undo|<amount> - zoom control; repeated views, zoom limits, and undo with no prior zoom are not undo steps

Try it

printf 'addall\ngoto 400\nzoom in\nzoom in\nzoom in\nzoom full\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom full demo

:zoom fit

in|out|full|fit|undo|<amount> - zoom control; repeated views, zoom limits, and undo with no prior zoom are not undo steps

Try it

printf 'addall\nmark 100\nbmark 300\nzoom fit\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom fit demo

:zoom undo

in|out|full|fit|undo|<amount> - zoom control; repeated views, zoom limits, and undo with no prior zoom are not undo steps

Try it

printf 'addall\nzoom in\nzoom in\nzoom in\nzoom undo\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom undo demo

:edge rise

edge rise next|prev [count]

Try it

printf 'addall\nedge rise next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :edge rise demo

:edge fall

edge fall next|prev [count]

Try it

printf 'addall\nedge fall next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :edge fall demo

:edge any

edge any next|prev [count]

Try it

printf 'addall\nedge any next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :edge any demo

:pattern

[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)

Try it

printf 'addall\ngoto 0\nzoom in\nzoom in\nmark 100\npattern top.state=0011 backward\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern demo

:pattern real

[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)

Try it

printf 'addall\ngoto 0\nzoom in\nzoom in\nmark 100\npattern top.voltage=3.000 backward\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern real demo

:pattern string

[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)

Try it

printf 'addall\ngoto 0\nzoom in\nzoom in\nmark 100\npattern top.phase=BO* backward\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern string demo

:pattern all

[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)

Try it

printf 'addall\ngoto 0\nzoom in\nzoom in\npattern -all top.clk=1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern all demo

:pattern clear

pattern -clear

Try it

printf 'addall\npattern -all top.clk=1\npattern -clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern clear demo

:shifttime

<±t> | shifttime {id} <±t> | shifttime reset - shift the view of all traces or one trace in time (File/Signal Time); repeating a shift or resetting zero shifts is not an undo step

Try it

printf 'addall\nshifttime 100\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :shifttime demo

:searchvalue any

searchvalue any

Try it

printf 'addall\nsearchvalue any\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue any demo

:searchvalue rise

searchvalue rise

Try it

printf 'addall\nsearchvalue rise\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue rise demo

:searchvalue fall

searchvalue fall

Try it

printf 'addall\nsearchvalue fall\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue fall demo

:searchvalue pattern

searchvalue <pattern> [id|name]

Try it

printf 'addall\nsearchvalue 000000?1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue pattern demo

:searchvalue jump

searchvalue jump next|prev

Try it

printf 'addall\nsearchvalue 000000?1\nsearchvalue jump next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue jump demo

:compress range

compress <t0> <t1>

Try it

printf 'addall\ncompress 200 800\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_compress.vcd"
recorded demo — :compress range demo

:compress clear

<t0> <t1> | compress clear - hide a time range from the wave view; clearing an empty compression map is not an undo step

Try it

printf 'addall\ncompress 200 800\ncompress clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_compress.vcd"
recorded demo — :compress clear demo

:fetch

left|right [n] - extend the visible time range; a zoom-limit no-op is not an undo step

Try it

printf 'addall\nfetch left 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :fetch demo

:discard

left|right [n] - shrink the visible time range; a zoom-limit no-op is not an undo step

Try it

printf 'addall\ndiscard left 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :discard demo

Markers & Measurements

:mark

[time] - place/move the primary marker; an explicit time focuses the visible wave position; repeating an unchanged placement is not an undo step

Try it

printf 'addall\nmark 400\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :mark demo

:bmark

[time] - place/move the alt (baseline) marker; an explicit time focuses the visible wave position; repeating an unchanged placement is not an undo step

Try it

printf 'addall\nbmark 300\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :bmark demo

:unmark

[alt] - delete the primary marker ('alt' deletes the baseline); clearing an absent marker is not an undo step

Try it

printf 'addall\nmark 10\nunmark\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unmark demo

:unmark alt

unmark [alt]

Try it

printf 'addall\nmark 10\nbmark 30\nunmark alt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unmark alt demo

:drop

<A-Z> [time] - place a unique named marker; repeating the same letter and time is not an undo step

Try it

printf 'addall\ndrop A 500\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :drop demo

:collect

<A-Z>|all - delete named marker(s); clearing an absent marker is not an undo step

Try it

printf 'addall\ndrop A 100\ndrop B 300\ncollect all\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :collect demo

:marks

- list all markers

Try it

printf 'addall\nmark 100\nbmark 300\ndrop A 500\nmarks\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :marks demo

:anchor

anchor set <name> [time] | anchor list

Try it

printf 'addall\ngoto 200\nanchor set reset_done\ngoto 500\nmark @reset_done\nanchor list\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :anchor demo

:delta freq

delta freq

Try it

printf 'addall\nmark 100\nbmark 300\ndelta freq\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta freq demo

:delta time

delta time

Try it

printf 'addall\nmark 100\nbmark 300\ndelta time\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta time demo

:delta fix

delta fix

Try it

printf 'addall\nmark 100\nbmark 300\ndelta fix\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta fix demo

:delta unfix

delta unfix

Try it

printf 'addall\nmark 100\nbmark 300\ndelta fix\ndelta unfix\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta unfix demo

:delta cycles

freq|time|fix|unfix | delta cycles <period> - marker delta display: time/frequency, frozen (fix), or clock cycles period accepts unit suffixes like 6.4ns; repeating the active mode/value is not an undo step

Try it

printf 'addall\nmark 25\nbmark 10\ndelta cycles 5\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :delta cycles demo

:annotate

<text> [time] | annotate del <n> | annotate list - floating text annotation pinned to a time; the time defaults to the primary marker/cursor and accepts unit suffixes; 'list' is read-only; saved with the session

Try it

printf 'addall\nannotate 5 first-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :annotate demo

Waveform Analysis

:toggle

[regexp] - switching-activity report: per-signal transition counts and stable signals (not a coverage database)

Try it

printf 'addall\ntoggle\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :toggle demo

:compare

<file> [range <t0> <t1>] [ignorexz] | compare {id} {id} | compare next|prev|clear|report - compare the open waveform with a reference file (optionally limited to a time range; ignorexz skips mismatches where either side has x/z bits), or two display rows against each other; mismatches become vertical marks navigated with next/prev; a two-row compare also opens a detail report of both values, reopened with 'compare report'; report reopening, identical results and clearing empty results are not undo steps

Try it

printf 'addall\ncompare tutorial_demo_b.vcd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :compare demo

:diff

{id} {id} | diff report - alias of :compare {id} {id}: mismatch marks of two display rows plus the detail report overlay; reopening the report or repeating an identical result is not an undo step

Try it

printf 'addall\ndiff 1 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :diff demo

:report

events [t0 t1] [regexp] | report samples <event-expression> [t0 t1] [-track <sig>]… [-sample native|pre-edge] | report property <match|assert|deassert|switch> <expression> [t0 t1] [-same-time all|final] | report extract [t0 t1] -source <name> <event> <predicate> [-payload <name> <signal>]… [-sample <name> native|pre-edge]… | report values <sig> [t0 t1] [-when <expr>] [-o <file>] | report records <sig> [t0 t1] [filters] [-o <file>] | report assertions <sig> [t0 t1] [-o <file>] | report protocol <valid> <ready> <clock> [t0 t1] [-payload <sig>]… [-sample native|pre-edge] | report failures <artifact> [-signal <sig>]… - signal reports: events = transition counts; samples = named/wildcard/edge event unions with iff guards and explicit event/sample times; property = typed four-state match/assert/deassert/switch captures; extract = merged named event sources with predicates and payloads; values = per-change time+value lines with optional condition; records/assertions = typed stream evidence; protocol = ready/valid diagnostics with native or pre-edge sampling; failures = bounded regression artifacts correlated with waveform values; -o writes supported reports to a file

Try it

printf 'addall\nreport events\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :report demo

:report protocol

report protocol <valid> <ready> <clock> [t0 t1] [-payload <signal>]… [-sample native|pre-edge]

Try it

printf 'report protocol top.valid top.ready top.clk 0 65 -payload top.data -sample pre-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report protocol demo

:report samples

report samples <event-expression> [t0 t1] [-track <sig>]… [-sample native|pre-edge]

Try it

printf 'report samples "posedge tb_top.clk iff tb_top.dut.issue_fire" 0 45 -track tb_top.dut.issue_fire -track tb_top.dut.issue_pc -sample pre-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_event_sampling.vcd"
recorded demo — :report samples demo

:report property

report property <match|assert|deassert|switch> <expression> [t0 t1] [-same-time all|final]

Try it

printf 'report property deassert "top.data == 8'haa" 0 65\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report property demo

:report extract

report extract [t0 t1] -source <name> <event> <predicate> [-payload <name> <signal>]… [-sample <name> native|pre-edge]…

Try it

printf 'report extract 0 65 -source rise "posedge top.clk" top.valid -payload rise top.data -source fall "negedge top.clk" "!top.valid" -payload fall top.data -sample fall pre-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report extract demo

:report failures

report failures <artifact> [-signal <signal>]…

Try it

printf 'report failures tutorial_failures.txt -signal top.data\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report failures demo

:stuck

[t0 t1] - highlight display signals with no changes in the time range (default: visible window); already-highlighted results are not an undo step

Try it

printf 'addall\nstuck\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :stuck demo

:expanddelta

[time] - list the delta-cycle sub-steps of the selected signal at the cursor (or [time]) in the report overlay; needs source seq data (T9)

Try it

printf 'addall\ndel 1\ngoto 20\nexpanddelta\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :expanddelta demo

Coverage

:coverage load

coverage load <file>

Try it

printf 'coverage load tutorial_coverage.xml\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage load demo

:coverage summary

coverage summary

Try it

printf 'coverage load tutorial_coverage.xml\ncoverage summary\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage summary demo

:coverage points

coverage points

Try it

printf 'coverage load tutorial_coverage.xml\ncoverage points\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage points demo

:coverage clear

coverage clear

Try it

printf 'coverage load tutorial_coverage.xml\ncoverage clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage clear demo

Semantic Design

:design index

design index <compiler-args...>

Try it

printf 'design index --top tutorial_design tutorial_design.sv\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design index demo

:design summary

design summary

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign summary\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design summary demo

:design instances

design instances

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign instances\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design instances demo

:design objects

design objects [filter]

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign objects selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design objects demo

:design processes

design processes

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign processes\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design processes demo

:design assignments

design assignments

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign assignments\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design assignments demo

:design diagnostics

design diagnostics

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign diagnostics\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design diagnostics demo

:design source

design source <path-or-id>

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign source tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design source demo

:design clear

design clear

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndesign clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design clear demo

:xprobe status

xprobe status

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe status\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe status demo

:xprobe rebuild

xprobe rebuild

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe rebuild\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe rebuild demo

:xprobe object

xprobe object <path-or-id>

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe object tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe object demo

:xprobe source-hover

hover a report source path

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe object tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe source-hover demo

:xprobe signal

xprobe signal <wave-path>

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe signal tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe signal demo

:xprobe diagnostics

xprobe diagnostics

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe diagnostics\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe diagnostics demo

:xprobe map

xprobe map list|add|del|clear

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe map add tutorial_design sim_top\nxprobe map list\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe map demo

:xprobe alias

xprobe alias list|add|del|clear

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nxprobe alias add tutorial_design.selected tutorial_design.selected\nxprobe alias list\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe alias demo

Source & Root-Cause Debug

:sourceview object

sourceview object <design-path-or-id> [time]

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nsourceview object tutorial_design.selected 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :sourceview object demo

:sourceview signal

sourceview signal <wave-path> [time]

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nsourceview signal tutorial_design.source_a 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :sourceview signal demo

:sourceview refresh

sourceview refresh [time]

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nsourceview object tutorial_design.selected 10\nsourceview refresh 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :sourceview refresh demo

:drivers

<design-path-or-id> [bit-offset-msb:lsb] - list bit-precise continuous, procedural and module-port assignments that drive an elaborated object range; report overlapping multi-driver offsets; the source cockpit target is undoable, and reopening an identical result is not an undo step

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ndrivers tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :drivers demo

:loads

<design-path-or-id> [bit-offset-msb:lsb] - list bit-precise static fanout assignments that read an elaborated object range; the source cockpit target is undoable, and reopening an identical result is not an undo step

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nloads tutorial_design.source_a\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :loads demo

:active-driver

<design-path-or-id> [range <msb:lsb>] [at <time>] [seq <signal-local-sequence|final>] - classify each bit-precise static driver as active, inactive or unknown using projected guards and waveform evidence; synchronize the target source/waveform and add mapped inputs; reopening an identical result is not an undo step

Try it

printf 'design index --top tutorial_design tutorial_design.sv\nactive-driver tutorial_design.selected at 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :active-driver demo

:tracex

<design-path-or-id> [range <msb:lsb>] [at <time>] [seq <signal-local-sequence|final>] - trace X/Z values backward through non-inactive bit-precise drivers with bounded traversal and explicit process/delta evidence gaps; add mapped trace signals; reopening an identical result is not an undo step

Try it

printf 'design index --top tutorial_design tutorial_design.sv\ntracex tutorial_design.selected at 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :tracex demo

Layout & Appearance

:set grid on

set grid on

Try it

printf 'addall\nset grid on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid on demo

:set grid off

set grid off

Try it

printf 'set grid on\nset grid off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid off demo

:set grid edge

set grid edge rise|fall|both [start <n>]

Try it

printf 'addall\nset grid edge rise\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid edge demo

:set grid period

set grid period <t> [start <n>]

Try it

printf 'addall\nset grid period 100\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid period demo

:set glitch

set glitch on|off

Try it

printf 'addall\nset glitch on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :set glitch demo

:set ic

set ic on|off

Try it

printf 'addall\nset ic on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set ic demo

:set base_symbols

set base_symbols on|off

Try it

printf 'addall\nset base_symbols on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set base_symbols demo

:set hiername

set hiername on|off

Try it

printf 'addall\nset hiername on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set hiername demo

:set highlight

set highlight on|off

Try it

printf 'addall\nset highlight off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set highlight demo

:set snap

set snap on|off

Try it

printf 'addall\nset snap on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set snap demo

:set cursorcenter

set cursorcenter on|off

Try it

printf 'addall\nset cursorcenter on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set cursorcenter demo

:set timeunit

set timeunit <unit>

Try it

printf 'addall\nset timeunit us\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set timeunit demo

:set timeunit file

set timeunit file

Try it

printf 'set timeunit us\nset timeunit file\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set timeunit file demo

:panels signals

panels signals toggle

Try it

printf 'addall\npanels signals toggle\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :panels signals demo

:panelwidth

panelwidth <cols>|auto

Try it

printf 'addall\npanelwidth 40\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :panelwidth demo

:split

- add a wave pane below with its own display list and time window (T17, up to 4 panes); the new pane copies the focused one, Tab cycles the focus, :only collapses back to one

Try it

printf 'addall\nsplit\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :split demo

:only

- collapse the split wave view back to a single pane, keeping the focused pane's view and display list (T17); already being in one pane is not an undo step

Try it

printf 'addall\nsplit\nonly\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :only demo

:theme

<builtin|file.yaml> - switch the skin: a built-in preset or a YAML file with colors/icons/palette; theme changes are restored by Ctrl+Z, while no argument only lists the presets

Try it

printf 'addall\ntheme dracula\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :theme demo

Sessions & Automation

:plugins

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf 'plugins\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins demo

:plugins complete-ids

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins complete-ids demo

:plugins complete-commands

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins complete-commands demo

:plugins actions

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf 'plugins actions tcl-protocol-decoder/plugin.json\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins actions demo

:plugins run

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf 'plugins run tcl-protocol-decoder/plugin.json check '{"profile":"ready-valid","clock":"top.clk","start":0,"end":65,"roles":{"valid":"top.valid","ready":"top.ready"}}'\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins run demo

:plugins run-x

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf 'plugins run tcl-x-detective/plugin.json scan '{"signal":"top.instruction_bus","start":0,"end":100,"class":"X","min_duration":1}'\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-x demo

:plugins run-triage

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf 'plugins run tcl-failure-triage/plugin.json triage '{"file":"tcl-failure-triage/failures.junit.xml","limit":32}'\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-triage demo

:plugins run-cli-protocol

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins run-cli-protocol demo

:plugins run-cli-transactions

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_transactions.vcd"
recorded demo — :plugins run-cli-transactions demo

:plugins run-cli-x

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-cli-x demo

:plugins run-cli-triage

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-cli-triage demo

:plugins exec

[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service

Try it

printf 'plugins exec ready-valid top 0 65\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins exec demo

:save

<file.svw> - save the waveform session, complete ordered open-file list and stable source-debug target; a closed cockpit stays closed when restored and can be reopened with :sourceview refresh

Try it

printf 'addall\nsave session.svw\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :save demo

:save multifile-session

<file.svw> - save the waveform session, complete ordered open-file list and stable source-debug target; a closed cockpit stays closed when restored and can be reopened with :sourceview refresh

Try it

printf 'open tutorial_demo_b.vcd\nsave multifile-session.svw\nactive 1\nsource multifile-session.svw\nfiles\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :save multifile-session demo

:record

<file.svw>|off - record session commands to a :source-replayable script (T16)

Try it

printf 'addall\nrecord session.svwcmd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :record demo

:source

<file.svw|file.svwcmd> - restore an svw 1 session as one undoable state change, including its ordered open-file list and stable source-debug target without reopening a saved closed cockpit, or replay a command script using each contained command's own undo boundary; reloading an identical session and scripts containing only read-only/external commands create no empty Ctrl+Z layer

Try it

printf 'addall\nsource tutorial_demo.svwcmd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :source demo

Help & Application

:q

- quit

Try it

printf 'q\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :q demo

:q!

- quit immediately without confirmation

Try it

printf 'q!\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :q! demo

:version

- show version

Try it

printf 'version\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :version demo

:help

[cmd] - open the help page, or show one command's usage

Try it

printf 'addall\nhelp zoom\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :help demo

Mouse right-click actions

right-click: signal context menu

right-click opens the complete signal action menu

recorded demo — right-click: signal context menu

right-click: Signal Detail

open the selected signal's detailed value and type view

recorded demo — right-click: Signal Detail

right-click: Copy Full Path

copy the full hierarchical signal path

recorded demo — right-click: Copy Full Path

right-click: Radix

cycle the selected signal's display radix

recorded demo — right-click: Radix

right-click: Alias

open the command line prefilled for the selected signal

recorded demo — right-click: Alias

right-click: Expand

expand a vector into bit rows, then right-click a child and combine the complete block again

recorded demo — right-click: Expand

right-click: Combine

combine an expanded bit block back into its original vector, then right-click that parent and expand it again

recorded demo — right-click: Combine

right-click: Highlight

toggle persistent row highlighting

recorded demo — right-click: Highlight

right-click: Elaborated Source

dock syntax-highlighted elaborated RTL with its focus line

recorded demo — right-click: Elaborated Source

right-click: Drivers

show the static semantic drivers of the mapped object

recorded demo — right-click: Drivers

right-click: Trace X

trace an unknown value from the current waveform time

recorded demo — right-click: Trace X

right-click: Delete Row

remove the selected display row

recorded demo — right-click: Delete Row

right-click: Group Marked

group the current batch-marked rows

recorded demo — right-click: Group Marked

right-click: Collapse Group

collapse or expand a group from its header

recorded demo — right-click: Collapse Group

right-click: Combine Group as Vector

concatenate a group's signals into one retained vector

recorded demo — right-click: Combine Group as Vector

right-click: Rename Group

open the selected group name in the command editor and rename the complete group block

recorded demo — right-click: Rename Group

right-click: Delete Group

delete a complete bounded group block

recorded demo — right-click: Delete Group

right-click: Ungroup

dissolve a group while retaining its signal rows

recorded demo — right-click: Ungroup

right-click: Restore Combined Group

restore the original bounded group through the combined vector's Restore Group action

recorded demo — right-click: Restore Combined Group

mouse: right-click places the alt marker

right-click in the wave area drops the alt/baseline marker (edge-snapped), same as middle click or Alt+click

recorded demo — mouse: right-click places the alt marker

UI essentials

n/N search marker follows both directions

lowercase n and uppercase N move the primary yellow marker, select the globally matched signal, stay visible and wrap between the first and last results

recorded demo — n/N search marker follows both directions

Left/Right search with an empty signal panel

Right finds and reveals the first value match even when no row is selected; Left follows the same cyclic search state back to the last match.

recorded demo — Left/Right search with an empty signal panel

extract to an indexed KBX, then search globally

stream any supported waveform into a globally ordered KBX; the next headless search uses its source-wide directory instead of scanning every signal.

recorded demo — extract to an indexed KBX, then search globally

read-only command scripts add no empty undo layer

:source delegates Ctrl+Z ownership to each replayed command; a version-only script leaves the preceding marker as the next real undo action

recorded demo — read-only command scripts add no empty undo layer

reloading an identical session adds no empty undo layer

The first svw 1 restore remains one transaction; repeating the same canonical state leaves Ctrl+Z pointed at that restore and returns directly to the later marker

recorded demo — reloading an identical session adds no empty undo layer

closed source cockpits retain their session target

Saving after Esc keeps the stable source target without reopening the dismissed cockpit; after another target is visited, loading the session and refreshing returns to the saved object

recorded demo — closed source cockpits retain their session target

expand exact internal evidence IDs

Reports prioritize human-readable evidence while retaining exact object, signal, assignment and process IDs behind i

recorded demo — expand exact internal evidence IDs

analysis queries do not hide the previous undo

Reopening or recomputing the same comparison and updating explicit cross-probe state leave the previous waveform edit directly reachable by one Ctrl+Z; the remap remains visible

recorded demo — analysis queries do not hide the previous undo

KBX refresh does not create a false Model undo

Refreshing the explicit external KBX snapshot never hides the previous waveform edit behind a partial undo layer

recorded demo — KBX refresh does not create a false Model undo

source refresh follows the Ctrl+Z target

Ctrl+Z restores the source target together with waveform and cockpit state, so refresh cannot jump back to the later object

recorded demo — source refresh follows the Ctrl+Z target

reopening source debug results is not an undo step

Repeated sourceview, active-driver and Trace-X reports leave the preceding real marker state reachable by one Ctrl+Z

recorded demo — reopening source debug results is not an undo step

static drivers own one source-cockpit undo step

A distinct drivers target is undoable, while reopening its identical result adds no empty layer; one Ctrl+Z restores the previous source target without clearing the marker

recorded demo — static drivers own one source-cockpit undo step

static loads own one source-cockpit undo step

A distinct loads target is undoable, while reopening its identical result adds no empty layer; one Ctrl+Z restores the previous source target without clearing the marker

recorded demo — static loads own one source-cockpit undo step

command editor wraps without hiding input

As a command reaches the right edge, the editor grows by one row and keeps every typed character visible.

recorded demo — command editor wraps without hiding input

recall commands from the launch directory

svw loads a validated .svw_history file at startup; Up and Down recall commands entered in earlier runs.

recorded demo — recall commands from the launch directory

return from history to the unfinished draft

After Up recalls the newest command, Down crosses the history boundary and restores the exact unfinished command instead of clearing it; Down on a fresh draft is a no-op.

recorded demo — return from history to the unfinished draft

reject unsafe command history before recall

A correct header is not sufficient when a history command contains invalid UTF-8 or terminal controls; svw rejects the whole file, so Up remains empty and version runs normally.

recorded demo — reject unsafe command history before recall

reject an oversized persisted command before recall

A correct-header history file cannot make Up copy and render an unbounded command; the whole file stays untouched and the newly typed version command runs normally.

recorded demo — reject an oversized persisted command before recall

type and edit Unicode without splitting characters

Raw UTF-8 input reaches the command editor atomically; Left and Backspace remove the emoji before Z as one visible character while preserving CJK and a combining accent.

recorded demo — type and edit Unicode without splitting characters

signal search keeps a long query's cursor visible

The one-row search prompt follows the active edit point, preserves Unicode display-cluster boundaries and reserves space for its result count instead of hiding typed text.

recorded demo — signal search keeps a long query's cursor visible

record byte-exact terminal diagnostics

--terminal-record writes a compressed trace of output bytes, write results, input, resize events and terminal environment metadata for offline diagnosis and replay.

recorded demo — record byte-exact terminal diagnostics

scroll a large signal panel without glyph artifacts

Fast signal-list scrolling completes every terminal frame; UTF-8 glyphs are never left truncated or replaced.

recorded demo — scroll a large signal panel without glyph artifacts

combining-character incremental repaint integrity

Selection updates retain each base-plus-combining sequence as one terminal cell; a same-size full repaint leaves the signal and waveform panels unchanged.

recorded demo — combining-character incremental repaint integrity

mouse: click to select a signal

click a signal row to select it

recorded demo — mouse: click to select a signal

mouse: double click reveals the scope

double click a signal row to open the picker filtered to the signal's scope

recorded demo — mouse: double click reveals the scope

mouse: click to place the marker

click anywhere in the wave area to drop the primary marker (edge-snapped)

recorded demo — mouse: click to place the marker

mouse: drag a box selection

drag in the wave area for a highlighted box selection

recorded demo — mouse: drag a box selection

mouse: wheel scrolls the time axis

wheel up/down scrolls the waveform in time

recorded demo — mouse: wheel scrolls the time axis

mouse: toolbar buttons

click toolbar buttons (here: zoom in)

recorded demo — mouse: toolbar buttons

mouse: overview minimap

click the TIME overview to jump the view there

recorded demo — mouse: overview minimap

large timestamps stay separated

wide ruler labels are thinned with a blank-cell safety gap instead of overwriting adjacent timestamps

recorded demo — large timestamps stay separated

modern terminal: drag the panel divider

a truecolor/256-color terminal uses jittered SGR mouse packets to drag the signal panel's right border; x/z ranges keep their semantic tint but render one centered value like numeric bus ranges, and the scrollbar remains explicit

recorded demo — modern terminal: drag the panel divider

8-color-class UTF-8 terminal: basic mouse resize

an older UTF-8 terminal with no 256-color declaration can resize the signal panel from basic X10 press/release alone; Unicode chrome, centered x/z values, semantic backgrounds and the scrollbar remain visible

recorded demo — 8-color-class UTF-8 terminal: basic mouse resize

8-color Unicode: resize the power-failure fixture

the exact tutorial_power_failure.vcd case accepts a basic X10 drag from the visible signal-panel border; the expanded panel exposes retention_restore in full

recorded demo — 8-color Unicode: resize the power-failure fixture

8-color Unicode: resize without a scrollbar

at 240x120 all seven traces fit without a scrollbar; a UTF-8-extended X10 drag still expands the signal name

recorded demo — 8-color Unicode: resize without a scrollbar

mouse: a row drag cannot become a double-click

after reordering a signal, an immediate click at the old screen cell selects its new row instead of opening search

recorded demo — mouse: a row drag cannot become a double-click

mouse: scrollbar capture survives pane overshoot

dragging past the narrow divider and below the pane keeps tracking and clamps the Signal Panel to its final row

recorded demo — mouse: scrollbar capture survives pane overshoot

mouse: unrelated input breaks double-click detection

Ctrl-click and keyboard navigation both prevent surrounding plain clicks from becoming an accidental search action

recorded demo — mouse: unrelated input breaks double-click detection

non-Unicode toolbar uses readable mnemonics

ASCII mode labels full view as F, rising/falling edges as ^/v with direction, any edge as E, search as / and grid as G; the complete label remains clickable

recorded demo — non-Unicode toolbar uses readable mnemonics

middle-click dismisses the context menu safely

middle-clicking even the destructive menu row closes the menu without executing it or deleting the signal

recorded demo — middle-click dismisses the context menu safely

context-menu help owns pointer input

a click over the hidden Delete Row cell selects only the top HELP page; closing help restores the unchanged menu

recorded demo — context-menu help owns pointer input

Signal Detail: mouse value format

click the pinned Binary button; h/b/u/s/f provide the same hex, binary, unsigned, signed and float choices

recorded demo — Signal Detail: mouse value format

Signal Detail: one-bit scalar formats

A one-bit scalar offers only bit-oriented hex/binary views; signed and unsigned vector choices are absent.

recorded demo — Signal Detail: one-bit scalar formats

Signal Detail yank: Unicode character count

copy a detail page containing CJK and a combining cluster; the toast reports visible characters instead of UTF-8 bytes

recorded demo — Signal Detail yank: Unicode character count

right-click: empty groups expose only valid actions

an empty group omits Combine as Vector because no signal bits exist to concatenate

recorded demo — right-click: empty groups expose only valid actions

mouse: pin Signal Detail hover

pause over a signal row to anchor the exact K Signal Detail content at the pointer; three fixed dots pulse for 3.5 seconds before PINNED appears, and q/Esc dismisses it

recorded demo — mouse: pin Signal Detail hover

mouse: pin and close a group summary

pause over a group title to pin its member/collapse summary after 3.5 seconds; q or Esc closes it immediately even when the terminal has queued passive mouse motion

recorded demo — mouse: pin and close a group summary

mouse: inspect source from pinned Signal Detail

a semantically mapped signal shows its source path and line; after the detail float pins, hover that link for bounded, syntax-highlighted SystemVerilog context

recorded demo — mouse: inspect source from pinned Signal Detail

mouse: pin a folded-cell tooltip

hover a wave cell containing multiple changes; the common 3.5-second timer pins the value chain until q/Esc

recorded demo — mouse: pin a folded-cell tooltip

SPC leader menu

Space opens the which-key menu; u opens +ui; g turns the grid on

recorded demo — SPC leader menu

SPC / searchvalue shortcut

Space then / prefills :searchvalue; enter a value pattern and n jumps to the first global match. Space Space and Space s / remain the signal-picker shortcuts.

recorded demo — SPC / searchvalue shortcut

? help page

? opens the full help overlay; search jumps directly to the task-grouped command index

recorded demo — ? help page

help search follows a long query's edit point

Help, report and detail overlays share a bounded, Unicode-safe less-style search editor; its direction sigil stays fixed while the query tail and block cursor remain visible.

recorded demo — help search follows a long query's edit point

help wraps long command descriptions

Long command usage and description text wraps inside the help panel instead of being clipped at the right edge.

recorded demo — help wraps long command descriptions

picker: Ctrl+A adds all matches

in the / Telescope picker, Ctrl+A adds every current match to the display list at once (same as :find)

recorded demo — picker: Ctrl+A adds all matches

group label band

groups are one-line colored labels; Enter or a click collapses them and shows the member count

recorded demo — group label band

Enter on a scalar is quiet

Enter folds groups and expanded vectors; on an ordinary scalar signal it intentionally does nothing

recorded demo — Enter on a scalar is quiet

expanded-vector parent and children

:expand retains a disclosure parent; its child bits are joined by a continuous │/├─/└─ tree, and Enter folds or opens the complete child block

recorded demo — expanded-vector parent and children

mouse: open a folded vector

even a fast double-click on the retained vector parent opens its complete bit tree exactly once and never searches

recorded demo — mouse: open a folded vector

mouse: double-click collapses a group once

a fast second click is absorbed after the first collapse, so the group stays folded instead of immediately reopening

recorded demo — mouse: double-click collapses a group once

Ctrl+Z undo with terminal keyboard reporting

a cancelled dd prefix and inert scalar Enter do not consume undo; one CSI-u Ctrl+Z restores the last real edit

recorded demo — Ctrl+Z undo with terminal keyboard reporting

boundary no-ops preserve the previous Ctrl+Z action

minimum panel/row sizing, an unchanged color, and an already-full zoom create no empty undo layers

recorded demo — boundary no-ops preserve the previous Ctrl+Z action

Ctrl+Z restores a keyboard signal reorder

Alt+Down moves the selected trace as a real display-list edit; one Ctrl+Z restores its original position

recorded demo — Ctrl+Z restores a keyboard signal reorder

Ctrl+Z clears keyboard batch marks in place

v and Shift+Enter each create one recoverable VISUAL mark; one Ctrl+Z clears that mark without removing signals added before it

recorded demo — Ctrl+Z clears keyboard batch marks in place

Ctrl+Z clears mouse batch marks in place

Ctrl+click and Shift+click each own one recoverable VISUAL mark; undo clears the mark or range without deleting the existing signal rows

recorded demo — Ctrl+Z clears mouse batch marks in place

Ctrl+Z restores keyboard marker placement

m and b each own one marker snapshot; Ctrl+Z clears only that primary or baseline marker and preserves the signals added before it

recorded demo — Ctrl+Z restores keyboard marker placement

idempotent marker commands do not consume Ctrl+Z

repeating mark/bmark/drop or clearing an absent primary, baseline or named marker leaves undo pointed at the last real session edit

recorded demo — idempotent marker commands do not consume Ctrl+Z

signal-list no-ops preserve the previous Ctrl+Z action

repeated add, alias, highlight, sort, find, and filter requests create no invisible undo layers

recorded demo — signal-list no-ops preserve the previous Ctrl+Z action

time-navigation no-ops preserve the previous Ctrl+Z action

repeated goto, clearing no compression, and zoom undo without history create no invisible undo layers

recorded demo — time-navigation no-ops preserve the previous Ctrl+Z action

repeated settings preserve the previous Ctrl+Z action

active set, delta, and time-shift values plus a default-off toggle create no invisible undo layers

recorded demo — repeated settings preserve the previous Ctrl+Z action

format and layout no-ops preserve the previous Ctrl+Z

repeating the active pane, panel width, trace format or analog mode, listing annotations, and clearing empty compare results create no invisible undo layers

recorded demo — format and layout no-ops preserve the previous Ctrl+Z

idempotent commands preserve the previous Ctrl+Z

current-file selection, empty mark clears, in-place move, repeated color/stuck/alias/group state, and unchanged alias file loads create no invisible undo layers

recorded demo — idempotent commands preserve the previous Ctrl+Z

repeated pattern marks preserve the previous Ctrl+Z

reapplying an unchanged :pattern -all result does not hide the previous real edit behind an empty undo layer

recorded demo — repeated pattern marks preserve the previous Ctrl+Z

searchvalue queries preserve the previous Ctrl+Z

querying the active search or repeating its mode, pattern and signal scope creates no invisible undo layer

recorded demo — searchvalue queries preserve the previous Ctrl+Z

unchanged vector and bus commands preserve Ctrl+Z

reopening an already-open selected vector or reapplying an identical virtual-bus splice creates no invisible undo layer

recorded demo — unchanged vector and bus commands preserve Ctrl+Z

external databases do not consume waveform Ctrl+Z history

coverage load/clear and design clear remain explicit database lifecycle operations while one Ctrl+Z still reaches the preceding waveform edit

recorded demo — external databases do not consume waveform Ctrl+Z history

yank and scope navigation do not consume Ctrl+Z

scope-picker navigation leaves hierarchy state untouched; one undo reaches the prior waveform edit while a yanked row remains available for paste

recorded demo — yank and scope navigation do not consume Ctrl+Z

Ctrl+Z restores mouse marker placement

wave clicks place the primary marker, while Alt+click, middle-click and right-click place the baseline marker; each gesture is independently undoable

recorded demo — Ctrl+Z restores mouse marker placement

cancelled color picker creates no empty undo layer

q or Esc cancels the deferred palette without consuming Ctrl+Z; the next undo reaches the preceding real edit

recorded demo — cancelled color picker creates no empty undo layer

Ctrl+Z restores the previous visual theme

a real theme switch owns one complete session snapshot; undo restores the prior skin without removing signals, while a theme query creates no empty undo layer

recorded demo — Ctrl+Z restores the previous visual theme

Ctrl+Z restores a mouse signal reorder

a completed row drag owns one undo snapshot; Ctrl+Z restores the original signal ordering

recorded demo — Ctrl+Z restores a mouse signal reorder

Ctrl+Z reopens a mouse-folded group

a structural label click owns one undo snapshot; Ctrl+Z reopens the same group instead of deleting it

recorded demo — Ctrl+Z reopens a mouse-folded group

Ctrl+Z restores a Ctrl+wheel zoom

mouse-centered zoom owns one undo snapshot and restores the prior time view without removing signals

recorded demo — Ctrl+Z restores a Ctrl+wheel zoom

Ctrl+Z restores a mouse box zoom

releasing a waveform box selection commits one undo snapshot for the resulting zoom

recorded demo — Ctrl+Z restores a mouse box zoom

Ctrl+Z restores a mouse panel resize

the divider redraws live while dragging and commits one undo snapshot when released

recorded demo — Ctrl+Z restores a mouse panel resize

Ctrl+Z restores a toolbar grid toggle

the visible grid button owns one display-state snapshot; undo restores the grid without removing earlier signals

recorded demo — Ctrl+Z restores a toolbar grid toggle

contextual help for the tree window

? explains the active popup; closing help returns to the tree with its selection and lazy state intact

recorded demo — contextual help for the tree window

contextual help for reports

? opens the report window's scrolling, search, copy and identifier-display key reference

recorded demo — contextual help for reports

contextual help for signal detail

? explains value formats, mouse selection, navigation and copy behavior for the active signal-detail window

recorded demo — contextual help for signal detail

contextual help for the color picker

? explains movement, apply and cancel keys for the active color palette

recorded demo — contextual help for the color picker

contextual help for the marker list

? explains marker selection and time jumping without discarding the marker-list state

recorded demo — contextual help for the marker list

contextual help for the right-click menu

? explains keyboard and direct-number operation of the active signal context menu

recorded demo — contextual help for the right-click menu

contextual help for the leader menu

? explains group traversal and cancellation while keeping the leader menu open underneath

recorded demo — contextual help for the leader menu

contextual help for quit confirmation

? explains confirmation and cancellation without itself accepting or dismissing the pending quit

recorded demo — contextual help for quit confirmation

:<N> signal jump

type a signal's panel number as a command to jump straight to it (vim line jump)

recorded demo — :<N> signal jump

:<time> wave jump

with W focus, type a time directly to move the waveform; bare values use the file timescale and explicit s/ms/us/ns/ps/fs suffixes are converted strictly

recorded demo — :<time> wave jump

G: last signal

G jumps to the last visible signal (vim)

recorded demo — G: last signal

gg: first signal

gg jumps back to the first visible signal (vim)

recorded demo — gg: first signal

Tcl UI plugin: layouts and emergency recovery

--plugin explicitly starts a user-provided tclsh out of process; waveform/session files never auto-load code. The plugin owns bounded retained cells, can dock beside the live built-in frame or take over the full TUI, and never receives the terminal descriptor or a C++ object pointer

recorded demo — Tcl UI plugin: layouts and emergency recovery

Tcl UI plugin: native signal/wave linkage

a plugin row submits a typed signal/time anchor; svw atomically selects and flashes the native signal row, moves the native waveform cursor, and transfers focus from the plugin surface to the W panel

recorded demo — Tcl UI plugin: native signal/wave linkage

multiple Tcl panels: adaptive layout and native linkage

svw composes independent plugin windows beside the native signal/wave frame, owns their draggable and Alt+arrow dividers, and lets a plugin result reveal its typed signal and time back in the native panels

recorded demo — multiple Tcl panels: adaptive layout and native linkage

Tcl protocol checker profiles and clocked evidence

one bounded native clock-edge sampler feeds reusable Tcl checker state and normalized results; mouse or keyboard switches protocol profiles without rebuilding svw

recorded demo — Tcl protocol checker profiles and clocked evidence

X/Z detective: bounded unknown intervals and native jump

scan the selected signal over the visible range, list exact X, Z and mixed-unknown intervals, then reveal one interval in the native signal and waveform panels

recorded demo — X/Z detective: bounded unknown intervals and native jump

failure triage: regression evidence linked to waveform time

load a bounded non-executable regression failure report, review the selected failure, and jump its exact time into the native waveform

recorded demo — failure triage: regression evidence linked to waveform time

animated wait feedback

operations that cross the response threshold keep the TUI alive with an elapsed-time spinner until their result is published

recorded demo — animated wait feedback

v batch marks (VISUAL)

v or Shift+Enter marks signal rows and the status line shows VISUAL (Esc clears); :group without selectors groups the marked set, and Tab offers it as the first candidate

recorded demo — v batch marks (VISUAL)

group an expanded vector block

:expand keeps the vector and reveals bit rows; marking a child for a structural edit carries the retained parent and complete child block

recorded demo — group an expanded vector block

mouse batch marks (Ctrl/Shift+click)

Ctrl+click toggles a row's batch mark, Shift+click marks the range from the selected row (VISUAL badge); :group without selectors groups the marked set

recorded demo — mouse batch marks (Ctrl/Shift+click)

Plugin Development

Tcl UI plugins

svw loads Tcl UI plugins only when the user explicitly supplies --plugin <manifest>. Waveforms and session files never cause executable plugin code to load.

Each plugin runs in a separate user-provided tclsh process. Set SVW_TCLSH when the interpreter is not named tclsh on PATH. svw and the child exchange bounded newline-delimited JSON-RPC 2.0 using the exact protocol name svw.ui.plugin.v1; svw does not link or ship a Tcl runtime. A breaking wire change will use a new exact protocol name. Existing v1 fields and meanings are never repurposed after release.

The complete method, notification, type, quota, and compatibility contract is in Tcl UI Plugin Protocol v1. Manifest capabilities are mandatory; supported entries in optional_capabilities are granted when available. The hello response is the authority for granted capabilities and discoverable methods, notifications, limits, and enum values. Tcl plugins use ::svw::has_capability before an optional operation; ::svw::supports_method, ::svw::supports_notification and ::svw::supports_value probe additive host features.

Plugins may submit retained surfaces and atomic layout contributions. Repeat --plugin to run several independent panels in one session, for example svw wave.kbx --plugin smart/plugin.json --plugin temporal/plugin.json. svw keeps one native frame, adaptively packs all contributed windows, and owns the mouse dividers and Alt+arrow resizing between them. svw remains the sole terminal owner: plugins cannot write ANSI to the TUI, receive a tty descriptor, or access C++ objects. An opaque fullscreen surface replaces the core frame and prevents unnecessary waveform rendering until it is removed. Three consecutive Escape keys terminate the focused plugin and atomically restore the native layout even if the plugin stops responding.

:plugins lists explicitly loaded plugins, versions, states, and declared actions. :plugins focus <id> focuses its first ready surface and :plugins unload <id> stops it. These host-only operations cannot cross a plugin's core.command.invoke boundary.

A manifest may declare a separate headless.entry and bounded actions. Agent, MCP, JSON-RPC, and :plugins run adapt the same sampling/execution service and result schema; they do not duplicate protocol logic or expose a generic command bridge. clocked-samples.v1 supplies bounded clock-aligned values with explicit native or pre-edge sampling and separate event/sample times, event-samples.v1 supplies guarded named/wildcard/edge unions, extraction-plan.v1 merges independent named sources and typed payloads, property-captures.v1 supplies typed match/assert/deassert/switch evidence, signal-changes.v1 supplies a bounded final-value transition stream, and bounded-arguments.v1 forwards only the caller's size-limited JSON object to an explicitly selected trusted plugin. The protocol checker, X/Z detective, and failure-triage examples exercise all three adapters.

headless.commands adds namespaced presets without registering global : commands. With the plugin loaded, :plugins exec ready-valid top.dut_if uses the visible range. The explicit form is :plugins exec path/plugin.json ready-valid top.dut_if [start end].

The dependency-free v1 SDK and example are published from the same reviewed source as fixed static-site downloads. Save the .txt responses with the shown runtime names; they are deliberately not included in the GitHub binary Release archive:

SDK, example entry, manifest, and SHA-256 list.

curl -fLO https://svw.run/assets/svw-plugin-sdk-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-main-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-manifest-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-downloads-v1.sha256.txt
# Linux:
sha256sum -c svw-plugin-downloads-v1.sha256.txt
# macOS uses: shasum -a 256 -c svw-plugin-downloads-v1.sha256.txt
mv svw-plugin-sdk-v1.txt svw_plugin.tcl
mv svw-plugin-example-main-v1.txt main.tcl
mv svw-plugin-example-manifest-v1.txt plugin.json
svw waveform.kbx --plugin plugin.json

The tracked development copies remain in examples/tcl-ui-plugin. The static publisher copies their exact bytes and generates the checksum list; it never exports svw product source.

ui.draw, ui.focus, ui.fullscreen, ui.layout, and ui.report are independent manifest capabilities. A plugin granted core.command.invoke can call an existing public svw command through ::svw::command; internal development commands are rejected at the command-registry boundary. The host runs accepted commands through the normal command path and returns ok plus the normal message/diagnostic to the optional callback. That capability is intentionally powerful: a public command has the same mutation, file-output and quit behavior as interactive use; it is not a read-only automation channel. Capabilities protect svw API ownership; they are not an operating-system sandbox. Tcl plugins are trusted local code, and Tcl's own exec has the user's operating-system authority. v1 therefore does not claim a misleading process.exec sandbox.

core.state.read provides a bounded core.state.get snapshot, and wave.read provides the lazy point query wave.value.get, the bounded resolved-change query wave.changes.range, and the paged, direct-child hierarchy query wave.scope.children. The Tcl helpers accept an optional callback that receives the raw JSON result line. The dependency-free SDK includes a bounded structural JSON decoder; field helpers take an explicit path, for example string_field $line {result message} and string_field $line {result sample value}. They never search unrelated nested objects for a matching key, so additive response fields cannot silently change which value a callback reads. json_decode, json_get, json_type, and json_payload are available when a callback needs a value not covered by a typed field helper. The first protocol version intentionally does not expose an unbounded “all changes” or recursive hierarchy request: custom views use bounded data APIs, so a large waveform cannot silently defeat svw's lazy reader.

ui.report lets a plugin publish one bounded immutable snapshot into the native report overlay through ::svw::report_publish. The host validates and copies the complete title/line set before opening it. The copy remains valid after plugin unload and can be replaced by a later native or plugin report; report text is never executed.

core.navigation is the native-panel integration boundary. A plugin row with a signal anchor calls ::svw::reveal_signal; a row with a signal and native tick calls ::svw::reveal_wave; a time-only record calls ::svw::reveal_time. These are not command-string aliases. The host resolves and validates the whole target first, then selects and scrolls the native signal row, moves the native cursor, flashes the row/time intersection, and transfers keyboard focus from the plugin surface to the native S or W panel. Adding a signal is always an explicit boolean choice. Conversely, plugins subscribe to selection, cursor, view, and wave state groups so native panel changes refresh their bounded view.

Sixteen focused examples exercise the extension boundary:

  • examples/tcl-scope-browser adds a focusable hierarchy window beside the
  • native frame. Enter expands only the selected scope and requests one bounded page; collapsed rows show the known direct-scope count.

  • examples/tcl-python-semantic keeps Python outside svw while preserving one
  • semantic authority. Its trusted Tcl process starts a bounded Python orchestrator, displays a spinner, and renders semantic driver/load results. Python asks the configured design compiler for the canonical bundle and consumes it only through the public svw agent JSON interface; it embeds no parser or duplicate AST projection. Configure executable paths with one-line python.txt, viewer.txt, and design-compiler.txt files beside the plugin when those tools are not on PATH.

  • examples/tcl-waveform-notebook captures the current cursor, selected
  • signal, and a short user note in a bounded, non-executable sidecar. The plugin never sources sidecar content; selecting a note atomically reveals its signal and time in the native panels.

  • examples/tcl-reset-clock-health analyzes the selected one-bit signal over
  • the visible interval. Bounded change pages drive clock period/jitter/duty and reset pulse reports; a fixed transition budget marks partial results instead of draining an arbitrarily dense signal.

  • examples/tcl-x-detective finds X, Z, and mixed-unknown intervals on the
  • selected signal, reports their duration, and reveals the selected signal/time target in the native panels.

  • examples/tcl-protocol-decoder uses one native bounded clock-edge sampler
  • plus reusable Tcl rule/state/result helpers. Switchable profiles cover ready/valid, AXI4/AXI4-Lite and ACE channels, AXI4-Stream, APB, AHB-Lite, Avalon-ST/MM, Wishbone B4, a TileLink channel, request/acknowledge, and generic FIFO or ID-tagged request/response correlation. The correlators report phase, latency, outstanding depth, reset termination, unmatched endpoints, multi-beat progress, and clipped-window completeness. AXI/ACE and TileLink profiles are explicitly channel-level checks, not full ordering, burst, cache, or coherence compliance.

  • examples/tcl-failure-triage loads a bounded, non-executable regression
  • failure report, shows failure details, and links each failure time to the native waveform without letting a log activate plugin code. An optional baseline report turns the same pane into a new/common/fixed regression diff.

  • examples/tcl-uvm-timeline filters phase, objection, sequence,
  • configuration, resource, and register activity from a bounded simulator export aligned to the waveform's native time axis.

  • examples/tcl-coverage-holes browses bounded coverage exports and reuses
  • the public coverage source command for the selected file and line.

  • examples/tcl-temporal-query evaluates a finite rise/fall/unknown/equals
  • query language over one selected signal and the visible interval.

  • examples/tcl-smart-watch sequentially samples at most 64 explicit signal
  • paths at the cursor without walking the waveform schema.

  • examples/tcl-memory-inspector lazily samples only the visible page of an
  • explicit address-to-signal memory map.

  • examples/tcl-fsm-visualizer decodes bounded state transitions and directed
  • edge counts from a value-to-state map.

  • examples/tcl-simulator-control owns a capability-negotiated external
  • simulator adapter after explicit user connect and keeps requests bounded.

  • examples/tcl-debug-recipes previews non-executable command recipes and
  • requires a second explicit action before sequential public-command execution.

  • examples/tcl-workflow-console demonstrates plugin-local actions, public
  • command composition, and atomic native report publication without dynamic global command registration.

The Python example is an integration pattern, not a new svw dependency or a second design database contract. Production integrations should consume the same elaborated source set and options used to build their design data; the small sources.f and demo.sv exist only to make the example reproducible.

The Tcl SDK writes protocol messages to stdout, so plugin logs must go to stderr. Its event handlers receive dictionaries for ui.resize, ui.layout, ui.input, and subscribed state.changed notifications. SDK drawing helpers buffer operations until surface_commit; one bounded ui.surface.update request then validates and publishes the complete generation atomically. RPC helpers return their request id. Core, waveform and surface commit helpers accept an optional callback; surface creation accepts it as -callback, and the other resource/layout/focus/subscription helpers accept it as their final optional argument. SDK option lists reject unknown names rather than silently accepting a misspelling. Host-owned UI resource effects from one plugin commit in receive order, while dependent Core operations must be issued from the first operation's callback. Protocol colors are default, palette:N, or #RRGGBB, never svw's internal integer encoding. Surface and layout ids remain plugin-local on the wire. An ui.layout dictionary includes placed, current generation, and the effective rectangle; node is non-empty only while the surface is embedded in the active layout. A false placed value therefore lets a plugin redraw the surface at its declared rectangle after layout restore instead of guessing from a missing event. Panels must redraw from this effective rectangle rather than assuming their requested cells remained exact: another plugin, a terminal resize, or a user divider drag may legitimately change both width and height. After the hello response, ::svw::limit <name> returns the negotiated session limit so a plugin does not need to embed host constants.

Tcl UI Plugin Protocol v1 Reference

This is the compatibility reference for svw.ui.plugin.v1. The protocol is used only for plugins explicitly selected with svw --plugin <manifest>. Waveform and session files never activate plugins.

Compatibility

Every frame is one UTF-8 JSON-RPC 2.0 object followed by a newline. The maximum frame is 1 MiB. Empty lines, top-level JSON-RPC batches, and plugin notifications are not accepted in v1. A request is:

{"jsonrpc":"2.0","id":1,"method":"session.ping","params":{}}

The response contains the same id and exactly one of result or error. When a parse or invalid-request failure prevents the host from recovering a valid id, the error response uses id:null. An id is either a UTF-8 string of at most max_id_bytes or a signed 64-bit integer; floating-point ids and null are rejected. A plugin must not reuse an id while its response is pending. Plugins must ignore unknown result members and unknown host notifications. Requests are schema-strict: unknown envelope or params members are rejected so a misspelling cannot silently change behavior. Published v1 names, field types and semantics are not removed or repurposed; a breaking protocol uses a new exact name.

Compatibility is intentionally asymmetric. Request envelopes, params, and objects nested inside params are closed and schema-strict, so adding a request member to an existing v1 method is not a compatible extension. Result objects and their nested objects, error data, manifest root members, and host-notification params and their nested objects are open: receivers ignore members they do not know. New methods, notifications, capabilities and registered values may be added; plugins discover them from session.hello before use. A new value never takes on the meaning of an existing value.

Responses may be delayed and may arrive in a different order from requests. Host notifications such as ui.resize may appear between a request and its response. A client correlates only by id; it must continue dispatching host notifications while a core or waveform request is pending.

Host-owned UI resource, layout, focus and subscription effects from one plugin are committed in frame-receive order. A later such request therefore observes the successful effects of earlier such requests, which makes create–draw–focus and restore–destroy sequences deterministic. This guarantee does not order a deferred Core or waveform operation against later requests; when one Core result is an input to another request, the plugin sends the dependent request from the first request's callback.

Errors contain a numeric code, stable name, human-readable message, and may gain object-valued data. Plugins branch on name, not message.

Error name Code
parse_error -32700
invalid_request -32600
method_not_found -32601
invalid_params -32602
internal_error -32603
capability_denied -32001
conflict -32002
not_found -32003
unavailable -32004
quota -32005

Manifest and handshake

{
  "id": "example-ui",
  "version": "1.0.0",
  "entry": "main.tcl",
  "protocol": "svw.ui.plugin.v1",
  "capabilities": ["ui.draw", "ui.focus"],
  "optional_capabilities": ["ui.layout"]
}

The entry must be a regular file inside the manifest directory. Capabilities in capabilities are required: an unknown or unavailable required capability rejects the manifest. optional_capabilities may name a capability unknown to an older v1 host; known items are granted and unknown items are ignored. The plugin checks the hello result's granted capabilities before using an optional feature. Duplicate names within or across the two arrays are rejected. v1 capabilities are core.command.invoke, core.navigation, core.state.read, wave.read, ui.draw, ui.focus, ui.fullscreen, ui.layout, and ui.report. Plugin and local resource ids use [A-Za-z0-9][A-Za-z0-9._-]*; entry paths reject control/NUL bytes and are canonicalized before the host verifies that they remain inside the manifest directory. version is opaque plugin metadata of at most 64 ASCII bytes using letters, digits, ., -, or +; svw does not infer protocol compatibility from it. A manifest is at most 64 KiB and the required-plus-optional capability count is at most 32.

The plugin process receives a scrubbed environment: the host keeps only its documented runtime baseline and variables whose names start with SVW_PLUGIN_CONFIG_. That prefix is an explicit user opt-in for non-secret plugin configuration, not a security boundary; plugins remain trusted local code. Arbitrary parent variables and secrets are not forwarded. The forwarded configuration is bounded to 64 variables and 64 KiB in total.

An optional headless declaration is independent of the UI handshake:

"headless": {
  "entry": "headless.tcl",
  "actions": [{"id":"check", "summary":"Bounded check",
               "input":"clocked-samples.v1"}],
  "commands": [{"id":"ready-valid", "summary":"Short check",
                "action":"check", "profile":"ready-valid", "clock":"clk",
                "roles":{"valid":"valid", "ready":"ready", "data":"data"}}]
}

The entry follows the same canonical in-directory regular-file rule. There are at most 16 unique actions; ids use the plugin id grammar and summaries are at most 160 bytes. An explicitly launched headless process receives one bounded JSON line, returns one svw.plugin.action-result.v1 line, and is terminated. Action input is one of clocked-samples.v1, event-samples.v1, extraction-plan.v1, property-captures.v1, signal-changes.v1, or bounded-arguments.v1. The extraction adapter merges named independent event sources, typed predicates, sample modes, and payloads into one bounded table. The property adapter accepts a typed expression, match|assert|deassert|switch, range, timestamp policy, and bounded limit; the host sends filtered captures, truth states, operands, and completeness. The event adapter accepts a bounded named/wildcard/edge union with per-term iff, explicit native or pre-edge sampling, and a role-to-signal tracked map. The host performs bounded waveform sampling before Tcl runs. The last adapter forwards the bounded JSON argument object unchanged and is intended for explicitly loaded trusted plugins that validate project artifacts themselves. There are at most 32 unique namespaced commands. Each targets a declared action and stores a profile plus relative clock/role leaves. plugins exec joins those leaves to one exact caller-supplied scope and defaults to the current visible range; optional paired start/end override it.

The base capability mapping is part of the frozen method registry:

Method Required capability
session.hello -
session.ping -
session.shutdown -
core.command.invoke core.command.invoke
core.signal.reveal core.navigation
core.time.reveal core.navigation
core.wave.reveal core.navigation
core.state.get core.state.read
event.subscribe core.state.read
event.unsubscribe core.state.read
ui.focus.release ui.focus
ui.focus.request ui.focus
ui.layout.replace ui.layout
ui.layout.restore ui.layout
ui.report.publish ui.report
ui.surface.create ui.draw
ui.surface.destroy ui.draw
ui.surface.update ui.draw
wave.changes.range wave.read
wave.samples.clocked wave.read
wave.scope.children wave.read
wave.value.get wave.read

Parameter-selected authority is also frozen and machine checked:

Method Condition Additional required capability
ui.surface.create mode=fullscreen ui.fullscreen
ui.surface.create focusable=true ui.focus

The parameter and result object of every method are frozen as well. Scheduling is deliberately not exposed: any response may be deferred or reordered, so a plugin always correlates it by id and continues processing notifications.

Method Params object Result object
session.hello session.hello.params session.hello.result
session.ping empty empty
session.shutdown empty empty
core.command.invoke core.command.invoke.params core.command.invoke.result
core.signal.reveal core.signal.reveal.params core.signal.reveal.result
core.time.reveal core.time.reveal.params core.time.reveal.result
core.wave.reveal core.wave.reveal.params core.wave.reveal.result
core.state.get empty core.state.get.result
event.subscribe event.subscription.params empty
event.unsubscribe event.subscription.params empty
ui.focus.release empty empty
ui.focus.request ui.focus.request.params empty
ui.layout.replace ui.layout.replace.params empty
ui.layout.restore empty empty
ui.report.publish ui.report.publish.params ui.report.publish.result
ui.surface.create ui.surface.create.params ui.surface.create.result
ui.surface.destroy ui.surface.destroy.params empty
ui.surface.update ui.surface.update.params ui.surface.update.result
wave.changes.range wave.changes.range.params wave.changes.range.result
wave.samples.clocked wave.samples.clocked.params wave.samples.clocked.result
wave.scope.children wave.scope.children.params wave.scope.children.result
wave.value.get wave.value.get.params wave.value.get.result

The complete v1 object-field contract is frozen below. Objects not listed as having fields use an empty params or result object as described by their method. conditional means the field is required only in the condition stated in the surrounding prose.

Object Field Type Presence
manifest id id required
manifest version version required
manifest entry relative-path required
manifest protocol protocol required
manifest capabilities capability[] required
manifest optional_capabilities capability[] optional
request jsonrpc literal:2.0 required
request id string-or-int64 required
request method method required
request params object required
response jsonrpc literal:2.0 required
response id string-or-int64-or-null required
response result object conditional
response error error conditional
notification jsonrpc literal:2.0 required
notification method notification required
notification params object required
error code int required
error name error-name required
error message string required
error data object optional
session.hello.params protocol protocol required
session.hello.params plugin id required
session.hello.result protocol protocol required
session.hello.result plugin id required
session.hello.result capabilities capability[] required
session.hello.result limits limit-map required
session.hello.result methods method[] required
session.hello.result notifications notification[] required
session.hello.result values value-map required
core.command.invoke.params command string required
core.command.invoke.result ok bool required
core.command.invoke.result message string required
core.signal.reveal.params signal string required
core.signal.reveal.params add bool required
core.signal.reveal.result signal string required
core.signal.reveal.result row nonnegative-int64 required
core.signal.reveal.result added bool required
core.signal.reveal.result filter_cleared bool required
core.time.reveal.params time int64 required
core.time.reveal.result time int64 required
core.wave.reveal.params signal string required
core.wave.reveal.params time int64 required
core.wave.reveal.params add bool required
core.wave.reveal.result signal string required
core.wave.reveal.result time int64 required
core.wave.reveal.result row nonnegative-int64 required
core.wave.reveal.result added bool required
core.wave.reveal.result filter_cleared bool required
core.state.get.result state state required
event.subscription.params events state-group[] required
wave.value.get.params signal string required
wave.value.get.params time int64 required
wave.value.get.result sample wave-sample required
wave.changes.range.params signal string required
wave.changes.range.params start int64 required
wave.changes.range.params end int64 required
wave.changes.range.params cursor string required
wave.changes.range.params limit positive-int required
wave.changes.range.result page wave-changes-page required
wave.samples.clocked.params clock string required
wave.samples.clocked.params signals string[] required
wave.samples.clocked.params start int64 required
wave.samples.clocked.params end int64 required
wave.samples.clocked.params edge clock-edge required
wave.samples.clocked.params cursor string required
wave.samples.clocked.params limit positive-int required
wave.samples.clocked.result page clocked-samples-page required
wave.scope.children.params scope string required
wave.scope.children.params offset nonnegative-int64 required
wave.scope.children.params limit positive-int required
wave.scope.children.result page wave-scope-page required
ui.report.publish.params title string required
ui.report.publish.params lines string[] required
ui.report.publish.result rows nonnegative-int64 required
state file string required
state format string required
state timescale string required
state minimum_time int64 required
state maximum_time int64 required
state cursor int64 required
state view_start int64 required
state view_end int64 required
state focus focus-value required
state selected_signal string required
state wave_revision uint64 required
state wave_loading bool required
state wave_progress int:0..100 required
wave-sample signal string required
wave-sample time int64 required
wave-sample width uint32 required
wave-sample kind wave-kind required
wave-sample value string required
wave-changes-page signal string required
wave-changes-page start int64 required
wave-changes-page end int64 required
wave-changes-page cursor string required
wave-changes-page next_cursor string required
wave-changes-page width uint32 required
wave-changes-page has_more bool required
wave-changes-page changes wave-change[] required
clocked-samples-page clock string required
clocked-samples-page signals string[] required
clocked-samples-page start int64 required
clocked-samples-page end int64 required
clocked-samples-page edge clock-edge required
clocked-samples-page cursor string required
clocked-samples-page next_cursor string required
clocked-samples-page has_more bool required
clocked-samples-page samples clocked-sample[] required
clocked-sample time int64 required
clocked-sample values wave-sample[] required
wave-change time int64 required
wave-change kind wave-kind required
wave-change value string required
wave-scope-page scope string required
wave-scope-page offset nonnegative-int64 required
wave-scope-page next_offset nonnegative-int64 required
wave-scope-page known_count nonnegative-int64 required
wave-scope-page count_exact bool required
wave-scope-page has_more bool required
wave-scope-page children wave-scope-child[] required
wave-scope-child name string required
wave-scope-child path string required
wave-scope-child subscope_count nonnegative-int64 required
wave-scope-child subscope_count_exact bool required
ui.surface.create.params surface id required
ui.surface.create.params mode surface-mode optional
ui.surface.create.params x int required
ui.surface.create.params y int required
ui.surface.create.params width positive-int required
ui.surface.create.params height positive-int required
ui.surface.create.params z int required
ui.surface.create.params opaque bool optional
ui.surface.create.params focusable bool optional
ui.surface.create.result generation uint64 required
ui.surface.update.params surface id required
ui.surface.update.params clear bool optional
ui.surface.update.params expected_generation nonnegative-int64 optional
ui.surface.update.params ops draw-op[] required
ui.surface.update.result generation uint64 required
ui.surface.destroy.params surface id required
ui.focus.request.params surface id required
text-op op literal:text required
text-op x int required
text-op y int required
text-op text string required
text-op fg color optional
text-op bg color optional
fill-op op literal:fill required
fill-op x int required
fill-op y int required
fill-op width nonnegative-int required
fill-op height nonnegative-int required
fill-op fg color optional
fill-op bg color optional
ui.layout.replace.params root layout-node required
layout-leaf id id required
layout-leaf kind literal:leaf optional
layout-leaf view view required
layout-leaf cells positive-int optional
layout-leaf weight positive-int optional
layout-leaf min_width positive-int optional
layout-leaf min_height positive-int optional
layout-container id id required
layout-container kind container-kind required
layout-container children layout-node[] required
layout-container cells positive-int optional
layout-container weight positive-int optional
layout-container min_width positive-int optional
layout-container min_height positive-int optional
layout-container gap nonnegative-int conditional
state.changed.params revision uint64 required
state.changed.params changed state-group[] required
state.changed.params state state required
ui.resize.params width positive-int required
ui.resize.params height positive-int required
ui.focus.changed.params focused bool required
ui.focus.changed.params surface id conditional
ui.layout.params revision uint64 required
ui.layout.params surface id required
ui.layout.params placed bool required
ui.layout.params node id conditional
ui.layout.params generation uint64 required
ui.layout.params x int required
ui.layout.params y int required
ui.layout.params width positive-int required
ui.layout.params height positive-int required
ui.input.params surface id required
ui.input.params key input-key required
ui.input.params modifiers modifier[] required
ui.input.params character string conditional
ui.input.params mouse_kind mouse-kind conditional
ui.input.params button mouse-button conditional
ui.input.params x int conditional
ui.input.params y int conditional

Creating a fullscreen surface additionally requires ui.fullscreen; creating a focusable surface additionally requires ui.focus.

The first request must be session.hello with protocol and plugin matching the manifest. The result returns the selected protocol, plugin id, granted capabilities, immutable host limits, supported methods, supported notifications, and a values object containing the supported values for each registered category. Plugins use these lists to probe additive v1 features instead of parsing an application version. session.ping has empty params. session.shutdown atomically releases the caller's surfaces, layout and focus.

The limits object is the authoritative bound for that session. Plugins read these values rather than embed current numbers. A compatible v1 host may raise a limit, but must not silently accept a request and truncate it.

Limit Meaning
max_message_bytes Maximum UTF-8 bytes in one inbound protocol frame.
max_string_bytes Maximum bytes in a bounded identifier/path/command string.
max_text_bytes Maximum bytes in one drawing text, command diagnostic, or waveform value.
max_surface_ops Maximum drawing operations in one atomic update.
max_surfaces Maximum live surfaces in the host registry.
max_surface_cells Maximum aggregate cell capacity of all live surfaces.
max_surface_update_cells Maximum clipped cells touched by one update.
max_surface_dimension Maximum surface dimension and absolute coordinate bound.
max_surface_z Maximum absolute surface z value.
max_layout_nodes Maximum nodes in one retained layout.
max_layout_depth Maximum root-relative layout depth.
max_layout_children Maximum children of one layout container.
max_layout_weight Maximum flex weight.
max_layout_gap Maximum inter-child gap in cells.
max_id_bytes Maximum bytes in a request or resource id.
max_subscriptions Maximum state groups in one subscription request.
max_messages_per_second Maximum inbound frames per plugin rate window.
max_pending_core_requests Maximum unfinished core requests accepted from one plugin.
max_scope_children_page Maximum direct child scopes returned by one hierarchy request.
max_wave_changes_page Maximum resolved change points returned by one range request.
max_clocked_sample_signals Maximum digital signals sampled together at one clock edge.
max_clocked_samples_page Maximum clock edges returned by one clocked sample request.
max_report_lines Maximum rows in one immutable native report snapshot.
max_report_bytes Maximum decoded UTF-8 bytes across one native report's rows.
handshake_timeout_ms Time allowed for the initial session.hello request.
recovery_escape_count Consecutive Escape keys reserved for emergency plugin recovery.
recovery_sequence_ms Maximum interval between emergency recovery Escape keys.

Core and waveform methods

  • core.command.invoke (core.command.invoke capability): params contain one
  • non-empty command string bounded by max_string_bytes, without a leading-shell interpretation. One optional leading : is ignored. Only commands marked public by the installed host command registry, including public focused-panel direct forms, may execute; internal development commands are rejected. This is the normal command path, so commands can mutate the session, open data, write command-defined files, or request host exit with the same behavior as interactive use. A completed invocation always returns result fields ok and message; a normal command diagnostic has ok:false and is not confused with a JSON-RPC transport/capability error. Public command syntax remains discoverable from the installed host; the method does not turn every command grammar into an independently frozen plugin method.

  • core.signal.reveal (core.navigation): resolves one exact, format-neutral
  • signal path, selects and scrolls its row into view in the native signal panel, flashes the row, releases plugin-surface focus, and transfers keyboard focus to that native panel. add:false requires the signal to be present; add:true may append it explicitly. A hiding signal filter is cleared with an undo snapshot, and the result reports row, added, and filter_cleared.

  • core.wave.reveal (core.navigation): performs the same exact signal-row
  • reveal and atomically moves the native waveform cursor to the in-range native-tick time, keeps that time visible, flashes the signal/time target, and transfers focus to the native wave panel. Signal resolution and time validation finish before any visible state is changed; a failed request cannot leave a partially added signal or moved cursor.

  • core.time.reveal (core.navigation): moves and focuses the native wave
  • panel at one in-range native tick when a plugin record has no meaningful signal anchor. If a native signal row is selected, its time intersection is flashed; no signal-list mutation is performed.

  • core.state.get (core.state.read): returns state with file, format,
  • timescale, minimum_time, maximum_time, cursor, view_start, view_end, focus, selected_signal, wave_revision, wave_loading, and integer wave_progress in 0..100.

  • wave.scope.children (wave.read): returns one bounded page of direct child
  • scopes. An empty scope selects the waveform root; otherwise scope is a previously discovered stable scope path. offset is a nonnegative child index and limit is 1..max_scope_children_page. The result echoes scope and offset, returns next_offset, and never includes signals or descendants. known_count and each subscope_count describe the hierarchy frontier currently known to the lazy reader; the matching *_exact flag is false until that count is complete. has_more means the caller may request next_offset; one request admits at most one bounded backend schema page, even when that page reveals no new direct child scope.

  • wave.value.get (wave.read): params contain a stable signal path and signed
  • 64-bit native-tick time. The sample result contains signal, time, width, kind, and value. This is a bounded, format-neutral point lookup; it does not enumerate the signal catalog or return a transition collection. Completion latency is deliberately not a wire guarantee: a backend with an indexed point operation may answer directly, while a generic compatible backend may scan that signal. Plugins continue serving notifications and correlate the eventual response by id.

  • wave.changes.range (wave.read): returns a bounded page of resolved value
  • changes for one stable signal path in the inclusive native-tick interval start..end. limit is 1..max_wave_changes_page. The first request uses an empty cursor; while has_more is true, pass the returned opaque next_cursor with the same signal and interval. Times are strictly increasing across pages. Multiple delta writes at one timestamp are intentionally collapsed to the final visible value, matching the normal waveform display; delta-cycle analysis remains a separate data contract. The host touches only the named signal and at most one bounded page, so the request cost is independent of total hierarchy cardinality. v1 still has no unbounded signal, recursive hierarchy, transition, or time-range dump.

  • wave.samples.clocked (wave.read): samples the final values of up to
  • max_clocked_sample_signals exact digital signal paths at rising or falling edges of one exact one-bit clock. The interval is inclusive, each page is bounded by max_clocked_samples_page, and the opaque cursor excludes the last returned edge. Host-side edge alignment gives Tcl checkers one common same-timestamp policy without materializing every role's transition stream.

Core commands and waveform reads may be deferred and may complete in a different order from requests; clients must not infer host scheduling from observed response timing. A slow reader request does not stop protocol I/O or the wait animation. The number of unfinished core requests is bounded by the negotiated limit, and duplicate pending ids are rejected. Once accepted, a core operation has at-most-once semantics and may finish even if its caller disconnects; unloading a UI is not a rollback mechanism for an already started command.

Native report publication

ui.report.publish requires ui.report. Params contain a non-empty bounded title and a non-empty lines string array. Each line is independently bounded by max_text_bytes; the complete snapshot is bounded by max_report_lines and max_report_bytes. The host validates the complete request before atomically replacing and opening the native report overlay, and returns the accepted rows count. Tab-separated lines use the native table renderer; all text still passes through the terminal-control filter.

The report is an immutable copy owned by the host after publication. Plugin unload does not close or invalidate it, and a later native command or plugin may replace it. Publication never executes report text and does not register a command, key, completion, or help entry. Dynamic global : command registration is intentionally absent from v1: its namespace, collision, help/completion, tutorial, unload and in-flight invocation lifecycle remain a separate future contract. Manifest headless.commands are bounded presets under the host-owned :plugins exec namespace. Plugins may also expose local surface actions and compose existing public commands through core.command.invoke.

Surfaces

All surface ids are local to the caller and are never returned with an internal owner prefix.

ui.surface.create requires ui.draw and these params: surface, x, y, width, height, and z. Optional mode is overlay, modal, or fullscreen; fullscreen additionally requires ui.fullscreen. Docking is a layout operation, not a fourth surface mode. Optional opaque and focusable default false; a focusable surface requires ui.focus. The result generation is zero, meaning not yet published. Surfaces with different z values compose and hit-test in ascending z order; ordering between equal-z surfaces is deliberately unspecified, so a plugin assigns distinct z values whenever overlap matters. An overlay receives mouse input only inside its committed rectangle. A focused modal or fullscreen surface also receives mouse input outside that rectangle; its local coordinates can therefore be negative or beyond its dimensions. An opaque fullscreen surface that covers the viewport suppresses core-frame rendering until it is removed or resized away from full coverage.

The live registry's aggregate surface capacity is bounded by max_surface_cells; allocation can therefore return quota when another surface already consumes the budget. ui.surface.update requires ui.draw, surface, and bounded ops. Optional clear defaults false. Optional expected_generation rejects stale drawing. The complete request is validated against a copy, then atomically published as one new generation. ops contains at most max_surface_ops operations and a text value contains at most max_text_bytes UTF-8 bytes. Supported operations are:

{"op":"text","x":2,"y":1,"text":"hello","fg":"palette:12","bg":"default"}
{"op":"fill","x":0,"y":0,"width":20,"height":3,"fg":"#C8D3F5","bg":"#222436"}

Colors are exactly default, palette:N where N is 0..255, or #RRGGBB. Coordinates are terminal cells. Text is UTF-8, host-width measured and clipped. Tabs advance to the next eight-cell stop; other terminal control codepoints are neutralized at the Frame boundary and can never become raw terminal control sequences. fill replaces the affected glyphs with spaces while applying its colors. An invalid operation leaves the current generation unchanged. The sum of clipped cells touched by all operations is bounded by max_surface_update_cells, preventing overlapping large fills from turning a small request into unbounded raster work.

ui.surface.destroy requires ui.draw and a local surface id. It also releases focus if that surface held the focus lease. A surface referenced by the active layout cannot be destroyed; the plugin restores or replaces that layout first, so no accepted request leaves a dangling visible leaf.

Layout and focus

ui.layout.replace requires ui.layout and one root. The complete tree is validated and installed atomically. Node kind is leaf (default), row, column, or stack. Every node needs a unique local id. Leaves use either builtin.frame or surface:<local-id> as view. Containers use children. Optional constraints are cells, weight, min_width, and min_height; row and column containers additionally accept gap; stack containers reject it because their children overlap. When present, cells, min_width, and min_height are positive integers—there is no public negative sentinel. A leaf requires exactly one view and no children; a container requires non-empty children and no view. A surface and builtin.frame may each appear at most once because each live view has one resolved rectangle. No other builtin.* name exists in v1, and plugins must not guess one. Native chrome, signal and wave leaves can be added later only as discoverable registered values after they have independent renderer, overlay and input geometry semantics. Plugins use negotiated limits rather than relying on current numeric values. Each plugin owns only its submitted layout contribution. A second plugin does not replace or reject the first contribution: the host keeps the native frame once, extracts each plugin's surface subtree, and adaptively packs independent plugin windows into bounded rows and columns. A single contribution retains its exact v1 tree, including an intentional fullscreen layout. With multiple contributors the core owns the aggregate tree and reflows panels according to their declared minimums; no plugin can position or resize another plugin's surface. ui.layout.restore removes only the caller's contribution. A surface embedded by a layout temporarily uses the resolved leaf rectangle; when it is no longer placed, it returns to the rectangle declared by ui.surface.create. Either resize invalidates the prior generation and requires redraw, so stale dock content cannot remain over the restored core UI.

Rows allocate the horizontal axis and columns the vertical axis. A child with cells gets that fixed primary-axis size subject to its minimum; other children receive their minimum plus the remaining cells in weight proportion and declaration-order remainder. gap is inserted only between placed children. Stack children share one rectangle; the core frame is composed first and plugin surfaces retain their explicit z ordering. Omitting builtin.frame suppresses native-frame rendering. If a later terminal resize cannot satisfy minimums, the host keeps the last valid placement, reports the resize, and lets the plugin submit a replacement layout.

The separators produced by a row or column are host-owned controls. Dragging a separator with the left mouse button resizes its adjacent native/plugin or plugin/plugin windows while honoring both minimums. While a plugin surface is focused, Alt+Left/Right resizes the nearest horizontal split by two cells and Alt+Up/Down resizes the nearest vertical split by one row. These successful resize gestures are reserved by the host and are not delivered as ui.input; the resulting ui.layout notifications are the plugin's redraw contract.

ui.focus.request requires ui.focus and an owned, committed, focusable surface. A mouse press on a committed focusable surface also acquires its focus lease. A programmatic request may move focus between the caller's surfaces but returns conflict instead of stealing another plugin's lease; an explicit user mouse press may transfer focus. ui.focus.release is idempotent when no plugin is focused but cannot release another plugin's lease. The negotiated number of consecutive Escape keys, each no more than recovery_sequence_ms after the previous one, is reserved for host recovery. The earlier Escape keys are delivered normally; the final one is consumed, then the host terminates that plugin and atomically removes its surfaces, layout and focus lease. Any non-Escape input or an expired interval resets the sequence.

Subscriptions and notifications

event.subscribe and event.unsubscribe require core.state.read. Their non-empty events array accepts cursor, view, selection, focus, and wave. The first host pass after a new subscription sends a snapshot even if the value did not just change. Later unchanged passes are coalesced.

Host notifications are JSON-RPC requests without id:

  • state.changed: monotonic revision, subscribed changed groups, and the
  • typed state snapshot.

  • ui.resize: terminal width and height.
  • ui.focus.changed: focused and, when true, the focused local surface.
  • It is sent initially and whenever that plugin gains, moves, or loses its focus lease, including an outside mouse click.

  • ui.layout: one notification for each surface owned by the plugin whenever
  • the layout revision changes. It carries local surface, revision, current generation, placed, and effective x, y, width, height. node is present only when placed is true. A placed:false notification after restore/removal explicitly reports the return to the surface's declared rectangle; generation zero requires redraw.

  • ui.input: local surface, normalized key, modifiers, optional
  • character, and mouse fields when applicable. modifiers is an array, not an internal bit mask. Mouse x and y are zero-based surface-local cells; button is a name and is present only for press/release/drag.

Resize, resolved-layout and focused-input notifications are resource lifecycle events and do not require state subscription. The focus field in a core state snapshot is the last native signal/wave panel focus; plugin focus lease changes are reported only by ui.focus.changed.

The following wire values are the v1 baseline. Hosts may add values where the surrounding result/notification is documented as extensible; plugins handle an unknown value without treating it as an existing one.

Category Value
state_group cursor
state_group view
state_group selection
state_group focus
state_group wave
surface_mode overlay
surface_mode modal
surface_mode fullscreen
layout_kind leaf
layout_kind row
layout_kind column
layout_kind stack
draw_op text
draw_op fill
builtin_view builtin.frame
clock_edge rising
clock_edge falling
input_key char
input_key up
input_key down
input_key left
input_key right
input_key home
input_key end
input_key page-up
input_key page-down
input_key delete
input_key f2
input_key mouse
input_key enter
input_key backspace
input_key escape
input_key unknown
input_modifier shift
input_modifier alt
input_modifier control
mouse_kind press
mouse_kind release
mouse_kind drag
mouse_kind wheel-up
mouse_kind wheel-down
mouse_kind move
mouse_button left
mouse_button middle
mouse_button right
focus_value signal
focus_value wave
wave_kind bits
wave_kind real
wave_kind string

Tcl SDK v1 procedures

The following dependency-free Tcl procedures are the stable v1 SDK baseline. New helpers may be added compatibly; existing names, positional arguments and defaults are not removed or repurposed. Names containing an underscore-prefixed namespace component are implementation details and are not SDK API.

Procedure Arguments
::svw::quote value
::svw::request method params ?callback?
::svw::has_capability name
::svw::limit name
::svw::supports_method name
::svw::supports_notification name
::svw::supports_value category value
::svw::ping ?callback?
::svw::surface_create id x y width height ?option value ...?
::svw::surface_clear id
::svw::surface_text id x y text ?option value ...?
::svw::surface_fill id x y width height ?option value ...?
::svw::surface_commit id ?callback?
::svw::surface_destroy id ?callback?
::svw::focus id ?callback?
::svw::focus_release ?callback?
::svw::layout_replace root-json ?callback?
::svw::layout_restore ?callback?
::svw::layout::leaf id view ?option value ...?
::svw::layout::row id children ?option value ...?
::svw::layout::column id children ?option value ...?
::svw::layout::stack id children ?option value ...?
::svw::ui::label surface x y text ?option value ...?
::svw::ui::box surface x y width height ?option value ...?
::svw::ui::progress surface x y width fraction ?option value ...?
::svw::ui::list surface x y values ?option value ...?
::svw::ui::button surface x y label ?option value ...?
::svw::ui::input surface x y width value ?option value ...?
::svw::ui::tabs surface x y labels selected ?option value ...?
::svw::ui::table surface x y headers rows ?option value ...?
::svw::ui::tree surface x y nodes ?option value ...?
::svw::command command ?callback?
::svw::reveal_signal signal ?add? ?callback?
::svw::reveal_time time ?callback?
::svw::reveal_wave signal time ?add? ?callback?
::svw::report_publish title lines ?callback?
::svw::state_get ?callback?
::svw::subscribe events ?callback?
::svw::unsubscribe events ?callback?
::svw::wave_value signal time ?callback?
::svw::wave_changes signal start end cursor limit ?callback?
::svw::wave_samples_clocked clock signals start end edge cursor limit ?callback?
::svw::wave_scope_children scope offset limit ?callback?
::svw::shutdown -
::svw::on event callback
::svw::bind key callback
::svw::unbind key
::svw::timer milliseconds callback
::svw::timer_cancel token
::svw::json_decode document
::svw::json_get document path
::svw::json_type value
::svw::json_payload value
::svw::string_field line path
::svw::number_field line path
::svw::bool_field line path
::svw::string_array_field line path
::svw::run -

Tcl UI 插件

只有用户显式传入 --plugin <manifest> 时,svw 才会加载 Tcl UI 插件。 波形文件和会话文件永远不会触发可执行插件代码。

每个插件都在用户提供的独立 tclsh 进程中运行。如果解释器在 PATH 中的名称 不是 tclsh,请设置 SVW_TCLSH。svw 与子进程使用精确协议名 svw.ui.plugin.v1,通过有界、按换行分帧的 JSON-RPC 2.0 通信;svw 不链接、 也不分发 Tcl 运行时。任何破坏兼容性的线协议变更都会使用新的精确协议名。 v1 发布后,已有字段和语义永远不会被挪作他用。

完整的方法、通知、类型、配额和兼容性约定见 Tcl UI 插件协议 v1 参考。manifest 中的 capabilities 是强制要求;optional_capabilities 中受当前 host 支持的项目会被 授予。hello 响应是已授予能力、可发现方法、通知、限制和枚举值的唯一权威来源。 Tcl 插件应在可选操作前调用 ::svw::has_capability;使用 ::svw::supports_method、::svw::supports_notification 和 ::svw::supports_value 探测 v1 中可兼容追加的 host 能力。

插件可以提交 retained surface 和原子布局贡献。重复传入 --plugin 即可在同一会话 运行多个独立 panel,例如 svw wave.kbx --plugin smart/plugin.json --plugin temporal/plugin.json。svw 只保留一份原生 frame,自适应排布所有贡献窗口,并持有 窗口间的鼠标分隔线和 Alt+方向键缩放。svw 始终是终端的唯一所有者: 插件不能向 TUI 写 ANSI、不能得到 tty 描述符,也不能访问 C++ 对象。不透明的 全屏 surface 会替代核心 frame,并在移除前避免无意义的波形渲染。即使插件停止 响应,连续按三次 Escape 也会终止当前聚焦插件,并原子恢复原生布局。

:plugins 显示显式加载插件的版本、状态和 manifest 动作;:plugins focus <id> 聚焦其 首个已就绪 surface,:plugins unload <id> 停止插件。这些 host-only 操作不能通过插件的 core.command.invoke 边界。

manifest 还可声明独立的 headless.entry 和有界动作。Agent、MCP、JSON-RPC 与 :plugins run 只是同一采样/执行服务和结果 schema 的适配器,不分别复制协议逻辑,也不提供 通用命令桥。clocked-samples.v1 提供有界的时钟沿对齐值,可显式选择 native 或 pre-edge,并分别返回事件时间与采样时间, event-samples.v1 提供带 guard 的 named/wildcard/edge union, extraction-plan.v1 合并独立的命名 source 与类型化 payload, property-captures.v1 提供类型感知的 match/assert/deassert/switch 证据, signal-changes.v1 提供有界且同一时间只保留终值的 transition stream, bounded-arguments.v1 只把调用方受消息大小限制的 JSON object 转交给显式选择的 受信任插件。协议检查器、X/Z 定位和回归分诊示例分别覆盖这三种适配器。

headless.commands 可声明 namespaced 短命令预设,但不会注册全局 : 命令。插件已加载 时可直接执行 :plugins exec ready-valid top.dut_if,默认检查可见范围;显式形式是 :plugins exec path/plugin.json ready-valid top.dut_if [start end]。

无外部依赖的 v1 SDK 和示例来自同一份已审阅源码,并以固定静态站点下载物发布。 请把 .txt 响应保存为下面展示的运行时文件名。GitHub 二进制 Release 归档不会 包含这些文件:

SDK、 示例入口、 manifest 和 SHA-256 清单。

curl -fLO https://svw.run/assets/svw-plugin-sdk-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-main-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-manifest-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-downloads-v1.sha256.txt
# Linux:
sha256sum -c svw-plugin-downloads-v1.sha256.txt
# macOS 使用:shasum -a 256 -c svw-plugin-downloads-v1.sha256.txt
mv svw-plugin-sdk-v1.txt svw_plugin.tcl
mv svw-plugin-example-main-v1.txt main.tcl
mv svw-plugin-example-manifest-v1.txt plugin.json
svw waveform.kbx --plugin plugin.json

受 Git 跟踪的开发副本位于 examples/tcl-ui-plugin。静态发布器只复制它们的 精确字节并生成校验和清单,绝不会导出 svw 产品源码。

ui.draw、ui.focus、ui.fullscreen、ui.layout 和 ui.report 是相互独立的 manifest 能力。获得 core.command.invoke 的插件可以通过 ::svw::command 调用现有公开 svw 命令;内部开发命令会在命令注册表边界被拒绝。host 通过正常命令路径执行 已接受的命令,并把 ok 和正常消息或诊断交给可选 callback。这个能力有意保持 强大:公开命令具有与交互使用相同的修改、文件输出和退出行为,它不是只读自动化 通道。能力控制的是 svw API 权限,而不是操作系统沙箱。Tcl 插件属于受信任的本地 代码,Tcl 自带的 exec 具有用户的操作系统权限;因此 v1 不会声称存在误导性的 process.exec 沙箱。

core.state.read 提供有界的 core.state.get 快照,wave.read 提供 lazy 的点 查询 wave.value.get、有界的已解析变化查询 wave.changes.range,以及只返回直接子 scope 的分页查询 wave.scope.children。Tcl helper 接受可选 callback,callback 会收到原始 JSON 结果行。无依赖 SDK 包含有界的结构化 JSON 解码器;字段 helper 使用显式路径, 例如 string_field $line {result message} 和 string_field $line {result sample value}。它们不会在不相关的嵌套对象中搜索同名 key,因此响应新增字段不会悄悄改变 callback 读取的值。如果 callback 需要 typed helper 尚未覆盖的值,可使用 json_decode、json_get、json_type 和 json_payload。首版协议有意不提供无界的“全部变化”或递归层级请求:自定义 view 只能使用有界数据 API,超大波形不能悄悄破坏 svw 的 lazy reader。

ui.report 允许插件通过 ::svw::report_publish 把一个有界不可变快照发布到原生 report overlay。host 会先完整校验并复制 title/line,再打开报告。插件卸载后副本 仍然有效,后续原生或插件报告可以替换它;报告文本永远不会被执行。

core.navigation 是插件与原生窗口之间的集成边界。带信号锚点的插件行调用 ::svw::reveal_signal,同时带信号和原生 tick 的行调用 ::svw::reveal_wave, 只有时间锚点的记录调用 ::svw::reveal_time。它们不是命令字符串的别名:host 先完整解析并校验目标,再选择并滚动原生 signal 行、移动原生 wave cursor、闪烁 信号与时间交点,并把键盘焦点从插件 surface 交还原生 S 或 W 窗口。是否把 尚未显示的信号加入 signal panel 必须由插件显式传入布尔值。反方向上,插件订阅 selection、cursor、view 和 wave 状态组,原生窗口变化会刷新插件的有界视图。

仓库中有十六个用于验证扩展边界的示例:

  • examples/tcl-scope-browser 在原生 frame 旁增加可聚焦的层级窗口。Enter 只展开
  • 当前 scope 并请求一个有界页面;收起的行显示当前已知的直接子 scope 数。

  • examples/tcl-python-semantic 把 Python 保持在 svw 进程之外,同时只保留一个语义
  • 权威来源。受信任的 Tcl 进程启动有边界的 Python 编排器、显示等待转圈,并绘制语义 driver/load 结果。Python 让已配置的设计编译器生成规范语义包,只通过公开的 svw agent JSON 接口消费结果,不嵌入另一套 parser 或 AST 投影。相关工具不在 PATH 时,可在插件目录的单行 python.txt、viewer.txt 和 design-compiler.txt 中指定绝对路径。

  • examples/tcl-waveform-notebook 把当前 cursor、所选信号和简短笔记保存到有界且
  • 不可执行的 sidecar。插件绝不会 source sidecar 内容;选中笔记后,它会在原生 signal 与 wave 窗口中原子显露该信号和时间。

  • examples/tcl-reset-clock-health 对可见区间内所选的一位信号做健康检查。有界变化
  • 页用于计算 clock 周期、jitter、duty 和 reset 脉宽;固定 transition 预算会明确 标记 partial 结果,不会默默抽干任意高密度信号。

  • examples/tcl-x-detective 查找所选信号的 X、Z 与 mixed-unknown 区间,显示持续
  • 时间,并在原生窗口中显露所选信号与时间目标。

  • examples/tcl-protocol-decoder 复用一个原生有界时钟沿采样器,以及 Tcl
  • 规则、跨页状态和统一结果组件。可切换 profile 覆盖 ready/valid、 AXI4/AXI4-Lite 与 ACE channel、AXI4-Stream、APB、AHB-Lite、Avalon-ST/MM、 Wishbone B4、TileLink channel、request/acknowledge,以及通用 FIFO 或带 ID 的 request/response 关联。关联结果包含 phase、latency、outstanding 深度、 reset 终止、未配对端点、多 beat 进度和裁剪窗口完整性。AXI/ACE 与 TileLink profile 明确只做 channel 级检查,不冒充完整 ordering、burst、cache 或 coherence compliance checker。

  • examples/tcl-failure-triage 读取有界且不可执行的回归失败报告,显示失败详情,
  • 并把每个失败时间关联到原生波形;日志本身不会激活插件代码。可选 baseline 报告 会让同一窗口显示 new/common/fixed 回归差异。

  • examples/tcl-uvm-timeline 从与波形原生时间轴对齐的有界 simulator export 中筛选
  • phase、objection、sequence、configuration、resource 和 register 活动。

  • examples/tcl-coverage-holes 浏览有界 coverage export,并复用公开
  • coverage source 命令打开所选文件和行。

  • examples/tcl-temporal-query 在一个所选信号和可见区间上执行有限的
  • rise/fall/unknown/equals 查询语言。

  • examples/tcl-smart-watch 在 cursor 处顺序采样最多 64 个显式信号路径,不遍历
  • 波形 schema。

  • examples/tcl-memory-inspector 只 lazy 采样显式地址到信号 memory map 的当前
  • 可见页。

  • examples/tcl-fsm-visualizer 用 value-to-state map 解码有界状态迁移与有向边计数。
  • examples/tcl-simulator-control 只在用户显式 connect 后持有能力协商的外部
  • simulator adapter,并限制每个请求。

  • examples/tcl-debug-recipes 预览不可执行的命令 recipe,并要求第二次显式操作后才
  • 顺序调用公开命令。

  • examples/tcl-workflow-console 展示插件局部 action、公开命令组合和原生报告原子
  • 发布,不注册动态全局命令。

Python 示例展示的是集成模式,不会给 svw 增加新的运行时依赖或第二套 design 数据 契约。生产插件应复用生成 design 数据时相同的 elaborated 源文件与选项;示例中的 sources.f 和 demo.sv 只用于可重复演示。

Tcl SDK 把协议消息写到 stdout,因此插件日志必须写 stderr。ui.resize、 ui.layout、ui.input 和已订阅的 state.changed 通知会以 dictionary 传给事件 handler。SDK 绘制 helper 会缓存操作,直到 surface_commit;随后一个有界的 ui.surface.update 请求会校验并原子发布完整 generation。RPC helper 返回请求 id。Core、波形和 surface commit helper 接受可选 callback;surface create 通过 -callback 接收,其余资源、布局、焦点和订阅 helper 则把 callback 作为最后一个 可选参数。SDK option list 会拒绝未知名称,不会静默接受拼写错误。同一插件的 host-owned UI 资源效果按接收顺序提交;相互依赖的 Core 操作必须从前一个操作的 callback 中发起。

协议颜色只能是 default、palette:N 或 #RRGGBB,绝不暴露 svw 内部整数编码。 surface 与布局 id 在线协议中始终是插件局部 id。ui.layout dictionary 包含 placed、当前 generation 和生效矩形;只有 surface 位于活动布局中时,node 才非空。因此 placed:false 能让插件在布局恢复后按 create 时声明的矩形重绘, 无需根据“没有事件”进行猜测。panel 必须按该生效矩形重绘,不能假设自己请求的 cells 始终精确不变;其他插件、终端 resize 或用户拖动分隔线都可能合法改变宽度和 高度。hello 响应后,::svw::limit <name> 返回本会话协商 得到的限制,插件无需内嵌 host 常量。

Tcl UI 插件协议 v1 参考

本文是 svw.ui.plugin.v1 的兼容性参考。只有用户显式执行 svw --plugin <manifest> 时才会使用该协议;波形文件和会话文件永远不会激活 插件。

兼容性

每个 frame 是一个 UTF-8 JSON-RPC 2.0 对象,末尾跟一个换行符,最大 1 MiB。 v1 不接受空行、顶层 JSON-RPC batch 或插件 notification。请求示例:

{"jsonrpc":"2.0","id":1,"method":"session.ping","params":{}}

响应携带相同 id,并且只能包含 result 或 error 之一。解析失败或无效请求 导致 host 无法恢复有效 id 时,错误响应使用 id:null。id 只能是长度不超过 max_id_bytes 的 UTF-8 字符串或有符号 64 位整数;浮点数和 null 都会被拒绝。 响应尚未返回时,插件不得复用 id。插件必须忽略未知的 result member 和未知的 host notification。请求 schema 是严格封闭的:未知 envelope 或 params member 会被拒绝,拼写错误不能静默改变行为。已发布的 v1 名称、字段类型和语义不会删除 或挪用;破坏兼容性的协议必须使用新的精确名称。

兼容性有意采用不对称规则。请求 envelope、params 以及嵌套在 params 内的对象 都是封闭且 schema-strict 的,因此给既有 v1 方法增加请求 member 不属于兼容扩展。 result 对象及其嵌套对象、错误 data、manifest 根 member、host notification 的 params 及其嵌套对象都是开放的,接收方必须忽略未知 member。可以新增方法、通知、 能力和注册值;插件使用 session.hello 发现后才能使用。新值永远不能获得已有值的 语义。

响应可以延迟,也可以不按请求顺序到达;ui.resize 等 host notification 可以出现 在请求与响应之间。client 只能通过 id 关联响应,并且在 Core 或波形请求等待期间 必须继续分发 host notification。

同一插件拥有的 UI 资源、布局、焦点和订阅效果按 frame 接收顺序提交。因此后一个 此类请求能观察到前一个成功请求的效果,使 create–draw–focus 与 restore–destroy 序列保持确定性。这个保证不会把 deferred Core 或波形操作与后续请求排序;当一个 Core 结果是另一个请求的输入时,插件必须从第一个请求的 callback 发起后续请求。

错误包含数值 code、稳定的 name、便于阅读的 message,并可新增对象类型的 data。插件只能按 name 分支,不能解析 message。

错误名 代码
parse_error -32700
invalid_request -32600
method_not_found -32601
invalid_params -32602
internal_error -32603
capability_denied -32001
conflict -32002
not_found -32003
unavailable -32004
quota -32005

Manifest 与握手

{
  "id": "example-ui",
  "version": "1.0.0",
  "entry": "main.tcl",
  "protocol": "svw.ui.plugin.v1",
  "capabilities": ["ui.draw", "ui.focus"],
  "optional_capabilities": ["ui.layout"]
}

entry 必须是 manifest 目录内的普通文件。capabilities 中的能力是强制要求: 必需能力未知或不可用会拒绝整个 manifest。optional_capabilities 可以包含旧 v1 host 不认识的能力;已知项会被授予,未知项会被忽略。插件在使用可选功能前检查 hello 结果中的 capabilities。两个数组内部以及跨数组的重复名称都会被拒绝。 v1 能力为 core.command.invoke、core.navigation、core.state.read、wave.read、ui.draw、 ui.focus、ui.fullscreen、ui.layout 和 ui.report。

插件与局部资源 id 必须匹配 [A-Za-z0-9][A-Za-z0-9._-]*。entry path 拒绝控制 字符和 NUL;host canonicalize 路径后再次确认其仍位于 manifest 目录内。 version 是最多 64 个 ASCII byte 的不透明插件元数据,只允许字母、数字、.、 -、+;svw 不会从中推断协议兼容性。manifest 最大 64 KiB,必需和可选能力 合计最多 32 项。

插件进程只接收清理后的环境:host 除了保留文档约定的运行时基础变量,只转发名称以 SVW_PLUGIN_CONFIG_ 开头的变量。这个前缀表示用户显式选择传入非机密插件配置, 不是安全边界;插件仍然是受信任的本地代码。其他父进程变量和 secret 不会被转发。 转发配置最多 64 个变量,总编码大小最多 64 KiB。

可选的 headless 声明独立于 UI 握手:

"headless": {
  "entry": "headless.tcl",
  "actions": [{"id":"check", "summary":"Bounded check",
               "input":"clocked-samples.v1"}],
  "commands": [{"id":"ready-valid", "summary":"Short check",
                "action":"check", "profile":"ready-valid", "clock":"clk",
                "roles":{"valid":"valid", "ready":"ready", "data":"data"}}]
}

该入口同样必须是 canonical 后仍位于 manifest 目录内的普通文件。动作最多 16 个且 id 唯一;id 使用插件 id 语法,summary 最多 160 bytes。只有调用方显式指定 manifest/action 时才启动 headless 进程;它接收一条有界 JSON,返回一条 svw.plugin.action-result.v1,随后由 host 终止。 action 的 input 只能是 clocked-samples.v1、event-samples.v1、 extraction-plan.v1、property-captures.v1、signal-changes.v1 或 bounded-arguments.v1。extraction adapter 把命名的独立 event source、类型化 predicate、sample mode 与 payload 合并为一个有界表。 property adapter 接受类型感知表达式、match|assert|deassert|switch、范围、同时间戳策略与 有界 limit;host 提供已筛选的 capture、truth、operand 与完整性。event adapter 接受有界的 named/wildcard/edge union、逐 term iff、native/pre-edge 与 role-to-signal tracked 映射。host 在启动 Tcl 前完成有界波形采样;最后一种原样 转交已经受消息大小限制的 JSON argument object,供显式加载且会自行校验项目产物的受信任 插件使用。 namespaced 短命令最多 32 个;它引用已声明 action,并保存 profile 与相对 clock/role 叶子。plugins exec 将叶子拼到调用方给出的精确 scope,默认使用当前可见范围,也可成对 指定 start/end。

基础能力映射属于已冻结的方法注册表:

方法 必需能力
session.hello -
session.ping -
session.shutdown -
core.command.invoke core.command.invoke
core.signal.reveal core.navigation
core.time.reveal core.navigation
core.wave.reveal core.navigation
core.state.get core.state.read
event.subscribe core.state.read
event.unsubscribe core.state.read
ui.focus.release ui.focus
ui.focus.request ui.focus
ui.layout.replace ui.layout
ui.layout.restore ui.layout
ui.report.publish ui.report
ui.surface.create ui.draw
ui.surface.destroy ui.draw
ui.surface.update ui.draw
wave.changes.range wave.read
wave.samples.clocked wave.read
wave.scope.children wave.read
wave.value.get wave.read

由参数选择的额外权限同样被冻结并由机器检查:

方法 条件 额外必需能力
ui.surface.create mode=fullscreen ui.fullscreen
ui.surface.create focusable=true ui.focus

每个方法的参数和结果对象也已经冻结。调度有意不进入协议:任何响应都可能 deferred 或重排,插件必须始终按 id 关联,并继续处理通知。

方法 参数对象 结果对象
session.hello session.hello.params session.hello.result
session.ping empty empty
session.shutdown empty empty
core.command.invoke core.command.invoke.params core.command.invoke.result
core.signal.reveal core.signal.reveal.params core.signal.reveal.result
core.time.reveal core.time.reveal.params core.time.reveal.result
core.wave.reveal core.wave.reveal.params core.wave.reveal.result
core.state.get empty core.state.get.result
event.subscribe event.subscription.params empty
event.unsubscribe event.subscription.params empty
ui.focus.release empty empty
ui.focus.request ui.focus.request.params empty
ui.layout.replace ui.layout.replace.params empty
ui.layout.restore empty empty
ui.report.publish ui.report.publish.params ui.report.publish.result
ui.surface.create ui.surface.create.params ui.surface.create.result
ui.surface.destroy ui.surface.destroy.params empty
ui.surface.update ui.surface.update.params ui.surface.update.result
wave.changes.range wave.changes.range.params wave.changes.range.result
wave.samples.clocked wave.samples.clocked.params wave.samples.clocked.result
wave.scope.children wave.scope.children.params wave.scope.children.result
wave.value.get wave.value.get.params wave.value.get.result

下面的表是完整 v1 对象字段约定。没有列出字段的对象使用相应方法中说明的空 params 或 result 对象。conditional 表示只在周围文字所述条件成立时必需。

对象 字段 类型 出现条件
manifest id id required
manifest version version required
manifest entry relative-path required
manifest protocol protocol required
manifest capabilities capability[] required
manifest optional_capabilities capability[] optional
request jsonrpc literal:2.0 required
request id string-or-int64 required
request method method required
request params object required
response jsonrpc literal:2.0 required
response id string-or-int64-or-null required
response result object conditional
response error error conditional
notification jsonrpc literal:2.0 required
notification method notification required
notification params object required
error code int required
error name error-name required
error message string required
error data object optional
session.hello.params protocol protocol required
session.hello.params plugin id required
session.hello.result protocol protocol required
session.hello.result plugin id required
session.hello.result capabilities capability[] required
session.hello.result limits limit-map required
session.hello.result methods method[] required
session.hello.result notifications notification[] required
session.hello.result values value-map required
core.command.invoke.params command string required
core.command.invoke.result ok bool required
core.command.invoke.result message string required
core.signal.reveal.params signal string required
core.signal.reveal.params add bool required
core.signal.reveal.result signal string required
core.signal.reveal.result row nonnegative-int64 required
core.signal.reveal.result added bool required
core.signal.reveal.result filter_cleared bool required
core.time.reveal.params time int64 required
core.time.reveal.result time int64 required
core.wave.reveal.params signal string required
core.wave.reveal.params time int64 required
core.wave.reveal.params add bool required
core.wave.reveal.result signal string required
core.wave.reveal.result time int64 required
core.wave.reveal.result row nonnegative-int64 required
core.wave.reveal.result added bool required
core.wave.reveal.result filter_cleared bool required
core.state.get.result state state required
event.subscription.params events state-group[] required
wave.value.get.params signal string required
wave.value.get.params time int64 required
wave.value.get.result sample wave-sample required
wave.changes.range.params signal string required
wave.changes.range.params start int64 required
wave.changes.range.params end int64 required
wave.changes.range.params cursor string required
wave.changes.range.params limit positive-int required
wave.changes.range.result page wave-changes-page required
wave.samples.clocked.params clock string required
wave.samples.clocked.params signals string[] required
wave.samples.clocked.params start int64 required
wave.samples.clocked.params end int64 required
wave.samples.clocked.params edge clock-edge required
wave.samples.clocked.params cursor string required
wave.samples.clocked.params limit positive-int required
wave.samples.clocked.result page clocked-samples-page required
wave.scope.children.params scope string required
wave.scope.children.params offset nonnegative-int64 required
wave.scope.children.params limit positive-int required
wave.scope.children.result page wave-scope-page required
ui.report.publish.params title string required
ui.report.publish.params lines string[] required
ui.report.publish.result rows nonnegative-int64 required
state file string required
state format string required
state timescale string required
state minimum_time int64 required
state maximum_time int64 required
state cursor int64 required
state view_start int64 required
state view_end int64 required
state focus focus-value required
state selected_signal string required
state wave_revision uint64 required
state wave_loading bool required
state wave_progress int:0..100 required
wave-sample signal string required
wave-sample time int64 required
wave-sample width uint32 required
wave-sample kind wave-kind required
wave-sample value string required
wave-changes-page signal string required
wave-changes-page start int64 required
wave-changes-page end int64 required
wave-changes-page cursor string required
wave-changes-page next_cursor string required
wave-changes-page width uint32 required
wave-changes-page has_more bool required
wave-changes-page changes wave-change[] required
clocked-samples-page clock string required
clocked-samples-page signals string[] required
clocked-samples-page start int64 required
clocked-samples-page end int64 required
clocked-samples-page edge clock-edge required
clocked-samples-page cursor string required
clocked-samples-page next_cursor string required
clocked-samples-page has_more bool required
clocked-samples-page samples clocked-sample[] required
clocked-sample time int64 required
clocked-sample values wave-sample[] required
wave-change time int64 required
wave-change kind wave-kind required
wave-change value string required
wave-scope-page scope string required
wave-scope-page offset nonnegative-int64 required
wave-scope-page next_offset nonnegative-int64 required
wave-scope-page known_count nonnegative-int64 required
wave-scope-page count_exact bool required
wave-scope-page has_more bool required
wave-scope-page children wave-scope-child[] required
wave-scope-child name string required
wave-scope-child path string required
wave-scope-child subscope_count nonnegative-int64 required
wave-scope-child subscope_count_exact bool required
ui.surface.create.params surface id required
ui.surface.create.params mode surface-mode optional
ui.surface.create.params x int required
ui.surface.create.params y int required
ui.surface.create.params width positive-int required
ui.surface.create.params height positive-int required
ui.surface.create.params z int required
ui.surface.create.params opaque bool optional
ui.surface.create.params focusable bool optional
ui.surface.create.result generation uint64 required
ui.surface.update.params surface id required
ui.surface.update.params clear bool optional
ui.surface.update.params expected_generation nonnegative-int64 optional
ui.surface.update.params ops draw-op[] required
ui.surface.update.result generation uint64 required
ui.surface.destroy.params surface id required
ui.focus.request.params surface id required
text-op op literal:text required
text-op x int required
text-op y int required
text-op text string required
text-op fg color optional
text-op bg color optional
fill-op op literal:fill required
fill-op x int required
fill-op y int required
fill-op width nonnegative-int required
fill-op height nonnegative-int required
fill-op fg color optional
fill-op bg color optional
ui.layout.replace.params root layout-node required
layout-leaf id id required
layout-leaf kind literal:leaf optional
layout-leaf view view required
layout-leaf cells positive-int optional
layout-leaf weight positive-int optional
layout-leaf min_width positive-int optional
layout-leaf min_height positive-int optional
layout-container id id required
layout-container kind container-kind required
layout-container children layout-node[] required
layout-container cells positive-int optional
layout-container weight positive-int optional
layout-container min_width positive-int optional
layout-container min_height positive-int optional
layout-container gap nonnegative-int conditional
state.changed.params revision uint64 required
state.changed.params changed state-group[] required
state.changed.params state state required
ui.resize.params width positive-int required
ui.resize.params height positive-int required
ui.focus.changed.params focused bool required
ui.focus.changed.params surface id conditional
ui.layout.params revision uint64 required
ui.layout.params surface id required
ui.layout.params placed bool required
ui.layout.params node id conditional
ui.layout.params generation uint64 required
ui.layout.params x int required
ui.layout.params y int required
ui.layout.params width positive-int required
ui.layout.params height positive-int required
ui.input.params surface id required
ui.input.params key input-key required
ui.input.params modifiers modifier[] required
ui.input.params character string conditional
ui.input.params mouse_kind mouse-kind conditional
ui.input.params button mouse-button conditional
ui.input.params x int conditional
ui.input.params y int conditional

创建 fullscreen surface 还需要 ui.fullscreen;创建 focusable surface 还需要 ui.focus。

首个请求必须是 session.hello,其中 protocol 与 plugin 必须匹配 manifest。 结果返回选定协议、插件 id、已授予能力、不可变 host 限制、受支持的 methods、 notifications,以及包含各注册类别可用值的 values 对象。插件应使用这些列表 探测 v1 可追加功能,而不是解析应用版本。session.ping 的 params 为空。 session.shutdown 会原子释放调用者的 surface、布局和焦点。

limits 是本会话边界的权威来源。插件必须读取它,不能内嵌当前数值。兼容的 v1 host 可以提高限制,但不能接受请求后静默截断。

限制 含义
max_message_bytes 单个入站协议 frame 允许的最大 UTF-8 byte 数。
max_string_bytes 有界标识符、路径或命令字符串允许的最大 byte 数。
max_text_bytes 单个绘制文本、命令诊断或波形值允许的最大 byte 数。
max_surface_ops 一次原子更新允许的最大绘制操作数。
max_surfaces host registry 中允许的最大 live surface 数。
max_surface_cells 所有 live surface 的最大 cell 总容量。
max_surface_update_cells 一次更新实际触达的最大裁剪后 cell 数。
max_surface_dimension surface 尺寸与坐标绝对值的最大边界。
max_surface_z surface z 绝对值的最大边界。
max_layout_nodes 一棵 retained 布局允许的最大 node 数。
max_layout_depth 相对 root 的最大布局深度。
max_layout_children 单个布局 container 允许的最大 child 数。
max_layout_weight flex weight 的最大值。
max_layout_gap child 间 gap 的最大 cell 数。
max_id_bytes 请求或资源 id 允许的最大 byte 数。
max_subscriptions 单次订阅请求允许的最大 state group 数。
max_messages_per_second 每个插件 rate window 允许的最大入站 frame 数。
max_pending_core_requests 允许单个插件同时存在的最大未完成 Core 请求数。
max_scope_children_page 单次层级请求允许返回的最大直接子 scope 数。
max_wave_changes_page 单次范围请求允许返回的最大已解析变化点数。
max_clocked_sample_signals Maximum digital signals sampled together at one clock edge.
max_clocked_samples_page Maximum clock edges returned by one clocked sample request.
max_report_lines 单个不可变原生报告快照允许的最大行数。
max_report_bytes 单个原生报告所有行允许的最大已解码 UTF-8 byte 数。
handshake_timeout_ms 初始 session.hello 请求允许的时间。
recovery_escape_count 为插件紧急恢复保留的连续 Escape 键数量。
recovery_sequence_ms 紧急恢复 Escape 键之间允许的最大间隔。

Core 与波形方法

  • core.command.invoke(能力 core.command.invoke):params 包含一个非空
  • command 字符串,长度受 max_string_bytes 限制,不做 shell 解释;可选的单个 前导 : 会被忽略。只允许执行安装版本命令注册表中标记为 public 的命令,包括 公开的聚焦 panel direct form;内部开发命令会被拒绝。它走正常命令路径,因此 可以修改会话、打开数据、写出命令定义的文件,或像交互操作一样请求 host 退出。 完成后始终返回 ok 与 message;普通命令诊断使用 ok:false,不会与 JSON-RPC transport/capability error 混淆。公开命令语法由已安装 host 提供发现能力,这个 方法不会把每条命令 grammar 变成独立冻结的插件方法。

  • core.signal.reveal(能力 core.navigation)解析一个精确信号路径,选择、滚动并
  • 闪烁原生 signal 行,然后把插件 surface 的键盘焦点交还原生 S panel。 add:false 要求该信号已显示,add:true 才允许显式加入;若 filter 隐藏目标, host 会先保存 undo 再清除 filter。

  • core.wave.reveal 原子执行同一 signal reveal,并校验、移动到给定原生 tick,
  • 闪烁信号/时间交点后聚焦原生 W panel。core.time.reveal 用于没有信号锚点的 时间记录,只移动 wave cursor,不修改 signal list。失败请求不会留下部分修改。

  • core.state.get(能力 core.state.read):返回 state,字段包括 file、
  • format、timescale、minimum_time、maximum_time、cursor、view_start、 view_end、focus、selected_signal、wave_revision、wave_loading,以及 0..100 的整数 wave_progress。

  • wave.scope.children(能力 wave.read):返回一页有界的直接子 scope。空
  • scope 表示波形根,非空值应使用之前发现的稳定 scope path。offset 是非负的 子项下标,limit 必须在 1..max_scope_children_page。结果回显 scope 与 offset,返回 next_offset,绝不包含 signal 或递归后代。known_count 与每个 subscope_count 表示 lazy reader 当前已知的层级 frontier;对应的 *_exact 为 false 时该计数仍可能增长。has_more 表示可以继续请求 next_offset。每次请求 最多让 backend 接纳一个有界 schema page,即使该页没有发现新的直接子 scope。

  • wave.value.get(能力 wave.read):params 包含稳定信号路径和有符号 64 位原生
  • tick time。sample 结果包含 signal、time、width、kind 和 value。 这是有界、格式无关的点查询,不枚举 signal catalog,也不返回 transition 集合。 完成延迟不是线协议保证:有索引点查询的 backend 可以直接回答,通用兼容 backend 可以扫描该信号。插件在等待期间继续处理通知,并按 id 关联最终响应。

  • wave.changes.range(能力 wave.read):对一个稳定信号路径返回原生 tick 闭区间
  • start..end 内的一页有界、已解析值变化。limit 必须在 1..max_wave_changes_page。首个请求使用空 cursor;当 has_more 为 true 时, 使用返回的 opaque next_cursor 和相同信号/区间继续请求。跨页时间严格递增。 同一时间戳的多次 delta 写入会折叠为最终可见值,与普通波形显示一致;delta-cycle 分析属于另一个数据契约。host 只访问指定信号和一页有界结果,因此代价与层级总 信号数无关。v1 仍不提供无界 signal、递归 hierarchy、transition 或时间范围 dump。

Core 命令和波形读取可以 deferred,也可以乱序完成;client 不得从响应时序推断 host 调度。慢 reader 请求不会阻塞协议 I/O 或等待动画。未完成 Core 请求数受协商限制, 重复 pending id 会被拒绝。Core 操作一旦接受就具有 at-most-once 语义,即使调用者 断开也可能继续完成;卸载 UI 不能回滚已启动的命令。

原生报告发布

ui.report.publish 需要 ui.report。params 包含非空且有界的 title 与非空 lines 字符串数组。每一行受 max_text_bytes 限制;整个快照同时受 max_report_lines 与 max_report_bytes 限制。host 在完整校验后原子替换并打开 原生 report overlay,返回实际接收的 rows。Tab 分隔行使用原生表格渲染,全部 文本仍会经过终端控制字符过滤。

发布后,报告是由 host 持有的不可变副本。插件卸载不会关闭或使它失效,后续原生 命令或其他插件可以替换它。发布报告不会执行文本,也不会注册命令、按键、补全或 help 条目。v1 有意不提供动态全局 : 命令注册:namespace、冲突、help/completion、 教程、卸载和执行中调用的生命周期需要独立的未来契约。manifest headless.commands 改为使用 host 管理的 :plugins exec namespace;插件也可在自己的 聚焦 surface 中提供局部 action,并通过 core.command.invoke 组合既有公开命令。

Surface

所有 surface id 都是调用者局部 id,返回值绝不会附加内部 owner 前缀。

ui.surface.create 需要 ui.draw,params 为 surface、x、y、width、 height、z。可选 mode 为 overlay、modal 或 fullscreen;fullscreen 还要求 ui.fullscreen。docking 是布局操作,不是第四种 surface mode。可选的 opaque 与 focusable 默认为 false;focusable surface 需要 ui.focus。结果 generation 为零,表示尚未发布。不同 z 的 surface 按升序组合并 hit-test;相同 z 的顺序有意不定义,重叠有意义时插件必须分配不同 z。

overlay 只在已提交矩形内接收鼠标输入。获得焦点的 modal 或 fullscreen surface 也接收矩形外输入,因此局部坐标可以为负或超过尺寸。覆盖 viewport 的不透明 fullscreen surface 会抑制 core frame 渲染,直到被移除或 resize 后不再完全覆盖。

live registry 的 surface 总容量受 max_surface_cells 限制,因此其他 surface 已 占用预算时,创建可以返回 quota。ui.surface.update 需要 ui.draw、surface 和有界 ops;可选 clear 默认为 false,可选 expected_generation 拒绝陈旧绘制。 完整请求先在副本上校验,再作为一个新 generation 原子发布。ops 最多包含 max_surface_ops 个操作,单个 text 最多 max_text_bytes 个 UTF-8 byte:

{"op":"text","x":2,"y":1,"text":"hello","fg":"palette:12","bg":"default"}
{"op":"fill","x":0,"y":0,"width":20,"height":3,"fg":"#C8D3F5","bg":"#222436"}

颜色精确限定为 default、0..255 的 palette:N 或 #RRGGBB。坐标单位为终端 cell。host 按显示宽度测量并裁剪 UTF-8 文本。Tab 前进到下一个八 cell tab stop; 其他终端控制 codepoint 会在 Frame 边界被中和,绝不能成为原始终端控制序列。 fill 用空格替换目标 glyph 并应用颜色。无效操作不改变当前 generation。所有操作 实际触达的裁剪后 cell 总数受 max_surface_update_cells 限制,避免重叠的大 fill 把小请求放大为无界 raster 工作。

ui.surface.destroy 需要 ui.draw 和局部 surface id;如果该 surface 持有焦点, 也会同时释放。活动布局引用的 surface 不能销毁;插件必须先恢复或替换布局,因此 任何已接受请求都不会留下悬空可见 leaf。

布局与焦点

ui.layout.replace 需要 ui.layout 和一个 root,整棵树校验后原子安装。node kind 为 leaf(默认)、row、column 或 stack;每个 node 都需要唯一的 局部 id。leaf 的 view 只能是 builtin.frame 或 surface:<local-id>;container 使用 children。可选约束为 cells、weight、min_width、min_height;row 与 column 还接受 gap,stack 因 child 重叠而拒绝它。cells、min_width、 min_height 出现时必须是正整数,没有公开的负值 sentinel。leaf 必须恰有一个 view 且没有 children;container 必须有非空 children 且没有 view。 surface 与 builtin.frame 各最多出现一次,因为每个 live view 只有一个解析后矩形。 v1 不存在其他 builtin.* 名称,插件不得猜测。原生 chrome、signal、wave leaf 只有 在具备独立 renderer、overlay 与 input geometry 语义后,才能作为可发现注册值新增。

插件使用协商限制,不能依赖当前数值。每个插件只拥有自己提交的布局贡献;第二个插件 不会替换第一个插件,也不会因第一个插件存在而被拒绝。host 只保留一份原生 frame, 抽取各插件的 surface 子树,再按照声明的 minimum 将独立插件窗口自适应排成有界的行列。 只有一个贡献时仍精确保留其 v1 布局(包括有意的 fullscreen);有多个贡献时,聚合布局 归核心所有,任何插件都不能放置或缩放另一个插件的 surface。ui.layout.restore 只删除 调用者自己的贡献。嵌入布局的 surface 临时使用解析后的 leaf 矩形;不再 placed 时回到 ui.surface.create 声明的矩形。两种 resize 都会使先前 generation 失效并要求重绘,陈旧 dock 内容不会覆盖恢复后的核心 UI。

row 分配水平轴,column 分配垂直轴。带 cells 的 child 在满足 minimum 的前提下 取得固定主轴尺寸;其他 child 先取得 minimum,再按 weight 比例分配剩余 cell, 余数按声明顺序分配。gap 只插在实际 placed 的 child 之间。stack child 共用一个 矩形;core frame 先组合,插件 surface 保留显式 z 顺序。省略 builtin.frame 会抑制原生 frame 渲染。如果后续终端 resize 无法满足 minimum,host 保留最后一次 有效 placement、报告 resize,并允许插件提交替代布局。

row/column 产生的分隔线是 host 控件。用鼠标左键拖动分隔线,可以在满足双方 minimum 的前提下调整相邻的原生/插件或插件/插件窗口。插件 surface 聚焦时,Alt+Left/Right 把最近的水平 split 调整两列,Alt+Up/Down 把最近的垂直 split 调整一行。成功的缩放 手势由 host 保留,不会作为 ui.input 发送给插件;插件应根据随后收到的 ui.layout notification 重绘。

ui.focus.request 需要 ui.focus 以及属于调用者、已提交、可聚焦的 surface。 鼠标按下已提交的 focusable surface 也会获取焦点 lease。程序请求可以在调用者自己 的 surface 间移动焦点,但不能抢占其他插件的 lease,否则返回 conflict;显式用户 鼠标点击可以转移焦点。无插件聚焦时,ui.focus.release 幂等成功,但它不能释放 其他插件的 lease。连续 Escape 的协商数量由 host 保留用于紧急恢复,相邻按键间隔 不得超过 recovery_sequence_ms。前面的 Escape 正常发送,最后一个由 host 消费, 随后终止插件并原子移除其 surface、布局和焦点 lease。非 Escape 输入或超时会重置 序列。

订阅与通知

event.subscribe 与 event.unsubscribe 需要 core.state.read。非空 events 数组接受 cursor、view、selection、focus、wave。新增订阅后的首次 host pass 会发送快照,即使值刚刚没有变化;之后未变化的 pass 会合并。

host notification 是没有 id 的 JSON-RPC request:

  • state.changed:单调 revision、已订阅的 changed group 和 typed state 快照。
  • ui.resize:终端 width 与 height。
  • ui.focus.changed:focused,以及为 true 时的局部 surface。初始状态、插件
  • 获得/移动/失去焦点(包括点击外部)时都会发送。

  • ui.layout:布局 revision 改变时,对插件拥有的每个 surface 各发送一次;包含
  • 局部 surface、revision、当前 generation、placed 和生效的 x、y、 width、height。只有 placed:true 时才有 node。恢复或移除布局后的 placed:false 明确报告回到 create 时声明的矩形;generation 为零要求重绘。

  • ui.input:局部 surface、规范化 key、modifiers、可选 character,以及
  • 适用时的鼠标字段。modifiers 是数组,不是内部 bit mask。鼠标 x、y 是从零 开始的 surface 局部 cell;button 是名称,只在 press/release/drag 时出现。

resize、布局解析和聚焦输入 notification 属于资源生命周期事件,不需要 state 订阅。Core state 快照中的 focus 是最后一次原生 signal/wave panel 焦点; 插件焦点 lease 只通过 ui.focus.changed 报告。

下表是 v1 线协议值基线。周围 result/notification 明确可扩展时,host 可以新增值; 插件必须把未知值当作未知值处理,不能套用已有值语义。

类别 值
state_group cursor
state_group view
state_group selection
state_group focus
state_group wave
surface_mode overlay
surface_mode modal
surface_mode fullscreen
layout_kind leaf
layout_kind row
layout_kind column
layout_kind stack
draw_op text
draw_op fill
builtin_view builtin.frame
clock_edge rising
clock_edge falling
input_key char
input_key up
input_key down
input_key left
input_key right
input_key home
input_key end
input_key page-up
input_key page-down
input_key delete
input_key f2
input_key mouse
input_key enter
input_key backspace
input_key escape
input_key unknown
input_modifier shift
input_modifier alt
input_modifier control
mouse_kind press
mouse_kind release
mouse_kind drag
mouse_kind wheel-up
mouse_kind wheel-down
mouse_kind move
mouse_button left
mouse_button middle
mouse_button right
focus_value signal
focus_value wave
wave_kind bits
wave_kind real
wave_kind string

Tcl SDK v1 过程

下表是无外部依赖 Tcl SDK 的稳定 v1 基线。可以兼容新增 helper;已有名称、位置参数 和默认值不会删除或挪用。名称中包含下划线前缀 namespace component 的过程属于实现 细节,不是 SDK API。

过程 参数
::svw::quote value
::svw::request method params ?callback?
::svw::has_capability name
::svw::limit name
::svw::supports_method name
::svw::supports_notification name
::svw::supports_value category value
::svw::ping ?callback?
::svw::surface_create id x y width height ?option value ...?
::svw::surface_clear id
::svw::surface_text id x y text ?option value ...?
::svw::surface_fill id x y width height ?option value ...?
::svw::surface_commit id ?callback?
::svw::surface_destroy id ?callback?
::svw::focus id ?callback?
::svw::focus_release ?callback?
::svw::layout_replace root-json ?callback?
::svw::layout_restore ?callback?
::svw::layout::leaf id view ?option value ...?
::svw::layout::row id children ?option value ...?
::svw::layout::column id children ?option value ...?
::svw::layout::stack id children ?option value ...?
::svw::ui::label surface x y text ?option value ...?
::svw::ui::box surface x y width height ?option value ...?
::svw::ui::progress surface x y width fraction ?option value ...?
::svw::ui::list surface x y values ?option value ...?
::svw::ui::button surface x y label ?option value ...?
::svw::ui::input surface x y width value ?option value ...?
::svw::ui::tabs surface x y labels selected ?option value ...?
::svw::ui::table surface x y headers rows ?option value ...?
::svw::ui::tree surface x y nodes ?option value ...?
::svw::command command ?callback?
::svw::reveal_signal signal ?add? ?callback?
::svw::reveal_time time ?callback?
::svw::reveal_wave signal time ?add? ?callback?
::svw::report_publish title lines ?callback?
::svw::state_get ?callback?
::svw::subscribe events ?callback?
::svw::unsubscribe events ?callback?
::svw::wave_value signal time ?callback?
::svw::wave_changes signal start end cursor limit ?callback?
::svw::wave_samples_clocked clock signals start end edge cursor limit ?callback?
::svw::wave_scope_children scope offset limit ?callback?
::svw::shutdown -
::svw::on event callback
::svw::bind key callback
::svw::unbind key
::svw::timer milliseconds callback
::svw::timer_cancel token
::svw::json_decode document
::svw::json_get document path
::svw::json_type value
::svw::json_payload value
::svw::string_field line path
::svw::number_field line path
::svw::bool_field line path
::svw::string_array_field line path
::svw::run -