2
\$\begingroup\$

Below is a 4 tap filter. That means the order of the filter is 4 and so it has 4 coefficients. the input is signed type of 8 bits wide. The output is also of signed type with 16 bits width. The design contains two files. One is the main file with all multiplications and adders defined in it, and another for defining the D flip flop operation.

The main file is given below:

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

entity fir_4tap is
port(   Clk : in std_logic; --clock signal
        Xin : in signed(7 downto 0); --input signal
        Yout : out signed(15 downto 0)  --filter output
        );
end fir_4tap;

architecture Behavioral of fir_4tap is

component DFF is 
   port(
      Q : out signed(15 downto 0);      --output connected to the adder
      Clk :in std_logic;      -- Clock input
      D :in  signed(15 downto 0)      -- Data input from the MCM block.
   );
end component;  

signal H0,H1,H2,H3 : signed(7 downto 0) := (others => '0');
signal MCM0,MCM1,MCM2,MCM3,add_out1,add_out2,add_out3 : signed(15 downto 0) := (others => '0');
signal Q1,Q2,Q3 : signed(15 downto 0) := (others => '0');

begin

--filter coefficient initializations.
--H = [-2 -1 3 4].
H0 <= to_signed(-2,8);
H1 <= to_signed(-1,8);
H2 <= to_signed(3,8);
H3 <= to_signed(4,8);

--Multiple constant multiplications.
MCM3 <= H3*Xin;
MCM2 <= H2*Xin;
MCM1 <= H1*Xin;
MCM0 <= H0*Xin;

--adders
add_out1 <= Q1 + MCM2;
add_out2 <= Q2 + MCM1;
add_out3 <= Q3 + MCM0;

--flipflops(for introducing a delay).
dff1 : DFF port map(Q1,Clk,MCM3);
dff2 : DFF port map(Q2,Clk,add_out1);
dff3 : DFF port map(Q3,Clk,add_out2);

--an output produced at every positive edge of clock cycle.
process(Clk)
begin
    if(rising_edge(Clk)) then
        Yout <= add_out3;
    end if;
end process;

end Behavioral;

VHDL code for the component DFF is given below:

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

entity DFF is 
   port(
      Q : out signed(15 downto 0);      --output connected to the adder
      Clk :in std_logic;      -- Clock input
      D :in  signed(15 downto 0)      -- Data input from the MCM block.
   );
end DFF;

architecture Behavioral of DFF is 

signal qt : signed(15 downto 0) := (others => '0');

begin 

Q <= qt;

process(Clk) 
begin 
  if ( rising_edge(Clk) ) then 
    qt <= D;
  end if;       
end process; 

end Behavioral;

I have written a small test bench code for testing the design. It contains 8 test inputs which are serially applied to the filter module. See below:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY tb IS
END tb;

ARCHITECTURE behavior OF tb IS 

   signal Clk : std_logic := '0';
   signal Xin : signed(7 downto 0) := (others => '0');
   signal Yout : signed(15 downto 0) := (others => '0');
   constant Clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: entity work.fir_4tap PORT MAP (
          Clk => Clk,
          Xin => Xin,
          Yout => Yout
        );

   -- Clock process definitions
   Clk_process :process
   begin
        Clk <= '0';
        wait for Clk_period/2;
        Clk <= '1';
        wait for Clk_period/2;
   end process;

   -- Stimulus process
   stim_proc: process
   begin        
      wait for Clk_period*2;
        Xin <= to_signed(-3,8); wait for clk_period*1;
        Xin <= to_signed(1,8); wait for clk_period*1;
        Xin <= to_signed(0,8); wait for clk_period*1;
        Xin <= to_signed(-2,8); wait for clk_period*1;
        Xin <= to_signed(-1,8); wait for clk_period*1;
        Xin <= to_signed(4,8); wait for clk_period*1;
        Xin <= to_signed(-5,8); wait for clk_period*1;
        Xin <= to_signed(6,8); wait for clk_period*1;
        Xin <= to_signed(0,8);

      wait;
   end process;

END;

The main question is the simulation result is coming good and whether this code will work on fpga virtex 4 device.I am using xilinx software and this software has ip core generators but i am not using them because i want to get good coding practice.

\$\endgroup\$
  • 1
    \$\begingroup\$ Hi kannan, welcome to StackExchange. Your question has layout problems that make it very hard to understand your problem. Please make your question readable using electronics.stackexchange.com/editing-help \$\endgroup\$ – user17592 Feb 2 '13 at 12:49
5
\$\begingroup\$

Add to your testbench a monitor process that watches the output and compares it to the expected value, allowing for the delays through the filter.

