As a designer, you might have heard of tools like Spyglass Lint, Questa Lint, JasperGold Superlint etc. It is used to do Lint checks in the RTL code. Linting is the most basic and most important RTL check a person does right after finishing with their RTL code. In software code written in languages like C++ or Python, the compiler is enough to do all the checks needed to make your code work correctly. If your code is compiling without errors, you are ready to deploy your code.
It is not the same case with coding languages that describes hardware like SystemVerilog or VHDL. Yes, there is a compiler that compiles your RTL to make sure you are not making any semantics error, but that’s about it. It does nothing else. Whereas there are a lot, and I really mean “A LOT!” of things that can still go wrong if you directly use this compiled code in your design. We are describing a hardware using these languages, the compiler does not know that. It is not considering that if this code is turned to a hardware, what are the things that can go wrong and cause a failure. LINT DOES THAT.
After a code is compiled and is free of semantics errors, we run Lint on this code. The Lint tools check the RTL against a set of predefined rules and will flag an error if any of the rules is violated. There are various good practices to follow while writing RTL code and Lint tool makes use of them. Some rules are legacy rules and are constant in the industry across companies and some rules are company specific. These rules have been perfected over the years and new ones are being added as industry and design practices evolve. It is important to note here that the rules are not constant, they keep getting revised according to the results seen in the real world.
9 out of 10 times you will get asked questions on Lint because if you are a designer, any kind, you must know Linting, simple as that. Think of your “ready to deploy” RTL code as compile + lint clean RTL code. Although there are hundreds and thousands of Lint rules, there are a selected few which you should absolutely know, and these are the rules that an interviewer wants to hear from you. Let us see them below.
Inferred Latches This is the most common mistake a beginner makes while writing an RTL code. Why it happens and its implications are very important to know even for a recent college graduate. Inferred latches are also referred to as unintentional latches. These latches are called unintentional because you did not want your RTL code to create a latch, still it did. This generally happens when your code does not account for the value held by a variable at certain times. Consider the code snippet below: always_comb begin if (a > b) c <= a; end We have not mentioned what the value of “c” should be if the condition is not satisfied. The synthesis tool will assume that if the condition is not satisfied, the value of “c” should be the previous value and hence it will infer a latch here. This is the reason why it is always advisable to complete the if-else statements and write all possible combinations when using case statement. Incomplete sensitivity list can also cause synthesis tool to infer a latch. Unless you deliberately want to add a latch due to some design requirements, latches are mostly bad for your design because: a. they cause difficulty in closing timing b. they have unpredictable behavior c. they create power issues Hence, it is very important to avoid unintentional latches in your design to save you from future hassles.
Combinational Loops Combinational Loops create race conditions. Consider the code snippet below: always @* a = a & b; This computes AND of “a” and “b” and sends it back to “a”. This will lead to an infinite loop completely occurring at zero operation time. The output will oscillate between 0 and 1 and is unpredictable. As you already know, we cannot afford unpredictability in a digital design (think CDC) and this needs to be avoided at any cost. How to resolve this? Put a flop in between so that ANDed values are sampled every cycle and then given back to “a” instead of directly giving it back. always @(posedge clk) begin if (reset) q <= 0; else q <= a & b; end assign a = q;
Width mismatches Consider this code snippet: wire [3:0] a; wire [7:0] b; assign a = b; Did you notice something? We are assigning 8-bit value to a variable which can only hold 4-bit value. What happens to the other 4-bits? It gets lost! This is dangerous and can cause tragic results if overlooked. Some compilers can catch this too but not all.
Multi Driven Signals It is illegal to use multiple assign statements to the same wire in a synthesizable code that will become the output port of a module. This type of code is not synthesizable, and the synthesis tool will throw an error if you try to synthesize it. We are running Lint just to avoid these kinds of issues so that we don’t see them down the design flow. Modern compilers might flag this as an error or warning too. Please note that it is LEGAL to drive a 3-state wire by multiple assign statements.
Checks for non-synthesizable blocks There are lots of constructs and keywords in SystemVerilog which are synthesizable, but a lot more are non-synthesizable and is only used in verification environments. For obvious reasons we need to avoid them in a synthesizable code. Lint can flag those kinds of constructs which cannot be synthesized so that we don’t see an error during synthesis.
Race Conditions These are situations where the order of the timing of events can affect the outcome of the design. It can occur due to improper use of blocking and non-blocking assignments, asynchronous resets or combinational loops. Race conditions can lead to synthesis-simulation mismatches and functional failures. The outcome of any design should always be the same and predictable. Lint tools can flag any design practices that may lead to race conditions and help you avoid it.
Undriven inputs The inputs which we have declared but not used anywhere in the code can result in undefined values or synthesis errors. Lint tools can catch these kinds of issues effortlessly.
Unused declaration This can help you catch declared signals, variables or modules that are never used in the design. They can make the code less readable and waste resources and hence we should remove them from the RTL.
I hope I was able to make you understand the importance of Linting and hope you will avoid these mistakes while writing your RTL code. I am leaving a link below which I think is the absolute best PDF available on the internet that outlines the best design practices. It is very detailed, and it not just lists the guidelines, but also explains what will happen if you don’t follow them. If you understand and follow all the guidelines mentioned in this PDF, you will become an infinitely better designer than you were before.
See you in the next one!