Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Surfer is a wave form viewer supporting VCD, FST, and GHW files as well as the memory transaction format FTR.

It is built to be highly configurable.

Commands

To execute a command, press space and type the command. There is fuzzy match support, so it is enough to type parts of the command name and it will display options that matches.

It is also possible to create a command file, extension .sucl, and run that. Running a command file can be done from within Surfer using the menu option in the File menu, through the toolbar button, or by typing the command run_command_file. It can also be done using the --command-file argument when starting Surfer.

Not all commands are available unless a file is loaded. Also, some commands are not available in the WASM-build (browser/VS Code extension).

Waveform/transaction loading and reloading

  • load_file <FILE_NAME>

    Load a file. Note that it works to load a waveform file from a command file.

    In WASM-builds (web browser/VS Code plugin) it is not possible to open a file due to file access restrictions. Use load_url.
  • switch_file <FILE_NAME>

    Load file, but keep waveform view.

  • load_url <URL>

    Load a URL.

  • reload

    Reload the current file. Does not work in a web browser.

  • remove_unavailable

    Remove variables that are not longer present in the reloaded/switched file.

State files

  • load_state <FILE_NAME>
  • save_state
  • save_state_as

Command files

  • run_command_file <FILE_NAME> (not on WASM)

    Run the commands in the given file.

    In WASM-builds (web browser/VS Code plugin) it is not possible to run another command file from a command file due to file access restrictions.
  • run_command_file_from_url <URL>

Add variable/transaction items

  • scope_add <SCOPE_NAME>, stream_add

    Add all variables in the specified scope to the waveform display.

  • scope_add_recursive <SCOPE_NAME>

    Add all variables in the specified scope and from all sub-scopes to the waveform display.

    Adding large hierarchies with a large number of variables can freeze surfer for a significant amount of time.
  • scope_add_as_group <SCOPE_NAME>

    Add all variables in the specified scope to the waveform display in a newly created group of the same name.

  • scope_add_as_group_recursive <SCOPE_NAME>

    Add all variables in the specified scope and all sub-scopes to the waveform display in a newly created groups nested.

    Adding large hierarchies with a large number of variables can freeze surfer for a significant amount of time.
  • variable_add <FULL_VARIABLE_NAME>, generator_add <FULL_GENERATOR_NAME>

    Add a variable/generator using the full path, including scopes/streams.

  • scope_select <SCOPE_NAME>, stream_select <STREAM_NAME>

    Select a scope/stream to be active (shown in the side panel).

  • variable_add_from_scope <VARIABLE_NAME>, generator_add_from_stream <GENERATOR_NAME>

    Add variable/generator from currently selected scope/stream.

Add other items

  • divider_add <NAME>

    Add a divider with the given name.

  • timeline_add

Groups

  • group_marked

    Add selected variables/items to a new group.

  • group_dissolve

  • group_fold_recursive

  • group_unfold_recursive

  • group_fold_all

  • group_unfold_all

Controlling item appearance

  • item_focus

  • item_set_color <COLOR_NAME>

  • item_set_background_color <COLOR_NAME>

  • item_set_format <FORMAT_NAME>

  • item_unset_color

    Reset to default color.

  • item_unset_background_color

    Reset to default background color.

  • item_unfocus

    Remove focus from currently focused item.

  • item_rename

  • theme_select <THEME_NAME>

  • zoom_fit

    Zoom to display the full simulation.

  • zoom_in

  • zoom_out

  • scroll_to_start, goto_start

  • scroll_to_end, goto_end

  • transition_next

    Move cursor to next transition of focused item. Scroll if not visible.

  • transition_previous

    Move cursor to previous transition of focused item. Scroll if not visible.

  • transaction_next

  • transaction_prev

UI control

  • show_controls

  • show_mouse_gestures

    Show mouse gesture help window.

  • show_quick_start

  • show_logs

    Show log window.

  • toggle_menu

    Toggle visibility of menu. If not visible, there will be a burger menu in the toolbar.

  • toggle_side_panel

Toggle visibility of the side panel, i.e., where the scopes and variables are shown.

  • toggle_fullscreen

    Toggle fullscreen view.

  • toggle_tick_lines

  • variable_set_name_type <Local | Unique | Global>

  • variable_force_name_type <Local | Unique | Global>

  • preference_set_clock_highlight <Line | Cycle | None>

  • preference_set_hierarchy_style <Separate | Tree>

Set if the design hierarchy is shown with scopes and variables separated or as a tree.

  • preference_set_arrow_key_bindings <Edge | Scroll>

  • config_reload

