Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
17255 Discussions

continuous averaging using VHDL

Altera_Forum
Honored Contributor II
17,084 Views

I have a question related to VHDL programming. I want to calculate the continuous average. My example code is: 

 

process (clk, reset) begin if (reset = '1') then state<=idle; out-val=0; elsif(rising_edge(clk)) then case state is when idle => if req='1' then state= out-1; end if; when out-1 => if done='1' then out-val<=data-in (11 downto 0) state <= done-st; endif; when done-st => ack <='1'; state <= idle; when others => state <= idle; end case; end if; end process;  

 

On every positive edge of clock, the value of "out-val" changes. I want to continuously take the average of "out-val". I want to take average of 32 values continuously. Is there a way where I can take average of 32 values continuously till the clock is running. Kindly let me know how can I do that. You can modify the above code as well. 

Many Thanks,
0 Kudos
90 Replies
Altera_Forum
Honored Contributor II
2,081 Views

 

--- Quote Start ---  

@kaz 

What if I want to converge to a single value with continuous averaging? Is it possible? As an example if I average first 32 samples and the averaged value used in the next averaged 32 samples. In the end I will converge to some stable ADC value. In this way I will have less fluctuation in my ADC values. 

--- Quote End ---  

 

 

Depends what you want. 

if you want running average then we are on it. 

if you want block average then you can average each block separately. 

if you then average the averages then it is up to you and your purpose but it only complicates what is basically simple subtract accumulate.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

The filter y(n) = k * y(n-1) + x(n) approximates to average(x) / (1 - k) for 0 < k < 1. 

It isn't as good as a rolling average, but is often good enough.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

@dsl 

Can you modify my code with your logic? I want to see if I converge to some value after continuous averaging.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

Not quickly - I'm a software engineer, I write assembler and C, not vhdl :-)

0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

 

--- Quote Start ---  

The filter y(n) = k * y(n-1) + x(n) approximates to average(x) / (1 - k) for 0 < k < 1. 

It isn't as good as a rolling average, but is often good enough. 

--- Quote End ---  

 

 

Interesting point. what I found is this: 

 

for n taps running average a good approximation is y(i) = a*y(i-1) + (1-a)*x(i); 

with a = (n-1)/n 

 

However this requires two(or one multiplier) plus adder, so I think the true running average is simpler if you can afford the storage. 

surely for large value of n then your suggestion is much better. Moreover the value (a) is programmable.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

If n is a power of 2 you don't need a multiply - just some shiftsand two adds

0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

 

--- Quote Start ---  

If n is a power of 2 you don't need a multiply - just some shiftsand two adds 

--- Quote End ---  

 

 

Yes, in that case we convert: 

 

y(n) = a*y(n-1) + (1-a)*x(n) ... two multipliers, one adder 

 

...to y(n) = a*y(n-1) + x(n) -a*x(n) 

 

i.e. y(n) = a*(y(n-1) -x(n)) + x(n); ... one multiplier, one subtractor, one adder 

then (a) can be power of 2 but we get limitations on cutoff point
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

@kaz, 

 

I have 2 questions related to the continuous averaging.  

First question is related to increasing the number of stages. I want to increase the number of stages from 32 to 1000. I am doing this for stabilizing the ADC current values. Do I have to modify the signal lengths in the following way: 

 

data_in : in std_logic_vector (31 downto 0); type type1 is array (1 to 999) of std_logic_vector(11 downto 0); signal stage: type1 := (others => (others => '0')); signal sub_result: signed(12 downto 0) := (others => '0'); signal sum: signed(16 downto 0) := (others => '0'); signal stage2: type1 := (others => (others => '0')); signal sub_result2: signed(12 downto 0) := (others => '0'); signal sum2: signed(16 downto 0) := (others => '0');  

 

The second question is: I was not getting correct values before but now with the following approach I get the correct values. Can you see and tell if the following way looks fine. It is working correctly but Is there a better way to do this? I used the same approach as u told me but in a different way. I need to average in parallel. The code is following: 

 

