Developing design: moving average filter. Part 2 – filter.

I created for filter basic and simple requirements:

  • average of 4 samples
  • signal enable:
    • ‘1’ – filter ON
    • ‘0’ – filter OFF, zeros at the output
  • synchronous reset
  • don’t care about frequency, resources etc.
  • output value updated in every cycle

Below you can find the code of the filter:

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

entity AvgFilter is
   generic (
      G_DATA_W    :integer := 16;
      G_FIL_L     :integer :=  4
   );
   port (
      clk         :in std_logic;
      rst         :in std_logic;
      en          :in std_logic;
      iv_data     :in std_logic_vector(G_DATA_W-1 downto 0);
      ov_avg      :out std_logic_vector(G_DATA_W-1 downto 0)
   );
end entity;

architecture AvgFilter_rtl of AvgFilter is

   -- calculate number of bits needed to extend sum vector
   function sumlog2(m :positive) return natural is
   begin
      for index in 1 to 30 loop
         if (m <= 2**index) then
            return(index);
         end if;
      end loop;
      return(31);
   end function;
   
   signal en_reg  :std_logic;
   
   -- array for storing samples
   type t_arr_FilL_x_data is array (G_FIL_L-1 downto 0) of unsigned(G_DATA_W-1 downto 0);
   signal a_samples   :t_arr_FilL_x_data;

begin
reg: process(clk)

   -- to add G_FIL_L values is needed sumlog2(G_FIL_L) more bits for result
   variable v_sum     :unsigned(G_DATA_W+sumlog2(G_FIL_L)-1 downto 0);
      
begin
   if rising_edge(clk) then
      if rst = '1' then
         en_reg    <= '0';
         a_samples <= (others => (others => '0'));
         v_sum     := (others => '0');
         ov_avg    <= (others => '0');
      else
         en_reg   <= en;

         a_samples(0) <= unsigned(iv_data);
         for i in 1 to G_FIL_L-1 loop
            a_samples(i) <= a_samples(i-1);
         end loop;   

         v_sum := (others => '0');
         if en_reg = '1' then
            for i in 0 to G_FIL_L-1 loop
               v_sum := v_sum + resize(a_samples(i), v_sum'length);
            end loop;
         end if;

         ov_avg <= std_logic_vector(v_sum(G_DATA_W+sumlog2(G_FIL_L)-1 downto sumlog2(G_FIL_L))); -- divide by sumlog2(G_FIL_L)
      end if;
   end if;
end process;
end architecture;

Though the code should be easy to understand, I will describe it a little bit:

  • a_samples – array of registers to store filter samples
  • v_sum – variable storing sum of samples. Comparing to input width, is extended by two bits, because that variable is prepared to catch sum of four samples
  • ov_avg – output register. Two LSB bits of sum are omitted. That operation is equal to dividing by 4, to get average value.

*** *** ***

All source codes used in that post you can find on gitlab.com.

*** *** ***

Leave a Reply

Your email address will not be published. Required fields are marked *