Cursor and markers

  • goto_cursor

    Go to the location of the main cursor. If off screen, scroll to it.

  • goto_marker <MARKER_NAME> | #<MARKER_NUMBER>

    Go to the location of the given marker. If off screen, scroll to it.

  • cursor_set <TIME>

    Move cursor to given time.

  • marker_set <MARKER_NAME> | #<MARKER_NUMBER>

    Add/set marker to location of cursor.

  • marker_remove <MARKER_NAME> | #<MARKER_NUMBER>

    Remove marker.

  • show_marker_window

    Display window with markers and differences between markers

Interactive simulation

  • pause_simulation
  • unpause_simulation

Viewports

  • viewport_add
  • viewport_remove

Waveform control protocol (WCP)

  • wcp_server_start (not WASM)

Start the WCP server. Typically, this is using port 54321 at address 127.0.0.1, but this can be changed using the address setting in the wcp part of the config file.

  • wcp_server_stop (not WASM)

Stop the WCP server.

Other

  • copy_value

Copy the variable name and value at cursor to the clipboard.

  • undo
  • redo
  • exit (not WASM)

Cursor and markers

In Surfer, the cursor is the vertical line that can be positioned by clicking the primary mouse button. The cursor will snap to a nearby transition if there is a waveform below.

There are also numbered markers. These are created from the cursor location, either by right-clicking and selecting New (or a number to redefine an existing), or by pressing Ctrl+0-9 (Cmd+0-9 on MacOS).

Although only marker 0 to 9 can be directly accessed using keyboard commands, there is support for up to 255 markers, numbered

It is possible to center on a marker by pressing 0-9.

The number shown in the column usually showing variable values of waveforms is the difference in time to the current cursor location.

Cursor and marker window

By pressing space and typing show_marker_window a rudimentary window showing differences between all defined markers and the cursor (primary) is shown. It is possible to center a marker/cursor by pressing on the name of the marker/cursor.

Mouse gestures

Surfer supports mouse gestured. These are activated using the middle mouse button, or if the middle mouse button is not available, but pressing Ctrl (Cmd on MacOS) and using the primary mouse button.

If the mouse pointer is close to the location where it was pressed, a graphical overlay showing the different gestures is shown.

Configuration

It is possible to modify how the mouse gestures behave.

[gesture]
# Size of the square encapsulating the instructions
size = 300
# Squared minimum move for the instructions to show up
deadzone = 20
# Radius, relative to size, for the background circle
background_radius = 1.35
# Gamma for the background circle
background_gamma = 0.75

# Mapping of different locations
[gesture.mapping]
north = "Cancel"
south = "Cancel"
west = "ZoomIn"
east = "ZoomIn"
northeast = "ZoomOut"
northwest = "ZoomToFit"
southeast = "GoToEnd"
southwest = "GoToStart"

The currently available mouse gestures actions are

NameDescription
CancelNo operation
GoToEndScroll view to last time in simulation
GoToStartScroll view to first time in simulation
ZoomInZoom in to the range defined by the start and end time of the gesture
ZoomOutZoom out a constant factor
ZoomToFitZoom to cover the whole range of the simulation

Note that although any action can be mapped to any direction, it may not make sense to map ZoomIn to anything else that East or West, as the difference in x-direction is what determines the zoom factor.

Measure time

It is possible to measure the time by holding shift and pressing the primary mouse button.

The behavior can be configured using the config primary_button_drag_behavior which can take the value Cursor (press shift to measure) or Measure (no need to press shift). There is also a preference setting for this in the Preference menu.

Remote file access

It is possible to start Surfer in server mode on a different computer and then connect to that computer to avoid downloading/copying large waveform files.

Surver

There is a stand-alone binary, Surver, that can be compiled, resulting in a much smaller binary and more likely to succeed on systems where Surfer may be hard to install due to GUI dependencies not being installed etc. There is basically no point in running surfer server, as surver will not have any drawbacks.

Using remote mode

There are two ways to start the server, either start the stand-alone server binary Surver:

surver <FILENAME>

or start Surfer in server mode using:

surfer server --filename <FILENAME>

In both situations, instructions how to progress will be printed. There are basically two ways to connect:

  1. If the computer running the server is directly accessible, it can be accessed using the provided URL.
  2. If not, you will need to setup an SSH tunnel by following the instructions.

Now, Surfer can be started using the provided URL/start command, or you can use File -> Open URL and enter the provided URL.

Configuration

Currently, the configuration options are quite rudimentary and can be provided on the command line. To see the available configuration values, either execute:

surver --help

leading to

Server for the Surfer waveform viewer

Usage: surver.exe [OPTIONS] <WAVE_FILE>

Arguments:
  <WAVE_FILE>  Waveform file in VCD, FST, or GHW format

