VHDL coding tips and tricks: VHDL: What is a Testbench and how do I use it?

Wednesday, March 3, 2010

VHDL: What is a Testbench and how do I use it?

Once you finish writing code for your design, the next step would be to test it. One method of testing your design is by writing a testbench code. A testbench is used for testing the design and making sure it works as per your specified functionalities. 

Using a testbench, we can pass inputs of our choice to the design to be tested. The outputs coming out of our design can be viewed on a simulation waveform or text file or even on console screen.

Lets see how this works with an example.

VHDL code for a 4 bit counter with reset:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity adder is
port (clk : in std_logic;
      reset :in std_logic;
      count : out unsigned(3 downto 0)  --output of the design. 4 bit count value.
     );
end adder;

architecture behavioral of adder is

--initializing the count to zero.
signal c : unsigned(3 downto 0) :=(others => '0');  

begin

count <= c;

process(clk,reset)
begin
    if(reset = '1') then    --active high reset for the counter.
        c <= (others => '0');
    elsif(rising_edge(clk)) then
    -- when count reaches its maximum(that is 15) reset it to 0
        if(c = 15) then
            c <= (others => '0');
        else    
            c <= c + 1;  --increment count at every positive edge of clk.
        end if;
    end if; 
end process;

end behavioral;

The design above is a 4 bit UP counter with active high asynchronous reset. The design has 2 inputs - a clock and reset and one output which is the count value.

In order to write a good testbench we need to first understand the design. At least we should know, what kind of errors might be there and what kind of input combinations can bring out these errors. The design we have here is a very simple one, so it shouldn't be too complicated.

Let me show you a sample testbench for this design.

VHDL testbench for 4 bit UP counter:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

-- entity declaration for your testbench. 
--Notice that the entity port list is empty here.
entity tb_adder is
end tb_adder;

architecture behavior of tb_adder is

-- component declaration for the unit under test (uut)
component adder is
port (clk : in std_logic;
      reset :in std_logic;
      count : out unsigned(3 downto 0)
     );
end component;

--declare inputs and initialize them to zero.
signal clk : std_logic := '0';
signal reset : std_logic := '0';

--declare outputs
signal count : unsigned(3 downto 0);

-- define the period of clock here.
-- It's recommended to use CAPITAL letters to define constants.
constant CLK_PERIOD : time := 10 ns;

