# Best way for average out

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

1. ### 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

2. ### 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

3. ### 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
4. ### flymolo

Joined:
Jun 2, 2011
Messages:
10
Hey. I had some free time today and i came up with this:
Code (Text):
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);

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