Options:
      --port <PORT>    Port on which server will listen
      --token <TOKEN>  Token used by the client to authenticate to the server
  -h, --help           Print help
  -V, --version        Print version

or

surfer server --help

which will print a similar set of options.

Translators

Surfer supports custom translators that are loaded at runtime via web-assembly.

See additional documentation how to install and write translator plugins.

If there is enough interest, we are contemplating hosting user-developed plugins in a central location.

It is also possible to use decoders, which does not require writing your own custom translator.

Installing Translator Plugins

Plugins come as a single .wasm file which surfer will search for in .surfer/translators the current working directory, as well as in the global configuration directory

OsPath
Linux~/.config/surfer/translators/
WindowsC:\Users\<Name>\AppData\Roaming\surfer-project\surfer\config\translators\
macOS/Users/<Name>/Library/Application Support/org.surfer-project.surfer/translators/

To install a translator, simply put the .wasm file in one of these locations, and it will be discovered automatically.

Translators execute arbitrary code, so some care should be taken before installing translators. However, they are sandboxed behind a web-assembly runtime that, unless there are security, does not allow any access to anything on the system that surfer does not allow.

Currently, the only system access surfer allows for plugins is

  • Reading the path of the current working directory
  • Reading arbitrary files

Writing Translator Plugins

Documentation on writing WASM-based plugins is available at https://docs.surfer-project.org/translator_docs/index.html

Decoders

Decoders allow translating n-bit signals into nice text representations. They are based on the instruction-decoder crate. To add additional decoders to Surfer, create a decoders directory in Surfer's config directory and add your decoders inside there.

OsPath
Linux~/.config/surfer/decoders/
WindowsC:\Users\<Name>\AppData\Roaming\surfer-project\surfer\config\decoders\
macOS/Users/<Name>/Library/Application Support/org.surfer-project.surfer/decoders/

To add a new decoder, create a subdirectory and add the required toml files. You can also add project-specific decoders to .surfer/decoders directories.

Configuration

Surfer can be customized by modifying configuration files.

Note that it is enough to only add the configuration parameters that are changed to the file. All other will have the default values.

For a list of all possible configuration options, please look at the default configuration. To replace Surfer's default configuration, add your configuration to a file called config.toml and place it in Surfer configuration directory. The location of the configuration directory depends on your OS.

OsPath
Linux~/.config/surfer/config.toml.
WindowsC:\Users\<Name>\AppData\Roaming\surfer-project\surfer\config\config.toml.
macOS/Users/<Name>/Library/Application Support/org.surfer-project.surfer/config.toml

Surfer also allows having custom configs per directory. To use a configuration in just a single directory, create a .surfer subdirectory and add a file called config.toml inside this subdirectory. If you now start Surfer from within the directory containing .surfer, the configuration is loaded.

The load order of these configurations is default->config.toml->project specific. All these configuration options can be layered, this means that configurations that are loaded later only overwrite the options they provide.

After changing the configuration, run the config_reload command to update the running Sufer instance.

Themes

To add additional themes to Surfer, create a themes directory in Surfer's config directory and add your themes inside there. That is

OsPath
Linux~/.config/surfer/themes/
WindowsC:\Users\<Name>\AppData\Roaming\surfer-project\surfer\config\themes\
macOS/Users/<Name>/Library/Application Support/org.surfer-project.surfer/themes/

You can also add project-specific themes to .surfer/themes directories. Additionally, configurations can be loaded using the Menubar option View/Theme or using the theme_select command.

For a list of all possible style options, please look at the default theme. For example of existing themes look here.

Config file

Theme parameters

Built-in themes

The following are examples of the built-in themes available in Surfer.

Default

Surfer default theme

Dark high contrast

Surfer dark high contrast theme

Dark plus

Surfer dark plus theme

IBM

Surfer IBM theme

Light high contrast

Surfer light high contrast theme

Light plus

Surfer light plus theme

Okabe Ito

Surfer Okabe Ito theme

Solarized

Surfer solarized theme

Development Information

Anyone is welcome to contribute to Surfer. As Surfer is licensed under EUPL 1.2 it is assumed that your contribution will also follow that license.

Once you find something to contribute, either that feature that you are missing or one of the issues, the pattern follows a regular git-like contribution:

  1. Fork and clone the repository
  2. Setup pre-commit
  3. Create a branch (other than main)
  4. Edit code
  5. Commit code with a sensible commit message
  6. Push branch
  7. Create a merge request
  8. Wait for the change to be merged, including fixing suggestions from reviewers
  9. Enjoy the new feature!

Pre-Commit

Surfer uses the pre-commit framework to do some basic checking when committing new code locally. By using this, the risk of CI errors is reduced.