data_in : in std_logic_vector (31 downto 0); type type1 is array (1 to 31) of std_logic_vector(11 downto 0); signal stage: type1 := (others => (others => '0')); signal sub_result: signed(12 downto 0) := (others => '0'); signal sum: signed(16 downto 0) := (others => '0'); signal stage2: type1 := (others => (others => '0')); signal sub_result2: signed(12 downto 0) := (others => '0'); signal sum2: signed(16 downto 0) := (others => '0'); process (clk, reset) begin if (reset = '1') then state<=idle; out_val=0; out_val_2 <= 0; avg_1 <= 0; avg_2 <=0; elsif(rising_edge(clk)) then case state is when idle => if req='1' then state= out_1; end if; when out_1 => if done='1' then data_out <= addr0 & bits; stage(1) <= data_in(11 downto 0); for i in 2 to 31 loop stage(i) <= stage(i-1); end loop; -- subtract last stage from input sub_result <= resize(signed(data_in),13) - signed(stage(31)); -- accumulate sum <= sum + sub_result; adc_a_out <= std_logic_vector(sum(16 downto 5)); avg_1 <= '1'; state <= out_2; endif; when out_2 => if done='1' then data_out <= addr1 & bits; stage2(1) <= data_in(11 downto 0); for i in 2 to 31 loop stage2(i) <= stage2(i-1); end loop; -- subtract last stage from input sub_result2 <= resize(signed(data_in),13) - signed(stage(31)); -- accumulate sum2 <= sum2 + sub_result2; adc_b_out <= std_logic_vector(sum2(16 downto 5)); avg_2 <= '1'; state <= done_st; when done_st => ack <='1'; state <= idle; when others => state <= idle; end case; end if; end process; 

 

Many Thanks.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

For a 32 'sample' IRF I'd do: 

y(n) = y(n-1) - y(n-1)/32 + x(n) 

the average is then y(n)/32.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

 

--- Quote Start ---  

For a 32 'sample' IRF I'd do: 

y(n) = y(n-1) - y(n-1)/32 + x(n) 

the average is then y(n)/32. 

--- Quote End ---  

 

 

Unfortunately your equation does not match 32 stage running averager at all. values shoot up very high and high frequencies are passed. 

But it works if: 

y(n) = y(n-1) - y(n-1)/32 + x(n)/32
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

having 1000 stages for 12 bits x 2 will require 24000 registers so you need to use ram for that. 

 

However 1000 stages is an overkill. If so I will use block averager that only needs accumulator for 1024 samples then divide sum by discarding 10 LSBs at the end of each 1024 samples block then clear the sum and restart second block. You will get some segmentation of result(sharp corners) but you can then if you wish average the block results. 

 

Regarding your code that "works", it looks ok to me.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

 

--- Quote Start ---  

@kaz, 

 

I have 2 questions related to the continuous averaging.  

First question is related to increasing the number of stages. I want to increase the number of stages from 32 to 1000. I am doing this for stabilizing the ADC current values. Do I have to modify the signal lengths in the following way: 

 

 

--- Quote End ---  

 

 

My final conclusion for 1024 average use equation suggested by dsl and modified as follows: 

y(n) = y(n-1) - y(n-1)/1024 + x(n)/1024 i.e. 

 

clocked process 

sum = sum - sum(20 downto 10) + data_in(11 downto 10); -- use more width for sum if necessary to avoid overflow. 

end process; 

 

only two bits of data_in are used so you may lose resolution but it might be just ok.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

@kaz 

You mean to do the following: 

 

data_in : in std_logic_vector (31 downto 0); signal sum: signed(23 downto 0) := (others => '0'); signal sum2: signed(23 downto 0) := (others => '0'); process (clk, reset) begin if (reset = '1') then state<=idle; out_val=0; out_val_2 <= 0; elsif(rising_edge(clk)) then case state is when idle => if req='1' then state= out_1; end if; when out_1 => if done='1' then data_out <= addr0 & bits; sum = sum - sum(22 downto 10) + data_in(11 downto 10); adc_a_out <= std_logic_vector(sum(22 downto 11)); state <= out_2; endif; when out_2 => if done='1' then data_out <= addr1 & bits; sum2 = sum2 - sum2(22 downto 10) + data_in(11 downto 10); adc_b_out <= std_logic_vector(sum2(22 downto 11)); state <= done_st; endif; when done_st => ack <='1'; state <= idle; when others => state <= idle; end case; end if; end process; 

 

