How about this: at reset (or at any other time if you want), assign the data_in
value to all elements in you stage
array. This should instantly set your average to the current value:
process (clk, reset)
begin
if (reset = '1') then
out_val <= 0;
stage <= (others => data_in(11 downto 0));
sum <= resize(255 * signed(data_in(11 downto 0)), sum'length);
elsif rising_edge(clk) then
...
The example below shows the complete code for a moving average calculator. My suggestion is that you study it until you understand it. Then, try to use it in your design. Finally, and only after you have a basic circuit working, you could change it to satisfy your design constraints (data width, number of samples, range of integers, use of signed
vs. integer
, etc.)
library ieee;
use ieee.std_logic_1164.all;
entity moving_average is
generic(
SAMPLES_COUNT: integer := 256
);
port (
sample: in integer;
average: out integer;
clock: in std_logic;
reset: in std_logic
);
end;
architecture rtl of moving_average is
signal samples_fifo: integer_vector(1 to SAMPLES_COUNT);
signal sum: integer;
begin
process (clock, reset) begin
if reset then
samples_fifo <= (others => sample);
sum <= SAMPLES_COUNT * sample;
elsif rising_edge(clock) then
samples_fifo <= sample & samples_fifo(1 to SAMPLES_COUNT-1);
sum <= sum + sample - samples_fifo(SAMPLES_COUNT);
end if;
end process;
average <= sum / SAMPLES_COUNT;
end;
Finally, if you want to use the above code to keep two separate averages for two distinct signals, simply instantiate the averaging entity twice:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity use_moving_average is
port (
clock: in std_logic;
reset: in std_logic;
channel_1_sample: in signed(11 downto 0);
channel_2_sample: in signed(11 downto 0);
channel_1_average: out signed(11 downto 0);
channel_2_average: out signed(11 downto 0)
);
end;
architecture rtl of use_moving_average is
signal average_1, average_2: integer;
begin
channel_1: entity work.moving_average
port map(
sample => to_integer(channel_1_sample),
average => average_1,
clock => clock,
reset => reset
);
channel_2: entity work.moving_average
port map(
sample => channel_2_sample,
average => average_2,
clock => clock,
reset => reset
);
channel_1_average <= to_signed(average_1, 12);
channel_2_average <= to_signed(average_2, 12);
end;
Edit: As I understand from your comments, you may need an extra input to set the average instantaneously to the current input value. In that case, you can use a load
input as shown below:
library ieee;
use ieee.std_logic_1164.all;
entity moving_average is
generic(
SAMPLES_COUNT: integer := 256
);
port (
sample: in integer;
average: out integer;
clock: in std_logic;
reset: in std_logic;
load: in std_logic
);
end;
architecture rtl of moving_average is
signal samples_fifo: integer_vector(1 to SAMPLES_COUNT);
signal sum: integer;
begin
process (clock, reset) begin
if reset then
samples_fifo <= (others => sample);
sum <= SAMPLES_COUNT * sample;
elsif rising_edge(clock) then
if load then
samples_fifo <= (others => sample);
sum <= SAMPLES_COUNT * sample;
else
samples_fifo <= sample & samples_fifo(1 to SAMPLES_COUNT-1);
sum <= sum + sample - samples_fifo(SAMPLES_COUNT);
end if;
end if;
end process;
average <= sum / SAMPLES_COUNT;
end;