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.