Skip to content

Contributor Guide

Olof Kraigher edited this page Feb 27, 2023 · 17 revisions

Purpose

The purpose of this document is to help new contributors to get started on the project. We do this by providing a high level overview of the project goals, norms and architecture.

Goals

The project also aims to develop a fully featured language server based on a reusable VHDL parsing & analysis library. The VHDL parsing & analysis library is intended to be able to be re-used by other open source projects that also need such functionally. The idea is that the investment in parsing & analyzing VHDL can be shared by many projects.

The choice of having the language server be the driving requirement behind the parsing & analysis library is the assumption that a language server is the most difficult requirement. A language server requires quick incremental re-analysis as well as robust handling of syntactically incorrect code as it is being edited. A fast and full featured language server also provides a lot of value to the users of VHDL.

The main focus is VHDL-2008 and above as it is the most practically interesting. User who wants to stick to 93 subset and get errors for use of newer VHDL features are not a priority. Such features can be added if the interest exists as a linting pass.

Sub-projects

  • vhdl_lang - VHDL parsing & analysis library
  • vhdl_ls - VHDL language server

Norms

  • Follow the VHDL LRM and aim to be 100% compliant.
    • Do not assume your mental model of VHDL is correct without reading the LRM.
    • Starting work without studying the LRM can lead to architectural dead ends.
  • All features must have good quality unit test proving they continue to work as intended.
    • The test should be nice to read and not be a burden to maintain but also cover the functionality.
  • Avoid false positives to always be usable
    • Never introduce a feature that cause incorrect errors, err on the side of not giving an error.
    • A language server that gives a lot of incorrect errors is a lot more annoying than one where errors missed
    • We cannot build everything from day one so try to implement features in an order that minimizes false positives

Architecture

Major design goals

The main driver for the vhdl_lang architecture is to be a good fit for a language server. This poses a few driving requirements:

  • The entire VHDL syntax-tree should be in memory and be query-able.
  • Syntax errors occur while typing new code and must be recoverable.
  • A VHDL code change should only require a small update of the in-memory AST.

Also to take advantage of modern multi core processors parallel processing should be performed.

Division of concerns

The structure of vhdl_lang is divided into of a few distinct concerns;

1. Parsing

Relevant sub-modules: data, ast, syntax

The first step is to parse the VHDL code into a syntax tree representing the lexical structure of the language. The syntax tree contains source location information that allows the mapping of every element to its original source position. Parsing can be done in parallel at the granularity of individual files.

-- Is foo an array or a function, at parsing time we cannot know
value := foo(1);

2. Analysis

Relevant sub-modules: analysis

The analysis step involves resolving all symbols and their type.

-- Resolving symbol std_logic is required to know it refers to the type defined in package ieee.std_logic_1164;
signal foo : std_logic;

-- Resolving subprogram calls may involve knowing the type of the arguments
function foo(value : boolean) return boolean;
function foo(value : integer) return boolean;
-- ...

-- Which symbol does foo refer to?
value := foo(1);

Analysis must consider the visible region of symbols. A symbol defined in a process is only visible inside that process and only after it was defined. Such regions form a tree structure. The LRM talks about declarative regions. We aim to perform the analysis in a single pass over the syntax tree resolving all symbols and types in the process. During analysis a tree like structure of declarative regions are constructed. This structure is separate from the syntax tree. The syntax tree elements only contain blank references that are filled in with references to the declarative region data structure.

Analysis cannot be fully parallelized since VHDL design units form a tree-like dependency graph that must be analyzed in the tree-order. As long as the dependency order is respected several design units can be analyzed in parallel. To support efficient parallel analysis a design unit is only exclusively locked for writing when analyzed by a single thread but when analyzing dependent design units they can share a read-only access to the already analyzed design unit.

3. Querying

Relevant sub-modules: ast::search

Querying involves searching the in-memory data structures generated by parsing and analysis. Typical queries are;

  1. Find definition: what symbol is at this cursor position and where is it defined.
  2. Find all references: the list of all references to the symbol under the cursor.

Efficient incremental analysis

Efficient incremental analysis on code changes is performed by first re-parsing only the file that changed. Based on the design units inside this file and the knowledge of the dependency graph all dependent design units are re-analyzed with as much parallelism as the dependency graph allow. This works well as even for large projects a single file change will not affect most of the dependency tree.