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

continuous averaging using VHDL

Altera_Forum
Honored Contributor II
12,509 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
3,755 Views

 

--- Quote Start ---  

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, 

--- Quote End ---  

 

 

first you nee to correct your syntax (don't use dash but underscore) 

For running average filter, the easiest way is delay input through 32 stages. subtract last stage from input and accumulate result.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

Actually I used underscore in my original code. I don't know why did I write 'out-1' here. Its 'out_1' Sorry for the confusion. Can you describe with an example how to do continuous averaging.

0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

 

--- Quote Start ---  

Actually I used underscore in my original code. I don't know why did I write 'out-1' here. Its 'out_1' Sorry for the confusion. Can you describe with an example how to do continuous averaging. 

--- Quote End ---  

 

 

running average: 

 

input =>=>=>...32 stages 

-----------------> subtract stage 32 from current input 

 

put the subtractor at end of delay pipe to subtract last stage from current input 

 

then take the result of subtraction into an accumulator (feedback adder with one register). truncate the accumulator sum as suitable.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

 

--- Quote Start ---  

 

then take the result of subtraction into an accumulator (feedback adder with one register). truncate the accumulator sum as suitable. 

--- Quote End ---  

 

 

discard 5 LSBs from accumulator result. The first initial 31 samples will not be correct but the stream will then get right.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

I exactly still don't understand what you mean. Can you write down the code if it is possible? 

ManY Thanks
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

 

--- Quote Start ---  

I exactly still don't understand what you mean. Can you write down the code if it is possible? 

ManY Thanks 

--- Quote End ---  

 

 

writing code is no small job. attached diagram should be clear.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

@kaz 

Just as an example, I am doing continuous averaging for 4 values. Can you check if my approach is correct. 

 

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); i_valid <=1; state <= done_st; endif; when done_st => ack <='1'; state <= idle; when others => state <= idle; end case; end if; end process; process begin wait until rising_edge(clk); valid1 <= i_valid; o_valid <= valid1; end process -- i_valid is control bit and enables when input data is valid process begin wait until rising_edge(clk); if reset = ’1’ then idx <= "0001"; elseif valid1 = ’1’ then idx <= idx rol 1; end if; end if; end process; process begin wait until rising_edge(clk); for i in 3 downto 0 loop if (i_valid = ’1’) and (idx(i) = ’1’) then M(i) <= out_val; end if; end loop; end process; mem_out <= M(0) when idx(0) = ’1’else M(1) when idx(1) = ’1’else M(2) when idx(2) = ’1’else M(3); add_sub <= sum - mem_out when valid1 = ’1’ else sum + mem_out; process begin wait until rising_edge(clk); if i_valid = ’1’ or valid1 = ’1’ then sum <= add_sub; end if; end process; 

 

sum is the output average value. The above code works if I have unsigned numbers. But I want to average the std_logic_vector. The data_in is defined as: data_in: in std_logic_vector (11 downto 0); 

 

Kindly help me in solving this issue. Please modify the above code so that I can get continuous averaging values for vector 'out_val'. 

 

Many Thanks.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

 

--- Quote Start ---  

@kaz 

Just as an example, I am doing continuous averaging for 4 values. Can you check if my approach is correct. 

 

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); i_valid <=1; state <= done_st; endif; when done_st => ack <='1'; state <= idle; when others => state <= idle; end case; end if; end process; process begin wait until rising_edge(clk); valid1 <= i_valid; o_valid <= valid1; end process -- i_valid is control bit and enables when input data is valid process begin wait until rising_edge(clk); if reset = ’1’ then idx <= "0001"; elseif valid1 = ’1’ then idx <= idx rol 1; end if; end if; end process; process begin wait until rising_edge(clk); for i in 3 downto 0 loop if (i_valid = ’1’) and (idx(i) = ’1’) then M(i) <= out_val; end if; end loop; end process; mem_out <= M(0) when idx(0) = ’1’else M(1) when idx(1) = ’1’else M(2) when idx(2) = ’1’else M(3); add_sub <= sum - mem_out when valid1 = ’1’ else sum + mem_out; process begin wait until rising_edge(clk); if i_valid = ’1’ or valid1 = ’1’ then sum <= add_sub; end if; end process; 

 

sum is the output average value. The above code works if I have unsigned numbers. But I want to average the std_logic_vector. The data_in is defined as: data_in: in std_logic_vector (11 downto 0); 

 

Kindly help me in solving this issue. Please modify the above code so that I can get continuous averaging values for vector 'out_val'. 

 

Many Thanks. 

--- Quote End ---  

 

 

 

I don't see how your approach works. If it is me I will just follow the diagram I posted earlier. for example 

 

-- not tested -- input data_in & output assumed std_logic_vector(15 downto 0); type type1 is array (1 to 31) of std_logic_vector(15 downto 0); signal stage: type1 := (others => (others => '0')); signal sub_result: signed(16 downto 0) := (others => '0'); signal sum: signed(20 downto 0) := (others => '0'); -- delay input 31 stages process begin wait until clock = '1'; stage(1) <= data_in; for i in 2 to 31 loop stage(i) <= stage(i-1); end loop; -- subtract last stage from input sub_result <= signed(data_in) - signed(stage(31)); -- accumulate sum <= sum + sub_result; output <= std_logic_vector(sum(20 downto 5)); end process;
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

