Best practice, design and code

From Robin

(Difference between revisions)
Jump to: navigation, search
Line 95: Line 95:
* First option has priority ("breaks" out of if statement when 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
-
** A '''latch''' is infered when a target is not sufficiently specified.
+
** A '''latch''' is inferred 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 maximum...
+
** ~4 levels should be the maximum...
-
Too illustrate some of the points given above, look at this deseptively simple process:
+
To illustrate some of the points given above, look at this deceptively simple process:
  '''process'''(all) '''is'''
  '''process'''(all) '''is'''
Line 120: Line 120:
     '''end process''';
     '''end process''';
-
Although this process may at first glance seem fine, it acutally infers some latching behaviour. From analyzing the process we get this state table:
+
Although this process may at first glance seem fine, it actually infers some latching behavior. From analyzing the process we get this state table:
{| class="wikitable" style="margin:auto"
{| class="wikitable" style="margin:auto"
Line 135: Line 135:
|}
|}
-
From the table we can see that when "inp1" is 0, "b" is a latched signal. Reviewing the code with this information we shure enough see an assignment missing for "b" when "inp1" is zero. Furthermore we also see that "a" only depends in "inp1", while "b" is dependent on both, making us able to simplify the code. Although this code simplification might seem to be a digression, it actually makes identifying the issue much easier:
+
From the table, we can see that when "inp1" is 0, "b" is a latched signal. Reviewing the code with this information we sure enough see an assignment missing for "b" when "inp1" is zero. Furthermore, we also see that "a" only depends on "inp1", while "b" is dependent on both, making us able to simplify the code. Although this code simplification might seem to be a digression, it actually makes identifying the issue much easier:
  '''process'''(all) '''is'''
  '''process'''(all) '''is'''
Line 148: Line 148:
     '''end process''';
     '''end process''';
-
From this simplified code we can much easier see that "b" is missing an assignment whereas "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.
+
From this simplified code we can much easier see that "b" is missing an assignment whereas "a" is fully covered. In addition to showing the importance of '''always specifying all output for all conditions of inputs''' it also shows the value of '''lowering the depth of if statements''' when possible.

Revision as of 08:19, 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 inferred 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 the maximum...


To illustrate some of the points given above, look at this deceptively simple process:

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;

Although this process may at first glance seem fine, it actually infers some latching behavior. From analyzing the process we get 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 0, "b" is a latched signal. Reviewing the code with this information we sure enough see an assignment missing for "b" when "inp1" is zero. Furthermore, we also see that "a" only depends on "inp1", while "b" is dependent on both, making us able to simplify the code. Although this code simplification 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 whereas "a" is fully covered. In addition to showing the importance of always specifying all output for all conditions of inputs it also shows 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