Best practice, design and code

From Robin

(Difference between revisions)
Jump to: navigation, search
Line 35: Line 35:
==Processes==
==Processes==
-
Unless necessary (eg. need to use if or case statements in a state machine), '''don't''' use a process! They can clutter the code, and make it less clear as to what the design is intended to do. This also ties in with the use of the appropriate conditional statement for a given task.
+
Unless necessary (eg. need to use if or case statements), don't use a process! They can clutter the code, and make it less clear as to what you want to do. This also ties in with the use of the appropriate conditional statement for a given task.
==Conditional statements==
==Conditional statements==
-
In VHDL there are many ways of describing the wanted conditional behavior of your design. To help with deciding which syntax is best, look at this table overview and summary:
+
In VHDL there are many ways of describing the wanted conditional behaviour of your design. To help with deciding which syntax is best, look at this table overview and summary:
{| class="wikitable" style="margin:auto"
{| class="wikitable" style="margin:auto"
Line 54: Line 54:
| '''with ... select''' || Single || Single || Optional  
| '''with ... select''' || Single || Single || Optional  
|}   
|}   
 +
 +
* Target: How many signals/variables can be set if true?
 +
* Condition: Can true condition be a statement?
 +
* When in doubt...
* When in doubt...
Line 59: Line 63:
* ''Only'' use ''''if''''...
* ''Only'' use ''''if''''...
** When you need to prioritize conditions...
** When you need to prioritize conditions...
-
** and have multiple targets
+
** and have multiple targets.
** Typically used for clocked processes.
** Typically used for clocked processes.
* It is fine to use ''''when...select'''' or ''''when...else'''' inside '''if''' and '''case'''
* It is fine to use ''''when...select'''' or ''''when...else'''' inside '''if''' and '''case'''
** Do you need if inside if?
** Do you need if inside if?
-
** Case inside case?
+
** Or case inside case?
** Readability suffers when nesting several levels of if or case.
** Readability suffers when nesting several levels of if or case.
-
Whatever you choose, keep the following in mind:
+
'''Whatever you choose, keep the following in mind:'''
* define:
* define:
** all output for
** all output for
Line 79: Line 83:
-
In VHDL, a latch is inferred when not all conditions is specified for a signal. (Proceed to explain why)
+
In VHDL, a latch is inferred when not all conditions is specified for a signal. (Proceed to explaing why)
===if===
===if===
 +
* Must be used in a process
* Must be used in a process
* Multiple conditions
* Multiple conditions
* Multiple targets
* Multiple targets
* Prioritizes - Order of clauses matters!
* Prioritizes - Order of clauses matters!
-
** First option has priority ("breaks" out of if statement when the first true is found).
+
 
 +
 
 +
* First option has priority ("breaks" out of if statement when first true is found).
* Can be used to infer both latches and flip-flops
* Can be used to infer both latches and flip-flops
-
** Latch when a target is not sufficiently specified.
+
** A '''latch''' is infered when a target is not sufficiently specified.
* Can be nested using ''''elsif''''
* Can be nested using ''''elsif''''
** Can replace ''any'' other conditional statement
** Can replace ''any'' other conditional statement
*** Not recommended!
*** Not recommended!
** Avoid deep nesting
** Avoid deep nesting
-
** ~4 levels should be the maximum...
+
** ~4 levels should be maximum...
 +
 
 +
Example of latching behaviour:
 +
 
 +
'''process'''(all) '''is'''
 +
    '''begin'''
 +
        '''if''' inp1 '''then'''
 +
            '''if''' inp2 '''then'''
 +
                a <= '1';
 +
                b <= '1';
 +
            '''else'''
 +
                a <= '1';
 +
                b <= '0';
 +
            '''end if''';
 +
        '''else'''
 +
            a <= '0';
 +
        '''end if''';
 +
    '''end process''';
 +
 
 +
This process gives this state table:
 +
 
 +
{| class="wikitable" style="margin:auto"
 +
|-
 +
! inp1 !! inp2 !! a !! b
 +
|-
 +
| 1 || 1 || 1 || 1
 +
|-
 +
| 1 || 0 || 1 || 0
 +
|-
 +
| 0 || 1 || 0 || '''Latched'''
 +
|-
 +
| 0 || 0 || 0 || '''Latched'''
 +
|}
 +
 
 +
From the table we can see that when "inp1" is zero, "b" is a latched signal. Reviewing the code with this information we shure enough see an assignment missing for "b" when "inp1" is zero. Furthermroe we also see that "a" only depends in "inp1", while "b" is dependent on both, making us able to simplify the code. Although this might seem to be a digression, it actually makes identifying the issue much easier:
 +
 
 +
'''process'''(all) '''is'''
 +
    '''begin'''
 +
        '''if''' inp1 '''then'''
 +
            a <= '1';
 +
            b <= inp2;
 +
        '''else'''
 +
            a <= '0';
 +
            -- b ass. missing
 +
        '''end if''';
 +
    '''end process''';
 +
 
 +
From this simplified code we can much easier see that "b" is missing an assignment and "a" is fully covered. In addition to showing the importance of '''always specifying all output for all conditions of inputs''' it also showes the value of '''lowering the depth of if statements''' when possible.
 +
 
 +
 
 +
 
===case===
===case===
Line 101: Line 158:
===when...select===
===when...select===
-
==FSM==
 
-
* Keep register assignment separate from combinational logic (as with/see sequential logic)
 
-
* Keep calculation of next state separate from state output
 
-
While this is stating the same criteria (state tree) twice, it makes reading the state machine behavior easier. In contrast, having next_state logic mixed with state output, debugging one will include debugging the other. It does not take much logic before the smokescreen effect is notable when reading code.
 
'''FSM Example:'''
'''FSM Example:'''

Revision as of 08:15, 13 June 2023


The rules noted here are guidelines that will help creating designs that are easy to verify, read and maintain

RTL code

Sequential logic

Separate registers from combinational logic

  • By separating registers from combinational logic you will reduce the risk of creating more registers than you planned to do. By keeping combinational logic in processes that are solely combinational, you will get the full benefit of warnings from the toolchain if you accidentally create latches or registers within the code.

Register reset should be stated where the registers are assigned

  • Reset is for clearing registers to have them in a predictable state. Although synchronous reset can be stated as combinational logic, the use of reset is setting registers, not interfering with the combinational code function. This may seem as a slight contradiction to the bullet above, but for the sake of modifiability and general readability, it makes sense to keep reset.

Sequential logic example:

REGISTERS: process(clk) is 
  begin 
    if rising_edge(clk) then 
      if reset then 
        r_signal <= '0';
        r_vector <= (others => '0');
      else
        r_signal <= next_signal;
        r_vector <= next_vector;
      end if;
    end if;
  end process;
  
COMBINATIONAL_LOGIC:
next_signal <= valid_a and valid_b;
next_vector <= std_logic_vector("0" & unsigned(a) + "0" & unsigned(b));

Variables and Signals

---

Processes

Unless necessary (eg. need to use if or case statements), don't use a process! They can clutter the code, and make it less clear as to what you want to do. This also ties in with the use of the appropriate conditional statement for a given task.

Conditional statements

In VHDL there are many ways of describing the wanted conditional behaviour of your design. To help with deciding which syntax is best, look at this table overview and summary:

Conditional statement overview in VHDL
Statement Targets Conditions Process
if Multiple Multiple Required
case Multiple Single Requires
when ... else Single Multiple Optional
with ... select Single Single Optional
  • Target: How many signals/variables can be set if true?
  • Condition: Can true condition be a statement?


  • When in doubt...
    • Try 'with...select'. This will force you to make visible choices.
  • Only use 'if'...
    • When you need to prioritize conditions...
    • and have multiple targets.
    • Typically used for clocked processes.
  • It is fine to use 'when...select' or 'when...else' inside if and case
    • Do you need if inside if?
    • Or case inside case?
    • Readability suffers when nesting several levels of if or case.

Whatever you choose, keep the following in mind:

  • define:
    • all output for
    • all conditions


Latching

Often times the issue with using the incorrect conditional statement for the task is the inferring of latches in the design. To understand why this is a problem, we must understand what a latch is, and how this can create a misbehaving and unpredictable design.

A latch is a digital component similar to a flip-flop in that it is used to store values, but differs from a flip-flop in when it is able to be read to. A latch is "open" during the positive duty cycle of the clock, while a flip-flop is edge-triggered. This means that if a value changes during the positive duty cycle (maybe to prepare for the next clock cycle). A flip-flop won't read this value, but a latch will, which may result in an incorrect design.


In VHDL, a latch is inferred when not all conditions is specified for a signal. (Proceed to explaing why)


if

  • Must be used in a process
  • Multiple conditions
  • Multiple targets
  • Prioritizes - Order of clauses matters!


  • First option has priority ("breaks" out of if statement when first true is found).
  • Can be used to infer both latches and flip-flops
    • A latch is infered when a target is not sufficiently specified.
  • Can be nested using 'elsif'
    • Can replace any other conditional statement
      • Not recommended!
    • Avoid deep nesting
    • ~4 levels should be maximum...

Example of latching behaviour:

process(all) is
   begin
       if inp1 then
           if inp2 then
               a <= '1';
               b <= '1';
           else
               a <= '1';
               b <= '0';
           end if;
       else
           a <= '0';
       end if;
   end process;

This process gives this state table:

inp1 inp2 a b
1 1 1 1
1 0 1 0
0 1 0 Latched
0 0 0 Latched

From the table we can see that when "inp1" is zero, "b" is a latched signal. Reviewing the code with this information we shure enough see an assignment missing for "b" when "inp1" is zero. Furthermroe we also see that "a" only depends in "inp1", while "b" is dependent on both, making us able to simplify the code. Although this might seem to be a digression, it actually makes identifying the issue much easier:

process(all) is
   begin
       if inp1 then
           a <= '1';
           b <= inp2;
       else
           a <= '0';
           -- b ass. missing
       end if;
   end process;

From this simplified code we can much easier see that "b" is missing an assignment and "a" is fully covered. In addition to showing the importance of always specifying all output for all conditions of inputs it also showes the value of lowering the depth of if statements when possible.



case

when...else

when...select

FSM Example:

This example will base itself on this diagram:

File:Best_practices_fsm_diagram.png

Personal tools
Front page