VHDL coding tips and tricks: February 2011

Thursday, February 17, 2011

File reading and writing in VHDL - Part 2

I have published a post on file reading and writing using VHDL before. You can access it here. As you know file manipulation will help you to verify your design more effectively at the debugging stage of your design.

For file operation we use the library named textio in the STD directory. This library contains the in built functions for reading and writing files.

Reading Files in VHDL:

The below code reads a set of binary numbers from the file named read.txt and put them into a 4 bit std_logic_vector signal. The text file used with the code can be downloaded from here.

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
use STD.textio.all; --Dont forget to include this library for file operations.

ENTITY read_file IS
END read_file;

ARCHITECTURE beha OF read_file IS

    signal  bin_value : std_logic_vector(3 downto 0):="0000";
   
BEGIN
   
   --Read process
    process
      file file_pointer : text;
        variable line_content : string(1 to 4);
      variable line_num : line;
        variable j : integer := 0;
        variable char : character:='0';
   begin
        --Open the file read.txt from the specified location for reading(READ_MODE).
      file_open(file_pointer,"C:\read.txt",READ_MODE);   
      while not endfile(file_pointer) loop --till the end of file is reached continue.
      readline (file_pointer,line_num);  --Read the whole line from the file
        --Read the contents of the line from  the file into a variable.
      READ (line_num,line_content);
        --For each character in the line convert it to binary value.
        --And then store it in a signal named 'bin_value'.
        for j in 1 to 4 loop       
            char := line_content(j);
            if(char = '0') then
                bin_value(4-j) <= '0';
            else
                bin_value(4-j) <= '1';
            end if;
        end loop;  
        wait for 10 ns; --after reading each line wait for 10ns.
      end loop;
      file_close(file_pointer);  --after reading all the lines close the file. 
        wait;
    end process;

end beha;

The above VHDL code can also be downloaded from here. I have commented the codes so that you can understand the flow of the code. The values read from the file are represented by a signal named bin_value. The simulation waveform is given below:

The main points to be noted are:
  • Declare file pointers and other variables required as given in the above code.
  • file_open() to open the file. Change the path of the file depending on where your file is stored.
  • Use read and readline functions.
  • Finally after everything is over, close the file using file_close.


Writing Files in VHDL:

  The following code is used for writing a file. Remember that these codes contain just examples and depending on what you have to write to the file the code should be changed. The code writes binary numbers from 0000 to 1111 to a file named write.txt


LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_arith.ALL;
use STD.textio.all;

ENTITY write_file IS
END write_file;

ARCHITECTURE beha OF write_file IS
   
BEGIN
   
   --Write process
    process
      file file_pointer : text;
        variable line_content : string(1 to 4);
        variable bin_value : std_logic_vector(3 downto 0);
      variable line_num : line;
        variable i,j : integer := 0;
        variable char : character:='0';
   begin
        --Open the file write.txt from the specified location for writing(WRITE_MODE).
      file_open(file_pointer,"C:\write.txt",WRITE_MODE);     
        --We want to store binary values from 0000 to 1111 in the file.
      for i in 0 to 15 loop
        bin_value := conv_std_logic_vector(i,4);
        --convert each bit value to character for writing to file.
        for j in 0 to 3 loop
            if(bin_value(j) = '0') then
                line_content(4-j) := '0';
            else
                line_content(4-j) := '1';
            end if;
        end loop;
        write(line_num,line_content); --write the line.
      writeline (file_pointer,line_num); --write the contents into the file.
        wait for 10 ns;  --wait for 10ns after writing the current line.
      end loop;
      file_close(file_pointer); --Close the file after writing.
        wait;
    end process;

end beha;

The above code can also be downloaded from here. Any file writing code should have the following structure:

  • Declare file pointers and other variables required as given in the above code.
  • file_open() to open the file. Change the path of the file depending on where your file is stored.
  • Use write and writeline functions.
  • Finally after everything is over, close the file using file_close.
Note:- The codes are tested successfully using Xilinx Webpack 12.1. Note that file reading is a part of testbench design. In actual FPGA you cannot read or write files. But for functional verification of your design this is a must know.

Wednesday, February 16, 2011

Be careful when using functions in your code.

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:


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;

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;

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.

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. 

Saturday, February 12, 2011

Difference between C and VHDL

It is normally said that once you learn one programming language it is pretty easy to learn the other programming languages. This is because the concepts are almost same in most of the programming languages with some only syntax differences.

But if you ask a hardware engineer, he may have a totally different opinion. If you dont stop thinking from a C programmer's perspective, then life as VHDL programmer will drive you nuts. Because both the languages have many differences between them. Both are different from the basic level itself, though they seem to have many similarities.

So let me compile some of the basic differences between C programming and VHDL programming.
  1. C is a middle level language. I mean its a mix of a high level language and an assembly language.
    VHDL is a hardware description language(HDL) . It is used for implementing the hardware circuit.
  2. C can only handle sequential instructions.
    VHDL allows both sequential and concurrent executions.  
  3. A C program can be successfully written with pure logical or algorithmic thinking.
    But a successful VHDL programmer needs thorough working knowledge of the hardware circuits. He should be able to predict how a given code will be implemented in hardware.
  4. Normally we don't care about resource usage in C. This is because a C program is usually ran on a computer which uses a powerful processor with high speed. We also don't care about the memory usage.
    But when it comes to VHDL a slightly complicated code can make you bent on your  knees. The memory and other logic elements are limited in a FPGA(where you normally put the VHDL code in). This is why it is very difficult to implement image processing algorithms in VHDL than in C.
These are some of the main points. If you have anything to add, feel free to add them in the comment section.