VHDL coding tips and tricks: How to stop using "buffer" ports in VHDL?

Tuesday, February 15, 2011

How to stop using "buffer" ports in VHDL?

Buffer ports are used when a particular port need to be read and written. This mode is different from inout mode. The source of buffer port can only be internal. For example if you need a signal to be declared as output, but at the same time read it in the design, then declare it as buffer type.

But buffer types are not recommended by Xilinx and they say if it possible try to reduce the amount of buffer usage. According to Xilinx, buffers may give some problems during synthesis. If a signal is used internally and as an output port then in every level in your hierarchical design, it must be declared as a buffer. So let me show how to reduce the amount of buffer usage with an example.

The following code uses a buffer. I am not going through the functionality of the code since it is very simple.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity with_buffer is  
port(   A : in unsigned(3 downto 0);        
      B : in unsigned(3 downto 0);        
      Clk : in std_logic;        
      C : buffer unsigned(3 downto 0) );
end with_buffer;


architecture BEHAVIORAL of with_buffer is

begin

process(Clk)
begin    
    if ( rising_edge(Clk) ) then        
        C <= A + B + C;    
    end if;
end process;

end BEHAVIORAL;

As you can see the signal C is used repetitively in the addition, and also its an output of the module. So we declared it as a buffer. Another way to code this same functionality without a buffer is given below:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity without_buffer is
port(   A : in unsigned(3 downto 0);        
      B : in unsigned(3 downto 0);        
      Clk : in std_logic;        
      C : out unsigned(3 downto 0) );
end without_buffer;

architecture BEHAVIORAL of without_buffer is

--intermediate signal to avoid the use of buffer.s
signal C_dummy : unsigned(3 downto 0);

begin
C <= C_dummy;  --Assign the intermediate signal to output port.

process(Clk)
begin    
    if ( rising_edge(Clk) ) then      
        C_dummy <= A + B + C_dummy; --Use the intermediate signal in actual calculation.
   end if;
end process;

end BEHAVIORAL;

What I have done is, I used an intermediate or dummy signal inside the process statement. The value of C is read from this dummy signal named C_dummy. And outside the process we assign the value of C_dummy to the output port C. This is how we reduce the buffer usage in vhdl. Avoiding buffer usage is very useful particularly in case of hierarchical designs.

Note:- Both the codes were synthesised successfully using Xilinx Webpack 12.1. The results may or may not vary for Altera FPGA's. 

4 comments:

  1. I think there's a typo in the second code section:

    C_dummy <= A + B + C_dummy; --Use the intermediate signal in actual calculation.

    I think it should be C_dummy <= A + B + C, because you cannot use C_dummy as an operand, since it doesn't have any defined value after the sum is complete.

    ReplyDelete
  2. @CN : Its not a typo. The whole addition operation takes place at the rising edge of clock, so C_dummy is never undefined. The previous value of C_dummy is added with the current value of A and B to form the current value of C_dummy.
    If use C_dummy <= A + B + C_dummy;
    then we are reading the data from signal C(which is declared as an output port).So its not possible unless we declare it as buffer which makes the whole thing pointless.

    ReplyDelete
    Replies
    1. It /is/ incorrect (or incomplete at least), because C_dummy is never initialized, thus undefined at start of simulation. At the first rising clk edge, the addition will effectively read C_dummy <= A + B + "UUUU"; ...The result is "UUUU"...

      Delete
    2. Well, if we're going to come along years later and comment then I'll jump on the bandwagon. Initialization in VHDL is the left-most value if not explicitly specified. For std_logic, that is 'U'. In this case, C_dummy will get C_dummy'left. That's largest value in this case because of using "downto", so the init will be to "1111" = 15. I make no claims that this would necessarily behave as desired. It would probably make more sense to set it as := to_unsigned(0, 4);

      Delete