There are many coding styles in VHDL where the code which work well in the simulation will not work on the actual fpga. In this article I am going to talk about one such RTL coding style.
As you know VHDL programmers normally use functions to represent combinational logic. But using functions every where without much thinking may result in dangerous bugs in the code. One perfect example is a function which replaces the code for a latch.
See the below code, which doesn't use a function:
As you know VHDL programmers normally use functions to represent combinational logic. But using functions every where without much thinking may result in dangerous bugs in the code. One perfect example is a function which replaces the code for a latch.
See the below code, which doesn't use a function:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
port( en1,a,Clk : in std_logic;
output : out std_logic );
end test;
architecture BEHAVIORAL of test is
begin
process(Clk)
begin
if ( rising_edge(Clk) ) then
if(en1 = '1') then
output <= a;
end if;
end if;
end process;
end BEHAVIORAL;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
port( en1,a,Clk : in std_logic;
output : out std_logic );
end test;
architecture BEHAVIORAL of test is
begin
process(Clk)
begin
if ( rising_edge(Clk) ) then
if(en1 = '1') then
output <= a;
end if;
end if;
end process;
end BEHAVIORAL;
In the above code, whenever signal en1 goes high, at the positive edge of Clk we assign a to output port. We don't specify what will happen when the value of en1 is '0'. So basically the code results in a latch.
When synthesised, XST(Xilinx synthesis tool) uses a FDE flip flop for implementing the code. If you check the link pointed by FDE, you can see the truth table of FDE flip flop. As expected, it works like a latch. Whenever CE is '0' the flip flop retains the previous output.
Now I am going re-write the above code using functions. I will replace some part of the combinational logic inside the process with a function. See the code below:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
port( en1,a,Clk : in std_logic;
output : out std_logic );
end test;
architecture BEHAVIORAL of test is
function latch (en1,a : std_logic ) return std_logic is
variable output : std_logic := '0';
begin
if(en1 = '1') then
output := a;
end if;
return output;
end latch;
begin
process(Clk)
begin
if ( rising_edge(Clk) ) then
output <= latch(en1,a);
end if;
end process;
end BEHAVIORAL;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
port( en1,a,Clk : in std_logic;
output : out std_logic );
end test;
architecture BEHAVIORAL of test is
function latch (en1,a : std_logic ) return std_logic is
variable output : std_logic := '0';
begin
if(en1 = '1') then
output := a;
end if;
return output;
end latch;
begin
process(Clk)
begin
if ( rising_edge(Clk) ) then
output <= latch(en1,a);
end if;
end process;
end BEHAVIORAL;
At the first look the above code is same as the first code. To confirm both has the same functionality, I simulated them. The outputs are matching. So they indeed work the same way in the simulation. But wait a minute. How about the synthesis results?
I ran XST for the above code and checked the technology viewer to see the synthesised circuit. What I saw was, instead of a FDE flipflop, XST used a FDR flipflop this time. Go to the link pointed by FDR and check the truth table given there. You can see that its not a latch. When R is '1' the flipflop output is reset to '0' rather than maintaining the previous value. So both the codes are going to work differently on board.
How did this happen? This is because all functions are synthesised into pure combinational logic by the synthesis tool. This means that you cannot go on using functions every where you want without proper brain storming. If you do, then the pre and post synthesis results may vary.
Note:- Both the codes where simulated and synthesized using Xilinx Webpack 12.1. The results may a vary a little depending on the tool you are using.
It happens because of this line:
ReplyDeletevariable output : std_logic := '0';
You've initialised the variable to '0' so it can;t latch. To build a functional latch, you'd have to pass the current output into the function and use it to initialise the output variable. But I don't see why that's so surprising to be honest?
@martin : Thanks for the comment.
ReplyDeleteWhen you remove the initialization of the variable, inside the function the XST doesnt use en1 input at all. It just manages the implementation with a simple FD flipflop.
what I wanted to say was that just replacing the combinational logic with a function may not work in certain cases.
And as you said you have to feedback the output if you want to synthesis the latch, which was not an obvious fact to beginners in vhdl.
Here is the code as per your suggestion for creating the latch using the function:
architecture BEHAVIORAL of test is
function latch (en1,a,o : std_logic ) return std_logic is
variable output : std_logic := o;
begin
if(en1 = '1') then
output := a;
end if;
return output;
end latch;
signal o : std_logic;
begin
output <= o;
process(Clk)
begin
if ( rising_edge(Clk) ) then
o <= latch(en1,a,o);
end if;
end process;
end BEHAVIORAL;