Best way for average out

Discussion in 'VHDL' started by Old Siam Sir, Jul 6, 2011.

  1. Old Siam Sir

    Old Siam Sir

    Joined:
    Jul 6, 2011
    Messages:
    2
    Hello everybody, I have to do a VHDL project and, inside this project, I have to insert a module for averaging out severale samples (std_logic_vector type).
    There's a way to have a simple divide for std_logic_vector? Using convert to integer and then dividing seems to be too "logical units-expansive", I don't know If you understood what I mean... :)

    I know I can make an arithmetic shift but i can use it only for powers of 2!

    There are simpler alogorithms?
    Old Siam Sir, Jul 6, 2011
    #1
    1. Advertising

  2. Old Siam Sir

    flymolo

    Joined:
    Jun 2, 2011
    Messages:
    10
    How many samples you want to average? Does the number of samples to average changes in runtime or do you want to pass it as generic? Is this going to be implemented in FPGA?
    In general i would use multiply->acccumulate->shift for an FPGA. You need to multiply it by some scaling factor then accumulate cut out the LSB's (or cut the LSB's before accumulate). Note that both accumulator and multiplier can be wider. Give some more spec. :)
    Last edited: Jul 6, 2011
    flymolo, Jul 6, 2011
    #2
    1. Advertising

  3. Old Siam Sir

    Old Siam Sir

    Joined:
    Jul 6, 2011
    Messages:
    2
    FPGA, samples 8 bit vectors, average should be done of 103 or 105 samples, 103 and 105 is
    "chosen" by a decider module

    Example: Samples are received one by one with f (known) frequency.
    I have to averege out 103 o 105 samples, depending by a flag imposed by the decider module

    E.G.: if flag is 1 i receive 103 samples and I averege out'em, after that, if is 0 I average out 105 and so on...

    Is this enough?
    Old Siam Sir, Jul 6, 2011
    #3
  4. Old Siam Sir

    flymolo

    Joined:
    Jun 2, 2011
    Messages:
    10
    Hey. I had some free time today and i came up with this:
    Code:
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.math_real.all;
    
    entity smp_avg is
    generic(
      IN_WIDTH    : natural := 8;       -- width of input - smp_in
      OUT_WIDTH   : natural := 8;       -- width of output - avg_out
      COEFF_EXT   : natural := 15;      -- internal precision
      AVG_NUM_1   : natural := 103;     -- number of samples to average 1
      AVG_NUM_2   : natural := 105;     -- number of samples to average 2 (must be larger than avg_num_1)
      ROUNDING    : boolean := true     -- output rounding to nearest / trunctation   
    );
    port(
      rst       : in  std_logic;                                -- system reset
      clk       : in  std_logic;                                -- system clock
      smp_in    : in  std_logic_vector(IN_WIDTH-1 downto 0);    -- sample input
      smp_nd    : in  std_logic;                                -- sample new data strobe
      avg_flag  : in  std_logic;                                -- selector: switches between number of samples ('1'=>AVG_NUM_1, '0'=>AVG_NUM_2)
      avg_out   : out std_logic_vector(OUT_WIDTH-1 downto 0);   -- averaging result
      avg_rdy   : out std_logic                                 -- average ready strobe
    );
    end smp_avg;
    
    architecture RTL of smp_avg is
    
      
      constant COEFF_WIDTH    : integer := COEFF_EXT - integer(floor(log2(real(AVG_NUM_2-1))));
      constant AVG_COEFF_1    : unsigned(COEFF_WIDTH-1 downto 0) := to_unsigned(integer(ceil(real((2**COEFF_EXT-1)/AVG_NUM_1))), COEFF_WIDTH);
      constant AVG_COEFF_2    : unsigned(COEFF_WIDTH-1 downto 0) := to_unsigned(integer(ceil(real((2**COEFF_EXT-1)/AVG_NUM_2))), COEFF_WIDTH);
      constant AVG_CNT_WIDTH  : integer := integer(ceil(log2(real(AVG_NUM_2-1))));
      
      signal  coeff     : unsigned(COEFF_WIDTH-1 downto 0);                                             -- coefficient demux output
      signal  mult      : unsigned(COEFF_WIDTH+IN_WIDTH-1 downto 0);                                    -- multiply result
      signal  acc       : unsigned(COEFF_WIDTH+IN_WIDTH+AVG_CNT_WIDTH-2 downto 0) := (others => '0');   -- sample accumulator
      signal  avg_cnt   : unsigned(AVG_CNT_WIDTH-1 downto 0) := to_unsigned(AVG_NUM_2, AVG_CNT_WIDTH);  -- sample counter
      signal  avg_out_s : unsigned(OUT_WIDTH-1 downto 0);                                               -- average output
      
      begin
    
        -- sample counter:
        sample_counter: process(clk)
        begin
          if clk = '1' and clk'event then
            if rst = '1' or avg_cnt = to_unsigned(0, AVG_CNT_WIDTH) then
              if avg_flag = '1' then
                avg_cnt <= to_unsigned(AVG_NUM_1, AVG_CNT_WIDTH);
              else
                avg_cnt <= to_unsigned(AVG_NUM_2, AVG_CNT_WIDTH);
              end if;
            elsif smp_nd = '1' then
              avg_cnt <= avg_cnt - 1;
            end if;
          end if;
        end process;
        
        -- coefficient mux
        coeff <= AVG_COEFF_1 when avg_flag = '1' else AVG_COEFF_2;
        
        -- multiplier
        mult <= unsigned(smp_in) * coeff;
        
        -- accumulator
        accumulator: process(clk)
        begin 
          if clk = '1' and clk'event then
            if rst = '1' or avg_cnt = to_unsigned(0, AVG_CNT_WIDTH) then
              acc <= (others => '0');
            elsif smp_nd = '1' then
              acc <= acc + resize(mult, acc'length);
            end if;
          end if;
        end process;
        
        -- output rounding
        process(acc)
        begin
          if ROUNDING = true and acc(acc'high-OUT_WIDTH) = '1' then
            avg_out_s <= acc(acc'high downto acc'high-OUT_WIDTH+1) + 1;
          else
            avg_out_s <= acc(acc'high downto acc'high-OUT_WIDTH+1);
          end if;
        end process;
        
        -- output register
        avg_out <= std_logic_vector(avg_out_s) when rising_edge(clk) and avg_cnt = to_unsigned(0, AVG_CNT_WIDTH);
        
        -- ready flag
        ready_register: process(clk)
        begin 
          if clk = '1' and clk'event then
            if avg_cnt = to_unsigned(0, AVG_CNT_WIDTH) then
              avg_rdy <= '1';
            else
              avg_rdy <= '0';
            end if;
          end if;
        end process;
          
    end RTL;
    It is not 100% tested, I tested it for default generic values (above). I made it parameterizable - thats why it may look little bit over-complicated. Anyways its far less logic hungry than division (30 Spartan-3 slices for whole module - with no embedded multipliers). You can play with generics to achieve precision/logic usage you are happy with. Usually you oversample to get more dynamic range at the cost of bandwidth. The module provides one output after each 103/105 samples are read. It's not moving average filter to be clear ;)
    flymolo, Jul 8, 2011
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steven

    AVERAGE SQL error... help please

    Steven, Oct 16, 2003, in forum: ASP .Net
    Replies:
    1
    Views:
    1,405
    Richard K Bethell
    Oct 16, 2003
  2. Meena Lalwani
    Replies:
    1
    Views:
    1,126
    Kevin Spencer
    Aug 31, 2005
  3. Bob
    Replies:
    12
    Views:
    1,095
    Greg N.
    Jan 13, 2006
  4. Cliff Wells

    Re: Please help - get average

    Cliff Wells, Oct 27, 2004, in forum: Python
    Replies:
    3
    Views:
    312
    Josiah Carlson
    Oct 27, 2004
  5. Frohnhofer, James

    RE: Please help - get average

    Frohnhofer, James, Oct 27, 2004, in forum: Python
    Replies:
    1
    Views:
    402
    Robin Becker
    Oct 27, 2004
Loading...

Share This Page