Forum: FPGA, VHDL & Co. Moving Average zu langsam


von Achim (Gast)


Lesenswert?

Hallo Zusammen,

in meinem Design habe ich ein Moving Average Filter, das bisher so 
aussieht:
1
library IEEE;
2
use IEEE.Std_Logic_1164.all;
3
use IEEE.Numeric_std.all;
4
use IEEE.math_real.log2;
5
use IEEE.math_real.ceil;
6
7
8
entity moving_average is
9
   generic (
10
      c_width          : integer := 14;               -- Bit width of input values
11
      c_average_depth   : integer := 4                 -- Amount of averaging
12
      );
13
   port (
14
      CLK           : in  std_logic;
15
      RESET         : in  std_logic;
16
      DATA_IN       : in  std_logic_vector(c_width - 1 downto 0);               -- Data input
17
      VALID_IN      : in  std_logic;                              -- Data valid input
18
      DATA_OUT      : out std_logic_vector(c_width - 1 downto 0);               -- Data output
19
      DATA_OUT_LONG : out std_logic_vector(c_width + (integer(ceil(log2(real(c_average_depth))))) - 1 downto 0) -- Data output (not rounded to input with)
20
      );
21
end moving_average;
22
23
24
architecture rtl of moving_average is
25
26
  constant c_count_width : integer := integer(ceil(log2(real(c_average_depth))));
27
  type   taverage_array is array (c_average_depth - 1 downto 0) of unsigned(c_width - 1 downto 0);
28
    signal s_average_array : taverage_array   := (others => (others => '0'));
29
  signal s_add_reg : unsigned(c_width + c_count_width - 1 downto 0) := (others => '0');
30
   
31
begin
32
33
  pfifo : process (CLK) is
34
    variable i : integer range 0 to c_average_depth - 1 := 0;
35
  begin
36
    if rising_edge (CLK) then
37
      if RESET = '1' then
38
        s_average_array <= (others => (others => '0'));
39
      else
40
        if VALID_IN = '1' then
41
          s_average_array(0) <= unsigned(DATA_IN);
42
          for i in 0 to c_average_depth - 2 loop
43
            s_average_array(i+1) <= s_average_array(i);
44
          end loop;
45
        end if;
46
      end if;
47
    end if;
48
  end process pfifo;
49
  
50
  padd : process (s_average_array)
51
    variable v_add_reg_temp : unsigned(c_width + c_count_width - 1 downto 0) := (others => '0');
52
  begin
53
    v_add_reg_temp := (others => '0');
54
    for i in 0 to c_average_depth - 1 loop
55
      v_add_reg_temp := v_add_reg_temp + s_average_array(i);
56
    end loop;
57
    s_add_reg <= v_add_reg_temp;
58
  end process padd;
59
            
60
   DATA_OUT      <= std_logic_vector(s_add_reg(c_width + c_count_width - 1 downto c_count_width));
61
   DATA_OUT_LONG <= std_logic_vector(s_add_reg);
62
   
63
end rtl;

Wird hier z.B. der gleitende Mittelwert über 4-8 Werte gebildet, 
funktioniert die Sache super. Nun muss ich aber über 64 Werte mitteln. 
Da aus einem anderen Porzess getaktet auf "DATA_OUT" zugegriffen wird, 
ist das Design viel zu langsam - etwa 13MHz auf einem Spartan 3e (es 
müsste mindestens 25 schaffen). Eine Möglichkeit wäre, nicht den jeweils 
ersten Wert von der Summe abzuziehen, sondern den Mittelwert. Gibt es 
einen Weg das Design schneller zu machen, ohne auf diesen Trick zurück 
zu greifen?

Danke und viele Grüße

Achim

von Tobias L. (murxwitz)


Lesenswert?

einen etwas anderen Ansatz: du musst ja nicht jedesmal die komplette 
Summe berechnen, sondern nur den neuen Wert dazu addieren und den n't 
letzten Wert abziehen, du musst ja eh alle Werte speichern

