Accelerating Hardware Design Using DVT IDE for Visual Studio Code

Overview

DVT IDE for Visual Studio Code is an integrated development environment that significantly improves productivity of hardware design and verification engineers who are working with languages like Verilog, SystemVerilog, or VHDL. This video focuses on how design engineers can benefit from the features provided by DVT.

00:00 Introduction
06:37 DVT Compilation
09:25 Design Exploration
18:17 Code Navigation
22:52 Quick Fixes
26:23 IntelliSense
30:07 Code Formatting & Refactoring
33:34 Tasks
34:58 Source Code Management
36:09 Contact Us

DVT IDE for Visual Studio Code: https://dvteclipse.com/products/dvt-ide-for-visual-studio-code
Visual Studio Code: https://code.visualstudio.com/

You can install DVT IDE from Visual Studio Marketplace using ext install amiq.dvt command.

Visual Studio Code documentation resources:
Cheatsheet: https://code.visualstudio.com/docs/getstarted/keybindings#_keyboard-shortcuts-reference
Basic Editing: https://code.visualstudio.com/docs/editor/codebasics
Extension Marketplace: https://code.visualstudio.com/docs/editor/extension-marketplace
Version Control: https://code.visualstudio.com/Docs/editor/versioncontrol
Tasks: https://code.visualstudio.com/Docs/editor/tasks

This video was shot using DVT 22.1.1

Details

Introduction

DVT IDE for Visual Studio Code is an integrated development environment that significantly improves productivity of hardware design and verification engineers who are working with languages like Verilog, SystemVerilog, or VHDL. This video focuses on how design engineers can benefit from the features provided by DVT.

First, we'll cover some VS Code basics. Then, we'll see how DVT analyzes code as you type in order to provide instant compilation errors and maintain an up-to-date model of the design, which is then used by all the other features of the tool. We'll then talk about visualization techniques using structured views and diagrams, and about seamless navigation around code. Next, we'll cover how DVT can help with fixing all sorts of compilation problems, and how context-sensitive autocomplete, code formatting, and refactoring can help you write high-quality code fast. In the end, we'll catch a glimpse of DVT VS Code integrating with external tools and source code management.

Overview of Visual Studio Code GUI Features

User Interface

The user interface is divided into five areas: Editor, Sidebar, Activity Bar, Status Bar, and the Panels area.

Editor

The editor is where you spend most of your time coding. On the right there is a mini-map, a high-level overview of the file content, useful for quick navigation within the file. Another navigation bar, the breadcrumb, sits above the editor contents. It shows the current location and allows you to quickly navigate between folders, files, and the elements or symbols defined within the current file.

Editors can be freely split and moved around. Any basic text editing operation you would expect from a text editor is there, including vertical selection, multiple cursors, code folding, find and replace with regex across multiple files, and so on.

However, we will not cover such functions, but rather focus on DVT-specific features. Check the video description for a link to the VS Code User Guide section that covers basic editing.

Sidebar and Activity Bar

The Sidebar shows different views that assist you while working on your project. The views presented in the Sidebar change anytime you use the Activity Bar.

When selecting the Explorer activity, the Sidebar presents a file browser, and the content outline of the currently edited file. Particularly for a SystemVerilog design file, we see the module, ports, signals, and so on.

When selecting the Source Control activity, the Sidebar presents revision control changes.

When selecting the Extensions activity, the Sidebar presents the installed and recommended extensions. Now, since we're here, it is worth mentioning that you can search and install extensions. For example, for emulation modes like VI, Emacs, or Eclipse.

In the Activity Bar, you will also find the DVT activity, which provides various compilation-related views, as well as the Design and Verification Hierarchies. We'll talk about them in more detail later.

Status Bar and Panels Area

The Status Bar, located at the bottom of the window, displays information about the project and the currently active file in the editor.

Finally, below the editor is the Panels area, where you can find the Problems View, Output, Debug Console, and an integrated Terminal.

Command Palette

The Command Palette, which is usually brought up by using the Ctrl + Shift + P shortcut, provides quick access to most, if not all, of the tool functionality. It shows all available commands along with their shortcuts, with the most recently used ones at the top of the list.

Type a few letters to filter by command name. Matching commands are shown. The > sign here means we're looking for commands. If we turn it into a #, we are searching for symbols, like modules, interfaces, enums, macros, and so on. You can even specify what type of symbol you're looking for. Similarly, the @ prefix searches the symbols defined within the editor in focus, a very convenient way to navigate large files. Simply typing a few letters without any graphics is a quick way to open files.

Any of these modes of the Command Palette can also be reached by executing commands, such as Go to Symbol in Workspace..., Go to Symbol in Editor..., or Go to File....

