Best way for average out

Joined
Jul 6, 2011
Messages
2
Reaction score
0
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?
 
Joined
Jun 2, 2011
Messages
10
Reaction score
0
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:
Joined
Jul 6, 2011
Messages
2
Reaction score
0
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. :)

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?
 
Joined
Jun 2, 2011
Messages
10
Reaction score
0
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 ;)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top