Your approach didn't work for me. I checked the output on the FPGA but it didn't work for me. Basically I am trying to stabilize the ADC output values. That is why I am averaging the incoming data or captured data with ADC. out_val and out_val_2 are the 12 bit ADC outputs. I'll show how I used your approach below. Kindly correct me if I am wrong: 

 

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; out_val<=data_in (11 downto 0); avg_1 <= '1'; state <= out_2; endif; when out_2 => if done='1' then data_out <= addr1 & bits; out_val_2<=data_in (11 downto 0); avg_2 <= '1'; state <= done_st; when done_st => ack <='1'; state <= idle; when others => state <= idle; end case; end if; end process; --Then comes your approach -- delay input 31 stages adc_a_avg : process begin wait until clk = '1'; if avg_1 = '1' then 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 <= signed(data_in(11 downto 0) & '0') - signed(stage(31)); -- I had to append 0 because I was getting error if I don't put it. -- accumulate sum <= sum + sub_result; adc_a_out <= std_logic_vector(sum(16 downto 5)); end if; end process adc_a_avg; adc_b_avg : process begin wait until clk = '1'; if avg_2 = '1' then 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 <= signed(data_in(11 downto 0) & '0') - signed(stage2(31)); -- accumulate sum2 <= sum2 + sub_result2; adc_b_out <= std_logic_vector(sum2(16 downto 5)); end if; end process adc_b_avg; 

 

I measure the adc values (current and voltage) "adc_a_out and adc_b_out" after using the above approach and I get incorrect values. Kindly let me know where I am doing wrong.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

Couple of comments: 

Why are you appending '0' on the LSB? thats like a multiply by 2, and as it is signed, it then uses the MSB as the sign bit. I think you mean: 

 

('0' & data_in(11 downto 0); 

 

Unless data_in is signed, then you need: 

 

(data_in(11) & data_in(11 downto 0)); 

 

And as a general comment: 

 

Why is type1 an array of std_logic_vector? why not make your life easier and make it an array of signed, then you dont need all the type conversions.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

@tricky 

Sorry that was a mistake. You are right. I mean ('0' & data_in(11 downto 0); 

Sorry for the confusion. 

Can you tell me why I am not getting correct ADC values? 

I used the approach that was given to me in the previous comment by @kaz
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

If the direct ADC values are incorrect, isnt this something for you to Debug? it would be easy to test this code does a rolling average in a simple testbench (you just pass a set of known values into the data_in port) so you can ensure this works before debugging any upstream problems.

0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

 

--- Quote Start ---  

@tricky 

Sorry that was a mistake. You are right. I mean ('0' & data_in(11 downto 0); 

Sorry for the confusion. 

Can you tell me why I am not getting correct ADC values? 

I used the approach that was given to me in the previous comment by @kaz 

--- Quote End ---  

 

 

you should not put '0' unless it is unsigned but your computation is signed. This will give wrong results. 

if your data is 12 bits then use less bits(subtractor needs 13 bits, sum needs 18 bits). 

you are wasting double resource for two cases. You can use one computation section for both switching between them at input to delay section. 

 

setting data type to signed at delay stages as Tricky suggested is an option but I believe doesn't make life any easier.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

An alternative is to use an 'infinite response filter' instead of a true average. This gives more weight to recent data, but is easier to calculate since it doesn't require all the old values be stored. 

Basically calculate 'new_irf = old_irf * 31/32 + sample'.
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

@kaz 

If I dont put '0', I get the following error. Should I reduce subtractor bits? 

Error (10344): VHDL expression error at adc_cntrl.vhd(160): expression has 12 elements, but must have 13 elements. 

Can you tell me how can I use one computation section for both switching between them at input to delay section? Can you modify my code? 

Many Thanks
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

@dsl 

Can you modify my code with your approach?
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

 

--- Quote Start ---  

@kaz 

If I dont put '0', I get the following error. Should I reduce subtractor bits? 

Error (10344): VHDL expression error at adc_cntrl.vhd(160): expression has 12 elements, but must have 13 elements. 

 

--- Quote End ---  

 

 

 

sub_result <= resize(signed(data_in),13) - signed(stage(31)); 

 

if you are not averaging both ADC data in parallel then you can switch between them at signal stage: 

 

if avg_1 then stage(1) <= adc1; elsif avg_2 then stage(1) <= adc2; end if;
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

 

--- Quote Start ---  

An alternative is to use an 'infinite response filter' instead of a true average. This gives more weight to recent data, but is easier to calculate since it doesn't require all the old values be stored. 

Basically calculate 'new_irf = old_irf * 31/32 + sample'. 

--- Quote End ---  

 

 

you are using an integrator of equation: 

y(n) = 31/32*y(n-1) + x(n); 

 

This has a different response from running average filter and is not functionally equivalent.  

The post is focused on running average. 

If it is ok then why not just use: y(n) = y(n-1) + x(n) 

i.e. just an accumulator: 

sum <= sum + data_in;
0 Kudos
Altera_Forum
Honored Contributor II
3,755 Views

@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.
0 Kudos
Altera_Forum
Honored Contributor II
3,520 Views

@kaz 

I am averaging both the adc data in parallel. That is why I used 2 computations.
0 Kudos
Reply