Your VHDL style is better than some - even better than some textbook examples I have seen, but still: a few comments :

  1. Using named rather than positional assignment for the "dff" component instantiations would save possible confusion between inputs and outputs for anyone trying to follow the pipeline. This doesn't really matter here, because :

  2. you can eliminate the dffs altogether; replace them with lines of the form Q1 <= MCM3; in the same clocked process as Yout <= add_out3; that greatly simplifies the whole filter.

  3. You can reduce number of conversion functions by making H0..3 integer types; and if they are constant, make them constants! As multiplication between signed and integer is defined,no other changes are required.

    type coefficient is new integer range -128 .. 127;
    constant H0 : coefficient := -2;

  4. Lose the redundant parentheses in if ( rising_edge(Clk) ) then - this isn't C!

  5. The DRY principle applies in VHDL too... there are several ways to apply it to the testbench : my choice would be a local procedure.


   stim_proc: process

      procedure Input(D : in integer range -128 .. 127) is
      begin
         Xin <= to_signed(D,8); 
         wait for clk_period*1;
      end Input;

   begin        
      wait for Clk_period*2;
        Input(-3);
        Input( 1);
\$\endgroup\$
2
\$\begingroup\$

The Task

The task at hand is to verify that the behavior of your device - the fir_4tap entity - matches a certain specification. I'll just assume then that your filter should match the following equation:

$$ y = \sum_{n=0}^{3} b_n \cdot x_n $$

We need a reference

The first thing we need is a reference model which we can compare the RTL design to. So let's started with a VHDL function that matches the behavior of the equation:

function filter_kernel( x : real_vector(0 to 3); b : real_vector(0 to 3)) return real is
    variable y : real := 0.0;
begin
    for n in x'range loop
        y:= y + b(n) * x(n);
    end loop;
    return y;
end function filter_kernel;

With our reference function in place we need to wrap it in a clocked process so we can compare it to the RTL design:

    reference_model : process is
       variable x : real_vector(0 to 3) := (others => 0.0);
       variable y_ref : real;
    begin
        wait until falling_edge(clk);
        y_ref := filter_kernel(x, b);
    end process reference_model;

Connect it all together

And last, we need to connect it to a stimulus source and compare the output to our RTL design. Let's call it our testbench:

entity testbench is
end entity testbench;

architecture bench of testbench is
    constant b : real_vector(0 to 3) := (1.0, 0.0, 0.0, 0.0);
    signal clk : std_ulogic := '0';
    signal x : signed(7 downto 0);
    signal y : signed(15 downto 0);
begin
    clk <= not clk after 10 ns;

    device_under_test : fir_4tap(clk => clk, Xin => x, Yout => y);

    reference_model : process is
       variable x : real_vector(0 to 3) := (others => 0.0);
       variable y_ref : real;
       variable RV : RandomPType; -- See http://www.osvvm.org for details.
    begin
        wait until falling_edge(clk);

        fir_filter : y_ref := filter_kernel(x, b);

        update_x : x := RV.RandReal & x(0 to 2);

        compare_outputs : assert y_ref = to_real(sfixed(y));
    end process reference_model;
end architecture bench;

Summary

And that's basically all there is to it:

  1. You need a reference. It could be a VHDL model as in this case, or it could be data from a file.
  2. You need a way to generate stimulus. This could also be random data like here, or a golden dataset.
  3. You need to compare your design with the reference model. Preferably this should be handled automatically by your test bench.

There's a lot more to learn about verification, but it should at least get you started. Next you would perhaps add more test cases:

  • Do you need to test more coefficient sets?
  • Do you care about saturation and overflow?
  • Does you test bench handle pipelining?
  • Perhaps you want to extract data for visualizing filter response?

As you see, there is a lot more work to be done. Good luck!

\$\endgroup\$
-2
\$\begingroup\$

Try it! It's honestly the answer. There is no technique to know if a code will work or not into a physical device, specially if we think about external constraints like PCB signal integrity, pin locations, available clocks and speeds, bandwidth, delay times, etc.

\$\endgroup\$
  • 2
    \$\begingroup\$ Try it in real hardware only comes after you think it should work in theory. Simulators are a much more efficient way to try that out than using a "burn-and-crash" process \$\endgroup\$ – Martin Thompson Feb 5 '13 at 14:39
  • \$\begingroup\$ I meant try it after simulation says it's worth it \$\endgroup\$ – Joan Feb 7 '13 at 23:09
  • \$\begingroup\$ OK, that's fair enough! Maybe you could add that to your answer... (then I can remove my downvote ;) \$\endgroup\$ – Martin Thompson Feb 8 '13 at 11:58
  • \$\begingroup\$ +1 After writing the code and simulating it the only thing left to do is try it! You don't have to try it in real hardware, often times just compiling it for an FPGA will tell you if your code is synthesizeable or not, or tell you how efficiently your code gets turned into hardware, or tell you if you have any hope of meeting timespecs. \$\endgroup\$ – user3624 Mar 19 '13 at 4:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.