To change the UI color, open the Color Theme preference using the Command Palette and scroll through the entries to get an instant preview. Notice that themes are grouped into light, dark, and high contrast categories. When satisfied with a particular theme, press Enter to use it.

You can find plenty of information about VS Code on the web. This was just a quick introduction.

DVT IDE for Visual Studio Code

DVT Compilation

Now let's dive into the DVT IDE for VS Code. We should first mention that at the heart of it, there is a built-in SystemVerilog compiler. It analyzes the source code, reports compilation problems and builds a complete model of the elaborated design. This model is used by all other editing, navigation, and visualization features, from the workspace symbols we've just seen, to automatic correction of compilation problems, renaming signals across the design hierarchy, inspecting the values of elaborated parameters or schematic diagrams.

Even the most basic features are augmented. For example, the code highlighting. Notice how ports are green while internal signals are black, input ports in italic versus outputs in regular font. Furthermore, the compiler works incrementally. Whenever you touch the code, the changes are analyzed and the model gets updated instantly, including, of course, any compilation errors. You don't need to wait for seconds or perhaps even minutes to recompile your entire project only to reveal a missing semicolon. All this time spent hunting down trivial syntax errors is cut down to zero.

Furthermore, unlike a traditional compiler which stops at the first encountered error, DVT's engines are error resilient and continue code analysis in spite of the errors. This allows you to benefit from the tool even if parts of the code are incorrect or incomplete, as happens most of the time during development stages. DVT detects a wide variety of common problems on the fly, ranging from simple typos to tricky syntax and semantic issues in RTL constructs.

By the way, language constructs that deviate from the IEEE standard are also reported as non-standard. Keeping your codebase standard compliant is good practice and will save a lot of time if you ever need to add another SystemVerilog tool to your flows or switch to a different vendor.

Design Exploration

Now let's take a deep dive into the design exploration capabilities of the tool and quickly get familiar with this example SoC that will be used throughout the presentation.

The elaborated Design Hierarchy, starting from the design top, is presented in this dedicated view. Here, by the way, you can easily search for a particular instance, say uart, or for all instances containing a port or parameter with .addr in its name, or you can find all instances located directly under a particular design path, say, under the dma/ch1/.

Note that the elaborated parameter values are also presented in the members panel. Simply double-click to see where the parameter gets its value. Notice that resting the mouse pointer over the macro, displays its value in a tooltip.

Schematic and Flow Diagrams

Probably an even better means to visualize the design are the schematic diagrams. They present sub-instances, ports, connections, and combinational and sequential logic blocks. You can dive into sub-instances, expand instances in place, or increase the depth to see nested instances recursively. The search bar helps to quickly find your way around such large diagrams.

Conversely, you can turn to a flow diagram, where multiple connections from one instance to another are collapsed in a single directional bus, leaving room for a more abstract bird's eye view of the design. Filters allow you to focus on particular elements of interest. For example, clock or reset signals.

For signals, ports, instances, and logic blocks, you can choose to Show Connections. This action removes everything from the diagram except the selected elements and all other elements directly connected to them. You can even see the connections between multiple elements in the same diagram.

Diagrams are also a great means to trace signals, for example this port. Right-click and pick Show Connections. From this point on, you can rely on Show Sources and Show Destinations to further trace the signal. When all is done, go back to the filters and select Hide Unconnected Ports for a more compact representation. For fine-tuning, you can even manually remove unwanted elements from the diagram.

When you're satisfied with the looks of it, simply save it to a file to share it with your colleagues, or use it for documentation purposes.

Finite State Machines Diagrams

DVT can also help you visualize and understand Finite-State Machines, or FSMs. Trigger the Show Diagram command while standing on a state machine variable. Selecting a state or transition highlights previous and the next states in different colors. You can right-click on a transition and use Go to Source to jump to the assignment that causes it.

For further inspection, you can use the state transition table. For each transition, it shows the current state, next state, and condition. The transitions are conveniently numbered, both in the table and in the diagram. The table and diagram selections are cross-linked.

WaveDrom Timing Diagrams

Speaking of diagrams, DVT can also render waveforms using the popular WaveDrom format. The nicest part is that waveform descriptions can even be embedded into comments between dedicated pragmas. Hover over the comment to see the waveform in a tooltip. Even handier, the same happens when you hover over the module name in a different file, perhaps where it's instantiated.

Bottom line, DVT diagrams enable you not only to visualize, but also to interactively explore the design, to instantly focus on the relevant details while leaving aside unwanted clutter.

Navigation Capabilities

Design Breadcrumb

