Synthesizable Procedures

VHDL offers a procedure language construct.  Like most tools, it has a handful of advantages and disadvantages.  Procedures have long been used in simulations to allow complex logic sequences to be represented as a single statement.  This allows the sim to run a procedure like “flush_buffers” to perform a sequence of operations.

Declaration

For synthesis, there are some differences.  Procedures lose the sequence-of-operations features.  This is because synthesis wants to infer combinatorial logic.  In effect, procedures become multi-output functions.  Below is an example of a synthesizeable procedure:

-- Simple Procedure
procedure data_send (
  constant TERM      : in    integer := 1420;
  signal   Data_Out  : out   std_logic_vector;
  signal   Valid_Out : out   std_logic;
  signal   Last_Out  : out   std_logic;
           Data_In   : in    std_logic_vector;
           Valid_In  : in    std_logic;
  signal   Cnt       : inout std_logic_vector
  ) is
begin
  Data_Out  <= Data_In;
  Valid_Out <= Valid_In;

  if Valid_In = '1' then
    Cnt <= std_logic_vector(unsigned(Cnt) + 1);
  end if;

  if unsigned(Cnt) = TERM then
    Last_Out <= Valid_In;
  else
    Last_Out <= '0';
  end if;
 
end procedure;

The above code can then be used inside of a clocked process as follows:

p_test_proc : process (Clk) is
begin
  if Clk'event and Clk = '1' then
    if Rst = '1' then  -- read "disadvantages"
      cnt <= (others => '0');
    else
      data_send (
        Data_Out  => Dout,
        Valid_Out => Valid,
        Last_Out  => Last,
        Data_In   => Din,
        Valid_In  => Wr,
        Cnt       => cnt
        );
    end if;
  end if;
end process;

Notice that the “ports” of the procedure can have a construct description associated with them.  This allows the connections to be to a signal.  The default is connections to variables.  This can be annoying because a signal cannot be connected to a variable connection.  This is because the procedure would be required to use a blocking assign on the signal, something VHDL does not allow.

The second point is that inout’s become much more common.  In this case the counters is updated by using the current value of itself.  This makes it easier to instantiate than if two ports, Cnt_In and Cnt_Out were declared.

Finally, “generic” equivalents can be passed by declaring a connection as a constant.  This allows for more reusable procedures.

Local Procedures

Procedures actually inherit the scope in which they are used.  Thus a procedure can access and assign variables that are not in the list of connections.  This severely limits where the procedure can be used, as a specifically name, correctly typed variable must exist in each scope where the procedure is used.  XST doesn’t allow signals to be assigned from a procedure unless they are declared as above.

Advantages

The biggest advantage to using procedures is exact reuse.  When the exact same code structure is used in several places, a procedure can be used as an alternative to duplicating code or making extra components.  This can be useful for state machines.  It also allows logic to updated in several places at the same time if a problem is found.

The other advantage is that the action’s intent can be more clear.   When a group of assignments has a specific meaning, the use of a procedure can quickly tell the reader what the code is intending to do.  Its still a good idea to use comments though.

Disadvantages

The biggest disadvantage is that small changes often require either updating the procedure, or writing a variant.  This can eliminate the advantages of a reusable procedure quickly.

Next, it leads to less logically-efficient code.  In the above process, the counter needs to be reset.  In many cases, the user might assume a multi-cycle reset, where valids/lasts will stop after a few cycles.  Likewise, it might be assumed that data is only valid when valid = ‘1’.  The above code makes both assumptions.  But the counter does need to be reset.  The above code will result in Rst being used as an input to the LUTs for valid/data/last, as the procedure is not run when Rst = ‘1’.  Small oversights like this can become common, resulting in extra resource utilization.

Finally, it is another layer of abstraction.  When debugging, its easy to overlook “tested” code.  Thus an unexpected nuance in a procedure might be hard to find.  Like functions, it can also make it difficult to determine the complexity of logic associated with a given signal.

Conclusions

It was actually a bit difficult to find any information on how to declare and use procedures for synthesis.  I haven’t used procedures on any appreciably large design yet, so I can’t comment on how effective they are, or how well they synthesize just yet.  The above example synthesized into what I had expected — including the extra reset logic I had left in for an example disadvantage.

This entry was posted in VHDL. Bookmark the permalink.

Comments are closed.