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;