You might have noticed that the status bar gets updated as we navigate up and down the hierarchy in schematic diagrams. This is called the Design Breadcrumb. If we regard the design as a huge map, then the breadcrumb is our current location marker. It shows a couple of parents from the current module instance, and hovering over it with the mouse pointer reveals the entire path, starting from the design top. Click on it to move around the design using the Command Palette.

The Design Breadcrumb is also available when editing source code. Notice how the path got preserved when jumping from the diagram into source code, and how it gets updated when moving around the design. Hyperlinks dive into instances. Conversely, the Open Design Breadcrumb Instance command takes you one hierarchy level up, where the current module is instantiated.

Design parameter values shown in the tooltips or tracing also depend on the current breadcrumb. For example, this mux_input_size parameter has the value 3 as shown in the tooltip for this particular design path being set here. However, this M in the breadcrumb indicates that the module is instantiated on multiple paths throughout the design. Now, let's switch to a different path using the Select Other Design Breadcrumb Instance... command. Notice how this gets reflected in the display parameter value, which is now 46, getting its value in this other instantiation of the config_mux.

Speaking of design hierarchy paths, it's good to know that you can easily copy the current path to the clipboard, for instance, to use it in a configuration file.

Hyperlinks and Preprocessing

Now let's take a quick tour of other navigation capabilities. Hold down Ctrl and click on any identifier to jump to its declaration. Mix in the Alt key to open it in a new split editor, or use the context menu or the Command Palette. As you can see, this works for any language elements: modules, ports, signals, macros, and included files.

Speaking of preprocessing, it's good to know that DVT can apply preprocessing for a region of code. Use the Collapse Inline Macro Expansion command on the expansion pragma to revert it. You can go back and forward between the visited code locations like in a web browser.

Notice that you also have the Peek option, which, as the name implies, offers a lighter alternative to actually opening another editor, yet allowing you to peek around. Press the Escape key when done.

Find References and Rename Symbol

Conversely, to find where an identifier is used, we trigger the References command. Again, it comes in multiple flavors. Find All References displays results in a dedicated view in the sidebar. Peek References lets you quickly preview the usages with the arrow keys, without leaving your current location in the editor. Enter takes you there, and Escape closes the peek. Go to References is like the peek, except that it takes you directly to the usage, should there be only one.

Now, further refinements of the usages are available. Show Instances of a module will yield only places where it's instantiated, leaving out other types of references, such as binds or configuration rules. You can easily see the readers or writers of a signal, variable, or parameter. Speaking of writers, you can also use Jump to Assignment to cycle through the places where a signal gets its value, even across hierarchy levels.

Naturally, since the tool knows precisely where the name of a module or signal is used throughout the entire codebase, it can accurately perform renames. From the command palette, trigger Rename Symbol, enter the new name and press Enter to apply or Shift + Enter to preview the changes. Select the file about to be changed from the list to get a before and after comparison. This is a huge time-saver since manually changing the name of a port, module, or macro may involve edits to many files, and text editor searches may require tedious validation because of other elements in the code with similar or identical names.

Perhaps now it's worth stressing that all these searches are done semantically, within the compiled database. Inaccuracies inherent to text searches are a thing of the past, not to mention the time wasted with greps and filtering out matches inside comments or strings, or even worse, name collisions.

Code Editing with DVT

Quick Fixes

Now let's take a closer look at the code editing capabilities. DVT not only provides instant feedback, but also suggests fixes for problems such as mismatched port connections or incomplete sensitivity lists.

Let's focus on this non-existing port error and let's assume we'd want to create the port in the module's declaration. Traditionally, we'd have to add the port to the module's port list and specify its direction and data type as well. On top of that, we'd have some new issues to deal with. The other instances of the module are out of sync with the new port list and would have to be updated. Quite a lot of typing.

In contrast, let's apply one of the suggested fixes, namely Add Port. The port is created and the problem disappears instantly. Other instances of the module are updated with an empty port connection and the reminder comment. By the way, you can install extensions which scan and present in a dedicated view all the reminders embedded in comments.

Now, what if the owner of a module changes some ports without updating all of the module's instances? In this case, we get non-existing port errors, as well as missing port connection warnings. Simply press Ctrl + . with the editor cursor on either problem to bring up the list of quick fix suggestions, and pick Update Instance to match the module definition. Perhaps some of the changes were simple renames, and just need to be tied accordingly. But others may require new logic, and again, we can use a quick fix to explicitly declare the implicit signal that resulted from the new port connection. Note how the signal width gets automatically inferred. Now all we have to do is focus on the actual new logic.

Let's take another example, this undeclared identifier. This time, let's use the Command Palette to see the proposed corrections. Since the tool has an accurate representation of the entire design, it knows what other signals are likely to be used in this context and resemble the problematic identifier and proposes them as possible fixes.

