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>
Navigation
-
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
Name | Description |
---|---|
Cancel | No operation |
GoToEnd | Scroll view to last time in simulation |
GoToStart | Scroll view to first time in simulation |
ZoomIn | Zoom in to the range defined by the start and end time of the gesture |
ZoomOut | Zoom out a constant factor |
ZoomToFit | Zoom 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:
- If the computer running the server is directly accessible, it can be accessed using the provided URL.
- 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
Os | Path |
---|---|
Linux | ~/.config/surfer/translators/ |
Windows | C:\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.
Os | Path |
---|---|
Linux | ~/.config/surfer/decoders/ |
Windows | C:\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.
Os | Path |
---|---|
Linux | ~/.config/surfer/config.toml . |
Windows | C:\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
Os | Path |
---|---|
Linux | ~/.config/surfer/themes/ |
Windows | C:\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
Dark high contrast
Dark plus
IBM
Light high contrast
Light plus
Okabe Ito
Solarized
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:
- Fork and clone the repository
- Setup pre-commit
- Create a branch (other than
main
) - Edit code
- Commit code with a sensible commit message
- Push branch
- Create a merge request
- Wait for the change to be merged, including fixing suggestions from reviewers
- 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.
- Install pre-commit:
pip install pre-commit
- 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:
-
Run
cargo test
-
Run
./accept_snapshots.bash
-
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:
- If set by the user in the application, it will saved in the state file.
- 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.
Feature | Description | Default |
---|---|---|
accesskit | Accessibility support. | No{{footnote: Included in pre-built binaries.}} |
f128 | 128-bit floating-point translator. Requires building with gcc as underlying C-compiler. | No |
performance_plot | The show_performance command and the drawing performance plot window. | Yes |
python | Python translators. | No |
wasm_plugins | WASM translator plugins. | Yes |