VHDL coding tips and tricks: April 2012

Saturday, April 28, 2012

Not enough I/O pins in the FPGA board for your design?

Thanks to all the hardwork you have done, you successfully completed writing your first vhdl code. It did great in the functional simulation part. Now its time to test it on a FPGA board.

But looking at the board in hand, you realize that there are not enough input or output pins on the board to test the design. As you may have seen most of the FPGA boards have 8 switch inputs, 4 push buttons, 8 led's. There are other ways to increase I/O by using features like seven segment display, VGA monitor, RS232, DAC and ADC etc. But these features may make the design pretty complex and time consuming.

In such cases we can simply use the basic led's or switches in a multiplexed manner. In this article I have shown how to tackle such a problem in case you are out of input pins. But the same concept apply for output pins also.

Our design is named as my_design which has a 32 bit input and 8 bit output. Considering a typical FPGA board, we dont have 32 switches or push buttons. Suppose we have 8 switches. The idea is to apply the input in four stages of 8 bit each. This is how you do it.

1st stage : Set switches for 1st byte(LSB), press the push button.
2nd stage : change switches for 2nd byte and press the push button again.
3rd stage : change switches for 3rd byte, press the push button again.
4th stage : change switches for 4th byte(MSB) and press the push button again.

The code for my_design:

library IEEE;

entity my_design is
    Port ( input : in  STD_LOGIC_VECTOR (31 downto 0);
           output : out  STD_LOGIC_VECTOR (7 downto 0));
end my_design;

architecture Behavioral of my_design is


output <= input(31 downto 24) and input(23 downto 16)
        and input(15 downto 8) and input(7 downto 0);

end Behavioral;

  The code just does the AND operation between the 4 bytes of the 32 bit number entered.

Now the code for reducing the input numbers. I call this module as a wrapper module. This job of this module is to get the input in stages, concatenate it together and apply it to the instantiated my_design.

Code for wrapper module :

library IEEE;

entity wrapper is
    Port ( Clk : in  STD_LOGIC;
              push : in STD_LOGIC;
           input : in  STD_LOGIC_VECTOR (7 downto 0);
           output : out  STD_LOGIC_VECTOR (7 downto 0));
end wrapper;

architecture Behavioral of wrapper is

component my_design
port(   input : in  STD_LOGIC_VECTOR (31 downto 0);
      output : out  STD_LOGIC_VECTOR (7 downto 0));
end component;     

--state machine type
type stype is (idle,get_byte,delay);
signal s : stype := idle;
signal c1,c2 : integer := 0;
signal temp_reg : std_logic_vector(31 downto 0) := (others => '0');

uut : my_design port map
        (input => temp_reg, --concatenated signal
        output => output    );

    if(rising_edge(clk)) then
        case s is
            when idle =>
                if(push = '1') then
                    s <= get_byte;
                    c1 <= c1+1;
                end if;
            when get_byte =>
                temp_reg( (8*c1-1) downto (8*(c1-1)) ) <= input;
                s <= delay;
            when delay => --delay for a time gap.
            --this delay is required to avoid the same byte getting
            --registered into temp_reg for a single push button click.
                c2 <= c2+ 1;
                if(c2=25000000) then --for a 50 mhz clock, this generates a 0.5 sec delay.
                    c2 <= 0;
                    s <= idle;
                    if(c1=4) then
                        c1 <= 0;
                    end if;
                end if;
        end case;
    end if;
end process;

end Behavioral;

I am using a state machine in the code, to get this done. The state machine has 3 stages.
1)idle - here system waits for a push button click.
2)get_byte - the system gets the switch inputs and stores in the temp_reg.
3)delay - system waits for a particular time(here 0.5 sec) doing nothing. This is to avoid duplicate registering of the same input.

A testbench was created for testing the design. The simulation waveforms are given below for your better understanding.

Note :- In a similar way you can also use multiplexed outputs.

Sunday, April 22, 2012

Tips for an Error-free Functional Simulation

Getting a VHDL code to work in the functional simulation is not always an easy task.This article will cover some tips to quickly point out the errors in the code and make your life easier.

  1. Create a proper sensitivity list. Some times you may have to add other control signals too(other than clock) into you sensitivity list to get is working.
  2. Initialize the signals and variables correctly. If they are not initialized(normally they are set to '0'), then these signals will appear as "U" in the simulation waveform.
  3. If you see "X" in the waveform then that indicates concurrent writing to the same signal. A simple re-arrangement of the signal inside the process will normally take out this bug.
  4. In case you have arrays in the design make sure to check for out of bound error. This happens when you read or write a different index than the one available within the range of array.
  5. If elsif's are error prone. Always try to consider all the conditions of If elsif. If a particular condition is not considered then the value will remain unchanged. If you dont want this to happen then make sure you reset the signal, using an else condition.
  6. Within a process, signal assignments can be written in any order. They will get executed concurrently. But for variables, the order matters. line 1 is executed first, line 2 second and so on...
  7. One way to debug the code is to force one or more signals as constants and test the design. This will help you in localizing the error.
  8. Writing a location in RAM requires a small time delay. Account for this, while reading and writing from the same location in the same clock cycle. The read data will be the one written in the last clock cycle.
  9. Try synthesising the design. The synthesiser tool may give out some warnings or errors which will point you in the correct direction to solve the error in the functional simulation.
  10. When using components in the design, use name instantiation, so that you don't accidentally assign wrong signals to the component ports.