It is typically performed on the synthesisable Register Transfer Level (RTL) subset of the HDL, be it VHDL or Verilog/SystemVerilog, though it can also be partially applied at the gate level too.
Why lint? Because simulation and synthesis can take a long time, particularly for large gate-count devices, it is worth going into both with the cleanest code possible; thus minimising the risk of having to trace problems back to HDL. Or to put it another way, linting is about securing design quality assurance early on in the design flow.
Linting performs a static analysis of the code, checking it against recognized design rules and best-practice guidelines. It also helps identify problems such as unreachable statements, suboptimal synthesis, simulation versus synthesis mismatches, clock/reset connectivity issues, unsynchronized Clock Domain Crossings (CDCs) as well as looking for obstacles against testability, all of which help reduce the number of synthesis and implementation re-runs.
There are a number of RTL linting tools available on the market. They tend to be integrated with whichever design entry tool is being used. In some cases, and subject to the capabilities of the design entry tool, bugs discovered during linting can be shown in the design environment, in the code editor and/or on a schematic representation of the design’s netlist.
As a DRC tool, a linter needs to know what to be checking. In this respect, linting tools will include libraries of rules. These will include checks for syntax against best-practice guidelines such as STARC and the Reuse Methodology Manual. In-house coding styles can also be imposed as many companies wish to have consistency between their engineers and any subcontractors they are using.
Rules developed for providing design assurance for safety-critical applications are also available; for example, Aldec has a DO-254 plug-in for its ALINT-PRO linting tool. Developed in cooperation with industry experts that have worked on safety-critical avionics programs, the plug-in rules include checks for style, readability, simulation, clock/reset management, suitability for (design) re-use, portability, coding for safe synthesis and implementation.
The variations in naming and coding style, as well as the differences in the target implementation flows (i.e. ASIC versus FPGA), need to be taken into account. However, one guideline does not fit all technologies (100%) so a linter will typically provide a large number of flexible configuration switches.
A simple example is a conflicting requirement to use flip-flops with either asynchronous or synchronous resets, or, perhaps, active-high versus active-low resets. Some of the rules might not be appropriate for specific flows, and need to be disabled completely (e.g. gated clocks are fine within an ASIC project but are usually prohibited for FPGA designs).
Accordingly, it is a subset of enabled rules and the related configuration settings that form a ‘Linting Policy’; a reusable configuration artifact, which is usually common within a single organization, and is maintained by a leading design specialist.
In terms of the kinds of errors that linting tools find, these include:
- Violation of naming and comment conventions against regular expressions defined in the policy (i.e. naming active-low signals with _n suffix or uppercasing Verilog macros).
- Use of prohibited constructs that could break the synthesis and implementation flows (i.e. event control statements in subprograms, implicit state machines, mixing blocking and non-blocking assignments or assigning the same signal from multiple processes).
- Cases that create a mismatch between RTL and gate-level simulations (i.e. incomplete or redundant sensitivity list, misused synthesis pragmas, X-value propagation or out-of-range array references).
- Mismatching bit widths between LHS and RHS of the assignments, between subprogram arguments and the actual expressions, as well as between the operands of arithmetic and logical expressions, as well as mixing signed and unsigned arithmetic.
- Incomplete or redundant conditional statements and assignments under conditions; where an incomplete conditional statement would lead to unexpected inference of latches and a redundant conditional statement would create unreachable statements.
- Coding styles that lead to poor circuit performance after synthesis (i.e. priority-based versus parallel data paths or issues with arithmetic resource sharing)
- Clock and Reset issues, such as unexpected logic on the control path that might create: glitches; connection to unexpected pin kinds (e.g. clock-to-data or clock/reset mix signal mix-ups); a lack of controllability or a mismatch between the topology and timing constraints.
- CDC issues (e.g. unsynchronized transfers, broken synchronizers, combinational logic, divergence and convergence on the crossing, re-convergence of synchronized signals in the target domain, unsafe reset deassertions).
Library cells and IP blocks
The target implementation technology will always be based on libraries of primitive cells. In the FPGA world, the vendors provide simulation and timing models. Some libraries are common across several devices within a vendor’s portfolio (e.g. Xilinx’s Unisim library or Altera’s Megafunctions library). Sometimes though a library is device specific. In the ASIC world, designs refer to cell libraries provided by the foundry which match the selected chip manufacturing process.
In both cases some of the cells might be directly instantiated in the code, instead of being inferred during the synthesis. Some of the important cells – such as clocking resources, memories, serial transceivers – do not have any (or at best any efficient) RTL description equivalents, as they represent hard analog blocks. At gate level, the entire design will consist of primitive instantiations and the connecting wires only.
The simulation models of such primitives are mostly non-synthesizable, so a linter needs a different representation than a simulator to perform advanced clock/reset and CDC checks. In essence, the linter needs to have a picture of the timing paths across the cells, which might be obtained by reading a format like Liberty, or in the form of timing constraints. Some of the cells, like multi-clock memories, are very complex and have generic-dependent operational modes with different properties. It is expected that a linter will help to deal with these complexities out of the box or at least with minimal user effort.
On the other hand, no linter can be aware of all possible IP blocks. If the sources of the IP are inaccessible (encrypted or simply just not available), the user has no choice but to provide a substitute model for the linter; in the form of constraints or a simplified RTL implementation. The substitute model needs to represent at least the basic timing dependencies between the I/O pins. If it does not, important connectivity issues might be missed and the IP block is treated as a black-box.
Is Linting for me?
Linting has the potential to save time – by reducing the length and number of simulation and synthesis cycles, for example – as well as providing early confidence in the design. Linting can also help automate peer reviews, thus bringing the focus of the reviews to higher level architectural aspects (where other verification methods would be used) and the completeness of the documentation – because the boring task of catching the RTL mistakes is linting tool’s responsibility.
A recommended method is to run the initial iterations of policy adjustments and design corrections to obtain an empty or almost empty bug log.
Subsequent runs should then be monitored against changes, and the regressions need to be carefully reviewed. In reality, the log will not be empty, as some of the reported violations may need to be waivered; and the user has the option to explain them away by commenting in the report produced by the linting tool.
As for why waivers may be acceptable, it could be that the rule definition is too restrictive. For instance, the rule might state that non-portable synthesis pragmas should be avoided; but sometimes there is no workaround. In other cases, there might be external assumptions, which are invisible just by looking at the RTL. For instance, a design spec’ might state that a 4-bit input port (i.e. possible values from 0 to 15) is not be allowed to accept values greater than 10.
Complementing linting with some form of functional verification, such as formal property checking or assertion-based simulation, makes for a very powerful combination. For example, a linter may help in synthesizing some of the functional properties to provide proof (or a counter-example) in doubtful cases; i.e. they are unclear if detected by static checks alone.
As mentioned, there are a number of linting tools available on the market. Some are geared more towards very high gate-count ASICs and SoC designs; and have annual licence fees of up to 100k Euros per seat. Other linting tools are available, for considerably less money, which target smaller designs of up to (say) a few million gates. These are ideal for FPGA designs and smaller ASIC projects, such as IoT designs.
Also, the safety-critical industries, such as aerospace and automotive are making increasing use of multiple small- to medium-sized FPGAs within sub-systems.
In conclusion, linting has a clear and significant role to play in verifying the quality of the design (as expressed in HDL) at a time when it is easiest to fix bugs. It also provides the earliest possible assurance regarding the quality of the code.
about the author:
Sergei Zaychenko is a software product manager at Aldec – www.aldec.com. He is responsible for design rule checking and clock domain crossing verification.