begin

    -- instantiate the unit under test (uut)
   uut : adder port map (
            Clk => Clk,
            reset => reset, 
            count => count
        );      

   -- Clock process definitions( clock with 50% duty cycle is generated here.
   Clk_process :process
   begin
        Clk <= '0';
        wait for CLK_PERIOD/2;  --for half of clock period clk stays at '0'.
        Clk <= '1';
        wait for CLK_PERIOD/2;  --for next half of clock period clk stays at '1'.
   end process;
    
   -- Stimulus process, Apply inputs here.
  stim_proc: process
   begin        
        wait for CLK_PERIOD*10; --wait for 10 clock cycles.
        reset <='1';                    --then apply reset for 2 clock cycles.
        wait for CLK_PERIOD*2;
        reset <='0';                    --then pull down reset for 20 clock cycles.
        wait for CLK_PERIOD*20;
        reset <= '1';               --then apply reset for one clock cycle.
        wait for CLK_PERIOD;
        reset <= '0';               --pull down reset and let the counter run.
        wait;
  end process;

end;

The code is self explanatory with plenty of comments.

When these codes were simulated in Xilinx ISE 14.6, I got the following waveform. 

Simulation waveform:


I would summarize few points here,
  1. The entity port list of a testbench is always empty. 
  2. All the designs which you want to test, declare them as components in the testbench code. 
  3. The clock process part in the code, is only required for testing designs with a clock( sequential designs).
  4. Declare all the inputs and outputs in the design to be tested. Make sure you initialize all the inputs to zero ,in the system.
  5. Instantiate the design by any one of the Instantiation methods described available here. Note that this instantiation is written after the begin statement.
This post is for beginners to get an overview on writing testbenches. As you discover more in VHDL, you will need and learn to write more complicated testbenches. There are lot of example designs in this blog, and almost every design comes with a testbench.

30 comments:

  1. Inspecting the waveforms works for simple testbenchs, but it's easier to use VHDL asserts;
    You can put them at any given point in the test bench, to check whether the received values are as expected. This way you don't need to inspect waveforms (except when inspecting, when asserts tell you something is wrong)

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. In ModelSim, at least for me the "time := 1 ns;" doesn't work, but, with 10 ns is just fine.
    Which software did you use?

    ReplyDelete
  4. @Raphael : thanks for pointing out the mistake.I used xilinx ISE version 10.1.
    VHDL asserts are used to write intelligent testbenches. For these testbenches you need not see the waveform for seeing whether the code is working or not.They are used to alert the user of some condition inside the model. When the expression in the ASSERT statement evaluates to FALSE, the associated text message is displayed on the simulator console.Assertion statements can be used for complex designs where inputs and outputs have complex relation.
    I will put a post on assertion statements in some time, for now see this link(under the E4 heading):
    http://www.vhdl.org/comp.lang.vhdl/html3/gloss_example.html

    ReplyDelete
  5. hello can i get a link for the modified random number generator in open cores website.

    thanks in advance

    ReplyDelete
  6. @Divya : The link is available in this post.
    http://vhdlguru.blogspot.com/2010/08/random-number-generator-in-vhdlcont.html

    Just go to the project page and click on the download option.

    ReplyDelete
  7. I have been using the VHDL package linked below:
    http://opencores.org/project,vhld_tb

    For 14+ years ...

    Sckoarn

    ReplyDelete
  8. Hi

    I have a general question about testing of VHDL-code...

    Can anyone help me out on estimating the effort needed to specify and execute (formal) tests for VHDL?
    I'm quite experienced in (software) testing & estimations but I do not have any metric's or key figures suitable for VHDL (like the number of I/O's, lines of code, state machines etc.)

    A response in email will be appreciated: 4x4andmore @ gmail dot com

    Regards, Patrick

    ReplyDelete
  9. How would you change this to be a 32 bit counter. I'm a beginner. Thanks.

    ReplyDelete
  10. hey can i get the code of a function that converts Boolean into integer .thanks in advance

    ReplyDelete
  11. @anubhav : A boolean has only two values TRUE and FALSE.So why do you want to convert it to a integer. What is your actual purpose?

    ReplyDelete
  12. hello can i get a vhdl code for 32 bit array???i am new in this field.

    ReplyDelete
  13. hi...can i get the vhdl code for frequency multiplier without using unisim library..

    ReplyDelete
  14. hi... can u get me Viterbi decoder with register exchange and traceback method VHDL code? plzzzz
    replay me to tabu.unnisa@gmail.com

    ReplyDelete
  15. What is resettable counter and what is the main objective of it? please answer Thanks.

    ReplyDelete
  16. can you give some examples for synthesizable and non-synthesizable code in vhdl..

    ReplyDelete
  17. In your code you should use variable instead of a signal , multiple assignments inside a process are alright but it will be assigned the value only after exitin from the process .

    ReplyDelete
  18. thanks a lot,it was very helpful to do the sample example and know the model of executing it.

    ReplyDelete
  19. can you give me an example of vhdl code subtractor of integer

    ReplyDelete
  20. can you give me an example of vhdl xilinx subtractor in integer

    ReplyDelete
  21. can anyone please help in vhdl code for array multilplier and test bench also. please mail me to alisasky22@gmail.com

    ReplyDelete
  22. can you give me an example of tenst bench for this code?
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    use ieee.math_real.all;


    entity PID_Regler is
    generic(
    data_width: integer := 14;
    intern_data_width: integer := 18
    );

    port(
    clk_i : in std_logic;
    rst_i : in std_logic;
    k_p : in std_logic_vector(intern_data_width-1 downto 0);
    k_i : in std_logic_vector(intern_data_width-1 downto 0);
    k_d : in std_logic_vector(intern_data_width-1 downto 0);
    data_w_i : in std_logic_vector(data_width-1 downto 0);
    data_x_i : in std_logic_vector(data_width-1 downto 0);
    data_y_o : out std_logic_vector(data_width-1 downto 0)
    );
    end PID_Regler;

    architecture Behavioral of PID_Regler is

    signal w : std_logic_vector(data_width-1 downto 0);
    signal x : std_logic_vector(data_width-1 downto 0);
    signal e : std_logic_vector(data_width-1 downto 0);

    signal y : std_logic_vector(data_width-1 downto 0);
    signal yp: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
    signal yi: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
    signal yd: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
    signal ealt : std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
    signal yi_alt : std_logic_vector(intern_data_width-1 downto 0) := (others => '0');

    begin
    Regler: process(clk_i)
    begin
    --Mappen der Eingeänge
    w <= data_w_i;
    x <= data_x_i;

    --Regeldifferenz
    e <= std_logic_vector(signed(w) - signed(x));


    if rising_edge(clk_i) then
    if rst_i = '1' then
    yp <= (others => '0');
    yi <= (others => '0');
    yd <= (others => '0');
    ealt <= (others => '0');
    y <= (others => '0');
    else
    --e <= std_logic_vector(signed(w) - signed(x));
    yp <= std_logic_vector(signed(k_p)*signed(e));
    yi <= std_logic_vector(signed(yi_alt)+(signed(k_i)*signed(e)/10)); --yi_alt=yi
    yd <= std_logic_vector(signed(k_d)*((signed(e)-signed(ealt))*10)); -- ealt=e

    y <= std_logic_vector(signed(yp)+signed(yi)+signed(yd));
    --y <= resize((yp + yi + yd));
    ealt <= e;
    yi_alt <= yi;
    end if;
    end if;
    end process Regler;
    data_y_o <= y;
    end Behavioral;

    ReplyDelete
  23. how can i write a sawtooh code for Fs = 18khz in vhdl?

    ReplyDelete
  24. how to Instantiate the Unit Under Test (UUT) for an output which is an array of 0 to 63 values ??

    ReplyDelete
  25. Hi, I have written a vhdl code for decoder and it is being synthesized well but couldnot simulate the test bench, here I am attaching my code for your reference
    decoder.vhd

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;



    entity decoder is
    Port ( clk : in std_logic;
    A : in STD_LOGIC_VECTOR (1 downto 0);-- 2 bit input
    X : out STD_LOGIC_VECTOR (3 downto 0);-- 4 bit input
    EN : in STD_LOGIC); --enable input
    end decoder;

    architecture Behavioral of decoder is
    begin
    process (clk,A,EN)
    begin
    if(clk'event and clk= '1') then
    X <= "1111";
    if (EN = '1') then
    case A is
    when "00" => X(0) <= '0';
    when "01" => X(1) <= '0';
    when "10" => X(2) <= '0';
    when "11" => X(3) <= '0';
    when others => X <= "1111";
    end case;
    end if;
    end if;
    end process;
    end Behavioral;

    decoder_tb.vhd


    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    entity decoder_tb is
    -- Port ( );
    end decoder_tb;
    architecture Behavioral of decoder_tb is
    component decoder is
    Port ( clk : in std_logic;
    A : in STD_LOGIC_VECTOR (1 downto 0);
    X : out STD_LOGIC_VECTOR (3 downto 0);
    EN : in STD_LOGIC);
    end component decoder;
    signal clk : std_logic;
    signal A : std_logic_vector (1 downto 0);
    signal X : std_logic_vector(3 downto 0);
    signal EN : std_logic:= 0;
    begin
    uut : decoder port map (
    clk => clk,
    A => A,
    X => X,
    EN => EN );
    clk_gen : process
    begin
    clk <= '0';
    wait for clock_period/2;
    clk <= '1';
    wait for clock_period/2;
    end process clk_gen;
    sdp : process
    begin
    wait for 20 ns;
    EN <= 1;
    wait for 20 ns;
    EN <= 0;
    wait for 20 ns;
    A <= "00";
    wait for 20 ns;
    A <= "01";
    wait for 20 ns;
    A <= "10";
    wait for 20 ns;
    A <= "11";
    end process sdp;
    end behavioral;


    What is wrong in the above code?

    ReplyDelete
    Replies
    1. Before begin I think you should write
      constant clk_period : time := 1 ns;

      Delete
  26. Hi I have written a vhdl code for decoder and i successfully synthesized it but couldnot simulate my test bench as it fails because of some errors. I am posting my decoder.vhd and decoder_tb.vhd here for your reference

    decoder.vhd

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;

    entity decoder is
    Port ( clk : in std_logic;
    A : in STD_LOGIC_VECTOR (1 downto 0);-- 2 bit input
    X : out STD_LOGIC_VECTOR (3 downto 0);-- 4 bit input
    EN : in STD_LOGIC); --enable input
    end decoder;

    architecture Behavioral of decoder is
    begin
    process (clk,A,EN)
    begin
    if(clk'event and clk= '1') then
    X <= "1111";
    if (EN = '1') then
    case A is
    when "00" => X(0) <= '0';
    when "01" => X(1) <= '0';
    when "10" => X(2) <= '0';
    when "11" => X(3) <= '0';
    when others => X <= "1111";
    end case;
    end if;
    end if;
    end process;
    end Behavioral;

    decoder_tb.vhd

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    entity decoder_tb is
    -- Port ( );
    end decoder_tb;
    architecture Behavioral of decoder_tb is
    component decoder is
    Port ( clk : in std_logic;
    A : in STD_LOGIC_VECTOR (1 downto 0);
    X : out STD_LOGIC_VECTOR (3 downto 0);
    EN : in STD_LOGIC);
    end component decoder;
    signal clk : std_logic;
    signal A : std_logic_vector (1 downto 0);
    signal X : std_logic_vector(3 downto 0);
    signal EN : std_logic:= 0;
    begin
    uut : decoder port map (
    clk => clk,
    A => A,
    X => X,
    EN => EN );
    clk_gen : process
    begin
    clk <= '0';
    wait for clock_period/2;
    clk <= '1';
    wait for clock_period/2;
    end process clk_gen;
    sdp : process
    begin
    wait for 20 ns;
    EN <= 1;
    wait for 20 ns;
    EN <= 0;
    wait for 20 ns;
    A <= "00";
    wait for 20 ns;
    A <= "01";
    wait for 20 ns;
    A <= "10";
    wait for 20 ns;
    A <= "11";
    end process sdp;
    end behavioral;

    Can you help me what is wrong in my test bench?


    ReplyDelete
  27. Hi,
    I was wondering about initializing the signals to a particular value during declaration. Would this bring any advantage to the testing?

    ReplyDelete
  28. In the counter code, it is not necessary to test when counter has reached 15

    ReplyDelete