pseudocode:

summe += neu
summe -= alt(n)
for (k=1;k<=n;k++)
  alt(k-1) = alt(k)
alt(0) = neu

mittelwert = summe / n

finde leider den Link zu dem Beispiel gerade nicht

von Achim (Gast)


Lesenswert?

Unfassbar, dass ich da nicht selbst drauf gekommen bin. 141MHz indem auf 
den Akku jeweils nur der neue Wert aufaddiert und der letzte im Array 
subtrahiert wird. Vielen Dank :D

von Achim (Gast)


Lesenswert?

Was mir grade noch einfällt: könnte man xst eigentlich dazu bringen, 
dass Shift Register in ein BRAM zu packen?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Achim schrieb:
> könnte man xst eigentlich dazu bringen, dass Shift Register in ein BRAM
> zu packen?
Ja. Dazu müsstest du nur die Beschreibung so ändern, dass die Synthese 
ein BRAM erkennt. Siehe dort: 
http://www.lothar-miller.de/s9y/archives/20-RAM.html
Und im XST Users Guide...

Eine ressourcensparende Alternative wäre auch, ein PT1 Glied 
nachzubilden. Das ist dann ein Zweizeiler wie im 
Beitrag "Re: CPLD-Problem: moving average"

von Uli der Troll (Gast)


Lesenswert?

Allenfalls koennte man auch den exponentiellen Average verwenden, der 
braucht nur eine Speicherstelle.

von Fpgakuechle K. (Gast)


Lesenswert?

Achim schrieb:
> Was mir grade noch einfällt: könnte man xst eigentlich dazu bringen,
> dass Shift Register in ein BRAM zu packen?

Nimm den reset raus:
1
      if RESET = '1' then
2
        s_average_array <= (others => (others => '0'));
3
      else

Dein Ziel sollte allerdings kein mapping auf BRAM sondern auf 
Distributed RAM liegen, eigentlich auf SRL16. Gegenfalls kannst du ja 
ein FIFO per coregen generieren und einbauen, da hat es die Synthese 
einfacher.

MfG,

von VHDL-Fredi (Gast)


Lesenswert?

Lothar Miller schrieb:
>> könnte man xst eigentlich dazu bringen, dass Shift Register in ein BRAM
>> zu packen?
> Ja. Dazu müsstest du nur die Beschreibung so ändern, dass die Synthese
> ein BRAM erkennt. Siehe dort:

Oder noch einfacher:

Einen Haken bei XST Setting "pack slice logic into unused BRAM" setzen.

von Fpgakuechle K. (Gast)


Lesenswert?

VHDL-Fredi schrieb im Beitrag #3680965:
> Lothar Miller schrieb:
>>> könnte man xst eigentlich dazu bringen, dass Shift Register in ein BRAM
>>> zu packen?
>> Ja. Dazu müsstest du nur die Beschreibung so ändern, dass die Synthese
>> ein BRAM erkennt. Siehe dort:
>
> Oder noch einfacher:
>
> Einen Haken bei XST Setting "pack slice logic into unused BRAM" setzen.

Ein Shift register ist keine logic, das funktioniert also nicht.

Diese Funktion map nur Logic also was sich als LUT beschreiben läßt in 
einen (read-only) memory. Die logische Funktion wird bei FPGA start in 
den ROM "geschrieben". Ob '+' so ohne weiteres zu einer LUT umgewandelt 
wird, bezweifle ich. Gegegebenfalls müsste man die Addition als logische 
Funktion hinschreiben. Vorher mal abschätzen wie groß die LUT wird.



MfG,

von Sabine W. (sabine_w)


Lesenswert?

Eine Verständnisfrage, warum werden die Werte in s_average_array jeweils 
um eine Position verschoben und nicht einfach als Ring-Puffer 
eingetragen und ausgelesen? Grade bei einer Zweierpotenz- Puffergröße 
sollte das doch effizienter sein.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.