This is a Python package, so the instructions below assumes that you have a working Python in your console.

  1. Install pre-commit:
pip install pre-commit
  1. In the Surfer source-code directory:
pre-commit install

The first time you commit, there will be things installed, so the time taken can be long. However, the next time no installation is required.

Also note that if something fails, like cargo fmt has to reformat the code, you will have to commit again as the pre-commit hook only formatted the code, not committed the formatted code.

Note that the spelling check does not alter the code, but only points out errors. Hence, these must be manually corrected before committing again.

Tests

To run the tests locally, do

cargo test

When possible, it is nice to have a test of the added code. As Surfer is graphical to a large extent, we primarily rely on image tests located in libsurfer/src/tests/snapshots.sh. These tests send a suitable set of messages and then takes a snapshot of the screen content which is the ground truth. Easiest way it to copy a suitable test and change the messages.

After running the coverage test in the CI, either a red or a green vertical line will be present in the code view of the merge request to see which code was executed. Ideally, all new code should be tested, but it is currently not realistic to have that as a strict requirement.

Update Image Tests

If you change something that affects rendering or adds new image tests, you will have to update the test images:

  1. Run cargo test

  2. Run ./accept_snapshots.bash

  3. Add and commit new images

Or if you for whatever reason only want to update some of the images (if you're incrementally fixing things), copy snapshots/<test>.new.png to snapshots/<test>.png (and remove snapshots/<test>.diff.png).

Note that the test images are compressed using oxipng as part of the pre-commit hook.

Using egui Test Framework

When Surfer started with the graphical testing, egui did not have any testing facilities. Now it does, and it would be much beneficial to use egui_kittest. This would allow not just sending messages but to actually click on things etc, which allows both much higher test coverage and, more importantly, certainty that Surfer works as expected.

If you prefer to write the tests using egui_kittest that is much appreciated and clearly not a problem. More a step in the right direction.

Long Compilation Times

Compiling Surfer takes a long time which can be annoying during development. To make compilation faster, you can change lto to false and opt-level to a 0 or 1 towards the end of Cargo.toml in the root directory. This will speed up compilation at the expense of slightly larger and slower binaries (which is probably OK during development anyway).

Adding Configurations

The preferred pattern for a configuration value that can be set both in the program and in the config file is to add an Option-value in the UserState enum and then query the value first there, and, if not set by the user, take it from the config. This has two benefits:

  1. If set by the user in the application, it will saved in the state file.
  2. If not set by the user, any changes in the config will be reflected when loading a state.

To obtain this, a function similar to the following can be added to libsurfer/state_util.rs

#![allow(unused)]
fn main() {
    #[inline]
    pub fn show_default_timeline(&self) -> bool {
        self.user
            .show_default_timeline
            .unwrap_or_else(|| self.user.config.layout.show_default_timeline())
    }
}

and then this method is used everywhere to access the value. This also requires adding a public method in config.rs, in this case show_default_timeline(), that simply returns the corresponding config value (which should not be public to avoid overwriting etc).

Performance Measurement

If you want to measure performance, surfer has some features to help out. By running the command show_performance, it will show a graph with the total frame time, as well as the time taken to run various parts of the program.

The bulk of rendering is done in signal_canvas::generate_draw_commands. The results of generate_draw_commands are cached by default, and only recomputed when the viewport changes. This makes performance measurement harder, but there is a switch to turn off the cache. You can either click the "Continuous redraw" checkbox in the performance window, or run show_performance redraw to turn off the cache.

Command files

If you are debugging performance issues in a specific situation, you can use command files to automate the setup of surfer. For example, if you want to automatically add waves and turn on performance measurements, you can create `performance.sucl and add command prompt arguments that reproduce the issue to it, then run

surfer <wave file> -c performance.sucl

For example, to check performance with many displayed waves, performance.sucl may look like this:

show_performance redraw
module_add testbench.top.uut
module_add testbench.top.uut

Optimizations

Remember to run in release mode to get accurate performance measurements.

cargo run --bin surfer --release

Flamegraphs

Flamegraphs can be generated using cargo-flamegraph

CARGO_PROFILE_RELEASE_DEBUG=true ca flamegraph -- examples/picorv32.vcd -c performance.sucl

Compile Features

There are a number of compile time features that can be enabled or disabled.

FeatureDescriptionDefault
accesskitAccessibility support.No{{footnote: Included in pre-built binaries.}}
f128128-bit floating-point translator. Requires building with gcc as underlying C-compiler.No
performance_plotThe show_performance command and the drawing performance plot window.Yes
pythonPython translators.No
wasm_pluginsWASM translator plugins.Yes