Please correct me if I am wrong. 

Thanks.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

 

--- Quote Start ---  

@kaz 

You mean to do the following: 

 

data_in : in std_logic_vector (31 downto 0); signal sum: signed(23 downto 0) := (others => '0'); signal sum2: signed(23 downto 0) := (others => '0'); process (clk, reset) begin if (reset = '1') then state<=idle; out_val=0; out_val_2 <= 0; elsif(rising_edge(clk)) then case state is when idle => if req='1' then state= out_1; end if; when out_1 => if done='1' then data_out <= addr0 & bits; sum = sum - sum(22 downto 10) + data_in(11 downto 10); adc_a_out <= std_logic_vector(sum(22 downto 11)); state <= out_2; endif; when out_2 => if done='1' then data_out <= addr1 & bits; sum2 = sum2 - sum2(22 downto 10) + data_in(11 downto 10); adc_b_out <= std_logic_vector(sum2(22 downto 11)); state <= done_st; endif; when done_st => ack <='1'; state <= idle; when others => state <= idle; end case; end if; end process; 

 

Please correct me if I am wrong. 

Thanks. 

--- Quote End ---  

 

 

The sum & sum2 are themselves the average, you don't need remove the 11 LSBs from them. You can keep all 23 bits since average need not be same width as data though I don't expect the mean of a good random signed signal to be higher than data. 

 

However I did some modelling of 1024 stages and it does lose resolution badly so I suggest using this method for no more than 128 stages instead of 1024.
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

@kaz 

 

I think its better if I go with block average approach then because I don't want my resolution to be bad. As you said in one of the previous posts  

 

"If so I will use block averager that only needs accumulator for 1024 samples then divide sum by discarding 10 LSBs at the end of each 1024 samples block then clear the sum and restart second block. You will get some segmentation of result(sharp corners) but you can then if you wish average the block results." 

 

I don't have Quartus or FPGA board with me now. I will check it on Monday. Just to make sure and clear, can you modify the code for one state below for block averager and average the block results.  

 

data_in : in std_logic_vector (31 downto 0); type type1 is array (1 to 31) of std_logic_vector(11 downto 0); signal stage: type1 := (others => (others => '0')); signal sub_result: signed(12 downto 0) := (others => '0'); signal sum: signed(16 downto 0) := (others => '0'); process (clk, reset) begin if (reset = '1') then out_val=0; elsif(rising_edge(clk)) then case state is when out_1 => if done='1' then data_out <= addr0 & bits; stage(1) <= data_in(11 downto 0); for i in 2 to 31 loop stage(i) <= stage(i-1); end loop; -- subtract last stage from input sub_result <= resize(signed(data_in),13) - signed(stage(31)); -- accumulate sum <= sum + sub_result; adc_a_out <= std_logic_vector(sum(16 downto 5)); state <= out_2; endif; end process;
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

 

--- Quote Start ---  

@kaz 

 

I think its better if I go with block average approach then because I don't want my resolution to be bad. As you said in one of the previous posts  

 

Just to make sure and clear, can you modify the code for one state below for block averager and average the block results.  

 

 

--- Quote End ---  

 

 

here is my suggestion 

 