Finally, let's look at this combinational always block. There are a couple of problems detected in the sensitivity list: a signal is missing, while another is superfluous, both instantly corrected by the tool.

IntelliSense (Content Assist)

Now let's move on to another powerful tool functionality, IntelliSense in the VS Code terminology, also known as Content Assist or Autocomplete.

Simply press Ctrl + Space anywhere in your code to get meaningful context-aware completion suggestions. For example, here we only get ports of the instantiated module ahb2apb, here only parameters of ahb2apb, whereas here we only see signals or parameters of the current apb_subsystem module.

Start typing any fragment of the name to shrink down the list. It works similarly for signals in logic blocks or assignments, hierarchical paths in defparams or binds, macros, or includes.

Code Templates and Code Factory

Furthermore, you can quickly deploy code templates for language constructs, such as for loop generates, always blocks, or case statements.

Auto Instance is also available. Just place the cursor where you want the new instance, press Ctrl + Space, and type a few letters of the module name. Matching signals and parameters in the current module are automatically connected, while the remaining ones are created automatically. Use the Tab key to traverse signals and change their names as needed.

Another way of creating new module instances is via the Code Factory. Place the cursor on top of a module declaration and trigger the Set Code Factory Input command. Now, Ctrl + Space brings up just a handful of code factory operations. In addition to the instance, we can create signals, perhaps in a different section of the file, or even an entire test bench in a new file: a top module containing signal declarations, the instance, the initialization block, and the clock generator. By the way, the code factory can also help you quickly get started with the wavedrom description from the selected module. Once you are done using the code factory, trigger the Clear Code Factory Input command to return to the regular autocomplete.

Code Formatting and Refactoring

Our next topics are code formatting and refactoring, which will help you effortlessly improve code readability and maintainability, two key aspects of code quality, especially in projects that span across multiple teams.

This code looks quite messy by any standards. Run the Format Document command and everything falls nicely into place. The DVT code formatter is highly customizable. Just run the Open User Settings command, search for "DVT formatting", and tune the available knobs in accordance with your code styling guidelines. For example, indentation related, such as consistent alignment of begin...end, module parameter lists, or vertical alignment of ports, port connections, and signal declarations. By the way, it's worth remembering this place. This is where you can find and configure all the tool preferences. Now, let's take a look at how the formatter performs in port lists, port connections, signal declarations, and logic.

Speaking of code transformations, the tool can automatically expand .* wildcard port connections, and convert named argument bindings to positional argument bindings, and vice versa.

You've probably noticed that these functions are triggered via the Refactor... command. In a nutshell, refactoring means modifying the code without changing its functionality in order to improve readability and comprehensibility. Designs grow over time, and without maintenance, code tends to become more and more complex and harder to maintain. Perhaps a port name lost its relevance as the design evolved. As we've already seen, DVT helps you accurately rename it in one shot.

Maybe a module grew excessively and it's time to split it up into several new sub-modules. That's where the extract to module functionality can help save some minutes or even hours of tedious work. Simply select a fragment of code and run the Refactoring command, then pick Extract to Module. The selected piece of code will be moved to a new module in a new file. Fill in the destination directory and the name of the new module. The initial selection is replaced with an instance of the new module. The ports of the new module are automatically created and connected in the instantiation.

Integration with External Tools and Source Code Management

So far we've covered lots of visualization, editing, and navigation related functionalities of the tool. But an IDE does even more than that. It helps you over the entire development process, from writing and reviewing code to simulation and debugging.

If you just want to start the simulation, synthesis or linting from within VS Code without having to switch to a command line, the most straightforward way is from the built-in terminal. But going one step further, you can define tasks, which store a particular run configuration. Select Configure Task from the Command Palette. The task configuration includes the command or script to be executed, environment variables, and the way output should be handled.
Perhaps most importantly, you can configure problem matchers, which grab the standard output of the external tool and show the errors and warnings in the Problems View, alongside other issues. Instead of grepping through simulator logs, locating the originating file in the source tree, going to a line, and so on, the source of the error is one click away.

VS Code ships with a Git Source Control Manager extension. Most of the source control UI and workflows are common across other revision control extensions. In the case of Git, the file browser shows various one-letter decorations, for example, "Modified", "Untracked", or "Ignored" files. The timeline view presents the history of commits. Some useful annotations are also available in the editor, in the gutter and overview ruler.
The Source Control activity provides further functionality. The current branch is shown in the repositories view, and from here you can easily switch to a different branch or create a tag. All changes are presented in the source control view and from here, you can easily stage, view diffs, and even commit.

Conclusion

This brings us to the end of our presentation. Thank you for watching! We hope you've enjoyed learning about the DVT IDE for VS Code. Please don't hesitate to contact us for more information.