Forum Discussion
Altera_Forum
Honored Contributor
12 years agocontinuous averaging using VHDL
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,90 Replies
- Altera_Forum
Honored Contributor
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. - Altera_Forum
Honored Contributor
--- 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. - Altera_Forum
Honored Contributor
@kaz
You mean to do the following:
Please correct me if I am wrong. Thanks.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; - Altera_Forum
Honored Contributor
--- Quote Start --- @kaz You mean to do the following:
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.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; - Altera_Forum
Honored Contributor
@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
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."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."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; - Altera_Forum
Honored Contributor
--- 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
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)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; - Altera_Forum
Honored Contributor
@kaz
I hope I understand you right. You mean to say the following:
Am I right?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; - Altera_Forum
Honored Contributor
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; - Altera_Forum
Honored Contributor
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; - Altera_Forum
Honored Contributor
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.