signal counter : integer 0 to 1023 := 0; signal data_in_d : signed(11 downto 0) := (others => '0'); signal sum: signed(21 downto 0) := (others => '0); -clocked process counter <= counter + 1; data_in_d <= signed(data_in); if counter /= 0 then sum <= sum + data_in_d; else sum <= (others => '0'); avg <= std_logic_vector(sum(21 downto 10)); end if;  

 

avg will get updated every 1024 samples. if you then want to average the avg (or sum) then use previous method of say 32 samples of avg enabled on the counter = 0 pulse 

 

you might also run counter from 0 to 1024 instead of 0 to 1023 for more accurate result (I believe)
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

@kaz 

I hope I understand you right. 

You mean to say the following: 

 

 

signal counter : integer 0 to 1024 := 0; signal data_in_d : signed(11 downto 0) := (others => '0'); signal sum: signed(21 downto 0) := (others => '0); signal avg : std_logic_vector (11 downto 0); type type1 is array (1 to 31) of std_logic_vector(11 downto 0); signal stage: type1 := (others => (others => '0')); signal sub_result: signed(12 downto 0) := (others => '0'); signal sum_new: signed(16 downto 0) := (others => '0'); process (clk, reset) begin if (reset = '1') then out_val=0; elsif(rising_edge(clk)) then case state is when out_1 => if done='1' then data_out <= addr0 & bits; counter <= counter + 1; data_in_d <= signed(data_in); if counter /= 0 then sum <= sum + data_in_d; else sum <= (others => '0'); avg <= std_logic_vector(sum(21 downto 10)); end if; stage(1) <= avg(11 downto 0); for i in 2 to 31 loop stage(i) <= stage(i-1); end loop; -- subtract last stage from input sub_result <= resize(signed(avg),13) - signed(stage(31)); -- accumulate sum_new <= sum_new + sub_result; adc_a_out <= std_logic_vector(sum_new(16 downto 5)); state <= out_2; endif; end process; 

 

Am I right?
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

to avoid complexity make counter o to 1023. 

it looks ok except that you need the running average be enabled when count = 0 only i.e. include all that in the else section. 

 

if count /= 0 

... 

else 

... 

your running averager here... 

end if;
0 Kudos
Altera_Forum
Honored Contributor II
2,081 Views

okay. You mean like this: 

 

 

process (clk, reset) begin if (reset = '1') then out_val=0; elsif(rising_edge(clk)) then case state is when out_1 => if done='1' then data_out <= addr0 & bits; counter <= counter + 1; data_in_d <= signed(data_in); if counter /= 0 then sum <= sum + data_in_d; else sum <= (others => '0'); avg <= std_logic_vector(sum(21 downto 10)); stage(1) <= avg(11 downto 0); for i in 2 to 31 loop stage(i) <= stage(i-1); end loop; -- subtract last stage from input sub_result <= resize(signed(avg),13) - signed(stage(31)); -- accumulate sum_new <= sum_new + sub_result; adc_a_out <= std_logic_vector(sum_new(16 downto 5)); end if; state <= out_2; endif; end process;
0 Kudos
Altera_Forum
Honored Contributor II
2,061 Views

yes that is right. Even though it does not make much difference in this case compared with your previous approach because average of constant gives same constant anyway but just to be meaningful.

0 Kudos
Altera_Forum
Honored Contributor II
2,061 Views

@kaz 

 

With this approach I am getting no value. I used the following code: 

 

signal counter : integer range 0 to 1023 := 0; signal data_in_d : signed(11 downto 0) := (others => '0'); signal sum: signed(21 downto 0) := (others => '0); signal avg : std_logic_vector (11 downto 0); type type1 is array (1 to 31) of std_logic_vector(11 downto 0); signal stage: type1 := (others => (others => '0')); signal sub_result: signed(12 downto 0) := (others => '0'); signal sum_new: signed(16 downto 0) := (others => '0'); process (clk, reset) begin if (reset = '1') then out_val=0; elsif(rising_edge(clk)) then case state is when out_1 => if done='1' then data_out <= addr0 & bits; counter <= counter + 1; data_in_d <= signed(data_in(11 downto 0)); if counter /= 0 then sum <= sum + data_in_d; else sum <= (others => '0'); avg <= std_logic_vector(sum(21 downto 10)); stage(1) <= avg(11 downto 0); for i in 2 to 31 loop stage(i) <= stage(i-1); end loop; -- subtract last stage from input sub_result <= resize(signed(avg),13) - signed(stage(31)); -- accumulate sum_new <= sum_new + sub_result; adc_a_out <= std_logic_vector(sum_new(16 downto 5)); end if; state <= out_2; endif; end process; 

 

The value of adc_a_out is '0' all the time. Is there any mistake in it?
0 Kudos
Reply