Forum: FPGA, VHDL & Co. Mittelwertfilter


von R. K. (uc_student)


Lesenswert?

Hallo zusammen.

Meine Kentnisse im Bereich VHDL sind leider ein wenig eingeroste. Ich 
habe ein Signal, welches auf 1 Bit quantisiert ist. Ich möchte nun ein 
FIR-Filter (hier Moving Average Filter) mit 4 Koeffizienten 
implementieren. Beim Versuch die einzelnen Bits zusammenzuzählen weiss 
ich jedoch nicht weiter (also die 4. unterste Zeile). Ich habe schon 
verschiedenste Castin-Methoden probiert, jedoch ohne erfolg. Kann mir 
jemand sagen, wie ich diese 4 Bits zusammenzählen kann?
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use IEEE.NUMERIC_STD.ALL;
4
  
5
entity preamble_correlator is
6
  port (
7
    ClkxCI    : in std_logic;
8
    RstxRBI    : in std_logic;
9
    DataInxDI  : in std_logic;
10
    ReadyxSO    : out std_logic_vector (2 downto 0)
11
  );
12
end preamble_correlator;
13
14
architecture rtl of preamble_correlator is  
15
  
16
  signal DataBufxD     : std_logic_vector (3 downto 0);  
17
begin
18
19
 FIR_ff : process(ClkxCI,RstxRBI)
20
   begin
21
    if RstxRBI = '1' then
22
      DataBufxD <= (others => '0');
23
24
    elsif rising_edge (ClkxCI) then
25
      DataBufxD(3 downto 1) <= DataBufxD(2 downto 0);
26
      DataBufxD(0)          <= DataInxDI;
27
    end if;  
28
 end process FIR_ff;
29
 
30
   
31
FIR_comb:  process  (ClkxCI, RstxRBI)
32
    begin
33
      if RstxRBI = '0' then
34
        ReadyxSO <= (others => '0');
35
      elsif rising_edge (ClkxCI) then
36
        ReadyxSO <= DataBufxD(0)+DataBufxD(1)+DataBufxD(2)+DataBufxD(3);
37
      end if;
38
     end process  FIR_comb ;
39
end rtl;

von Dieter (Gast)


Lesenswert?

>if RstxRBI = '0' then


Ist der Reset so richtig beschrieben?

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


Lesenswert?

Dieter schrieb:
> Ist der Reset so richtig beschrieben?
Wozu überhaupt ein Reset?   ;-)
Seis drum...

R. K. schrieb:
> Kann mir jemand sagen, wie ich diese 4 Bits zusammenzählen kann?
Der erste logische Schritt wäre der, das eine Bit zu einem Vektor zu 
erweitern, dann diesen Vektor für die Addition zum unsigned zu casten:
1
  ReadyxSO <= std_logic_vector(unsigned("00"&DataBufxD(0))+unsigned("00"&DataBufxD(1))+
2
                               unsigned("00"&DataBufxD(2))+unsigned("00"&DataBufxD(3)));
Dann kommt aber die Meldung
1
Expression in type conversion to unsigned has 4 possible definitions 
2
in this scope, for example, SIGNED and UNSIGNED.

Das bedeutet, das die Cast-Funktion nicht weiß, welchen Datentypen sie 
da konvertieren soll, denn "00" kann ja ein bit_vector, ein 
std_logic_vector, ein signed oder ein unsigned sein.
Abhilfe schafft, hier nicht einen Cast sondern einen Qualifier zu 
verwenden, der der Addition sagt, dass die Vektoren als unsigned 
Operanden verwendet werden sollen:
1
  ReadyxSO <= std_logic_vector(unsigned'("00"&DataBufxD(0))+unsigned'("00"&DataBufxD(1))+
2
                               unsigned'("00"&DataBufxD(2))+unsigned'("00"&DataBufxD(3)));
Zum Hintergrund:
http://www.lothar-miller.de/s9y/archives/82-Qualifier.html


Darüber hinaus gibt es natürlich noch zig andere Methoden, die zum 
gewünschten Resultat führen würden...  ;-)

von CZM (Gast)


Lesenswert?

Ich würde mal die aynchronen resets weglassen oder in synchrone wandeln

wenn das casten nicht geht, dann einfach einzeln wandeln, der Compiler 
macht das dann schon

if (bit_x = '1') then
  value_x <= "00100";  die 4 steht hier für 1.0
else
  value_x <= "00000";
end if

das mit x für alle bits = 4x

dann addieren:

sum <= std_logic (unsigned (value_1) + + + );

sum ist dann maximal "10000" = 4x 1.00 = 4.00

mittel = sum (5 downto 3);

die vektoren sind in der grösse so gewählt, dass kein überlauf entsteht 
und beim summieren die längen passen, ferner gibt es keinen underflow 
oder rundungsfehler

von CZM (Gast)


Lesenswert?

ups synchron posting zu dem von LM

von Bronco (Gast)


Lesenswert?

Ich hatte mal eine ähnliche Anwendung (gesetzte Bits in einem 16Bit-Wort 
zählen). Hab es mit einer Schleife gelöst.
Ob das elegant ist oder nicht, mag ich nicht beurteilen, aber jedenfalls 
funktioniert es:
1
signal sBitsInWords    : std_logic_vector(4 downto 0);
2
3
process (CLK) 
4
  variable vBitsInWord : std_logic_vector(4 downto 0);
5
begin
6
  if (rising_edge(CLK)) then
7
    vBitsInWord := "00000";
8
    for c in 0 to 15 loop
9
      if ( WORDIN(c) = '1' ) then 
10
        vBitsInWord := vBitsInWord + 1; 
11
      end if;
12
    end loop;
13
    sBitsInWords <= vBitsInWord; 
14
  end if;
15
end process;

Wird innerhalb eines Taktzyklus' ausgeführt und die Synthese generiert 
daraus meines Wissens 16 Addierer.

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


Lesenswert?

R. K. schrieb:
> FIR_comb:  process  (ClkxCI, RstxRBI)
Der Prozess hat seinen Namen nicht verdient, weil er nicht 
kombinatorisch ist...

Ich hätte da noch einen:
1
    FIR_comb:  process  (DataBufxD)
2
    variable cnt : integer range 0 to 4;
3
    begin
4
        cnt := 0;
5
        for i in 0 to 3 loop
6
           if DataBufxD(i)='1' then 
7
              cnt := cnt + 1;
8
           end if;
9
        end loop;
10
        ReadyxSO <= std_logic_vector(to_unsigned(cnt,3));
11
    end process  FIR_comb ;

Ergebnis:
Macro Statistics
# Adders/Subtractors     : 3
 3-bit adder             : 3


EDIT: Hoppala, zu spät.
Aber wenigstens ist meine Lösung richtiger (4 Bits statt 16)...  ;-)
Und ausserdem wird die vorgeschlagene Addition eines integers auf einen 
std_logic_vector mit der numeric_std nicht funktionieren:
 vBitsInWord := vBitsInWord + 1;

von R. K. (uc_student)


Lesenswert?

Super danke.

Mir gefällt die Lösung von Lothar Miller am besten, da ich die 
for-schleifen in einem VHDL-Code irgendwie "unpassend" finde. (so lange 
man diese vermeiden kann).
Oder ist das sowiso egal und man hofft dass der Compiler was schlaues 
daraus macht?

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


Lesenswert?

R. K. schrieb:
> Oder ist das sowiso egal und man hofft dass der Compiler was schlaues
> daraus macht?
Nein, Hoffen hilft da nicht. Das hier ist z.B. am schnellsten und 
ressourcensparendsten:
1
 ReadyxSO <= "000" when DataBufxD="0000" else
2
             "001" when DataBufxD="0001" or DataBufxD="0010" or DataBufxD="0100" or DataBufxD="1000" else
3
             "010" when DataBufxD="0011" or DataBufxD="0110" or DataBufxD="1100" or DataBufxD="1001" else
4
             "011" when DataBufxD="0111" or DataBufxD="1110" or DataBufxD="1101" or DataBufxD="1011" else
5
             "100";

Letztlich muss die Lösung sogar in 3 simple 4er-LUTs passen, weil du 4 
Eingangssignale und je 1 Ausgangssignal (pro Zählerbit) hast:
1
 ReadyxSO(0) <= '1' when DataBufxD="0001" or DataBufxD="0010" or 
2
                         DataBufxD="0100" or DataBufxD="1000" or
3
                         DataBufxD="0111" or DataBufxD="1110" or        
4
                         DataBufxD="1101" or DataBufxD="1011" else
5
                '0';
6
 ReadyxSO(1) <= '1' when DataBufxD="0011" or DataBufxD="0110" or 
7
                         DataBufxD="1100" or DataBufxD="1001" or
8
                         DataBufxD="0111" or DataBufxD="1110" or        
9
                         DataBufxD="1101" or DataBufxD="1011" else
10
                '0';
11
 ReadyxSO(2) <= '1' when DataBufxD="1111"  else
12
                '0';

Dise beiden Lösungen sind allerdings wieder äquivalent.

von Matthias (Gast)


Lesenswert?

Die for-Schleife wird ja nicht sequentiell umgesetzt, sondern parallel. 
Kommt es da nicht im Bsp von Lothar mit zu einem "kombinatorischem" 
counter?

Ich meine zB so dass wenn DataBufxD(0) '1' ist der counter hochzählt. 
Wenn DataBufx(1) '1', etc. dann auch. Sprich sobald eines der Bits '1' 
ist, wird wild herum gecountet?

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


Lesenswert?

Matthias schrieb:
> der counter hochzählt.
Da zählt kein Counter!
Dieser Counter ist nur eine Hilfestellung für den Synthesizer. Damit 
weiß der Synthesizer, wei viele Addierer er verwenden muss. Der Counter 
taucht in der Realität nirgends auf...

von Bronco (Gast)


Lesenswert?

Lothar Miller schrieb:
> Aber wenigstens ist meine Lösung richtiger (4 Bits statt 16)...  ;-)
Selbstverständlich.

> Und ausserdem wird die vorgeschlagene Addition eines integers auf einen
> std_logic_vector mit der numeric_std nicht funktionieren:
>  vBitsInWord := vBitsInWord + 1;
Doch, funktioniert, nachweislich! Ich hab mich auch gewundert...

von R. K. (uc_student)


Lesenswert?

Stimmt. Lookup-Tabellen waren schon im Studium immer die schnellsten 
Varianten um so was zu wandeln. Jedoch hatte ich dies 
verdrängt/vergessen.
Um das Hoffen ein bisschen zu "vebessern" gehe ich meistens so vor:
1)Überlegen was ich will
2) Code schreiben
3) Im RTL schauen was der Compiler gemacht hat

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


Angehängte Dateien:

Lesenswert?

Bronco schrieb:
>> Und ausserdem wird die vorgeschlagene Addition eines integers auf einen
>> std_logic_vector mit der numeric_std nicht funktionieren:
>>  vBitsInWord := vBitsInWord + 1;
> Doch, funktioniert, nachweislich! Ich hab mich auch gewundert...
Kann nicht sein. Und wie erwartet:
1
ERROR:HDLParsers:808 - "C:/..../sob.vhd" Line 39. + can not have such operands in this context.

Das bekomme ich mit diesen Zeilen:
1
process (ClkxCI) 
2
  variable vBitsInWord : std_logic_vector(2 downto 0);
3
begin
4
  if (rising_edge(ClkxCI)) then
5
    vBitsInWord := "000";
6
    for c in 0 to 3 loop
7
      if ( DataBufxD(c) = '1' ) then 
8
        vBitsInWord := vBitsInWord + 1;  --   Zeile 39
9
      end if;
10
    end loop;
11
    ReadyxSO <= vBitsInWord; 
12
  end if;
13
end process;

von Bronco (Gast)


Lesenswert?

R. K. schrieb:
> Mir gefällt die Lösung von Lothar Miller am besten, da ich die
> for-schleifen in einem VHDL-Code irgendwie "unpassend" finde. (so lange
> man diese vermeiden kann).

Sag doch sowas nicht!
"For" hat auch in VHDL seine Darseinberechtigung, um parallele Logik zu 
erzeugen.
Stell Dir mal vor, Dein Filter hätte 1024 Taps.
Willst Du 1024 einzelne Zeilen schreiben und das ganze nachher noch 
warten können?

von Bronco (Gast)


Lesenswert?

Lothar Miller schrieb:
>>> Und ausserdem wird die vorgeschlagene Addition eines integers auf einen
>>> std_logic_vector mit der numeric_std nicht funktionieren:
>>>  vBitsInWord := vBitsInWord + 1;
>> Doch, funktioniert, nachweislich! Ich hab mich auch gewundert...
> Kann nicht sein. Und wie erwartet:

Auch auf die Gefahr hin, jetzt geteert und gefedert zu werden.

In der entsprechenden Datei benutzen wir natürlich noch:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.STD_LOGIC_ARITH.ALL;
4
use IEEE.STD_LOGIC_UNSIGNED.ALL;

(Auf die historischen Gründe will ich hier gar nicht eingehen...)

von R. K. (uc_student)


Lesenswert?

Das mit der parallelen Logik ist wirklich ein gutes Argument. (Ich werde 
dies später sicher noch brauche, wenn ich die Dezimation etc. 
implementiere)

Wenn ich mir mein Programm so genau anschaue, dann würde ich die 
1-Bit-Quantisierung gerne mal ein wenig aufweichen. Meine Signal ist 
somit nicht mehr 1-Bit, sondern 4 Bit breit. Kann ich in meinem Programm 
die Vektoren durch Arrays ersetzen?
1
  
2
TYPE   DataBufxDS IS ARRAY(0 to 63) OF std_logic_vector(3 downto 0);

von R. K. (uc_student)


Lesenswert?

Ich war wohl ein wenig voreilig. Eine möglich Lösung lautet prinzipiell 
wie folgt: (der Ausgang mit einer Breite von 3 Bits macht jedoch altell 
noch keinen Sinn)
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use work.IPoTP_constants.all;
4
5
use IEEE.NUMERIC_STD.ALL;
6
  
7
entity sync_chip is
8
  port (
9
    ClkxCI    : in std_logic;
10
    RstxRBI    : in std_logic;
11
    DataInxDI  : in std_logic_vector (2 downto 0);
12
    MetrikxDO   : out std_logic_vector (3 downto 0)
13
  );
14
end sync_chip;
15
16
architecture rtl of sync_chip is  
17
  
18
  TYPE   DataBufxD IS ARRAY(0 to 64) OF std_logic_vector(3 downto 0);
19
  
20
  signal DataBufxDS : DataBufxD :=(others =>(others=>'0'));
21
begin
22
23
 FIR_ff : process(ClkxCI,RstxRBI)
24
   begin
25
    if RstxRBI = '1' then
26
      DataBufxDS <= (others =>(others=>'0'));
27
28
    elsif rising_edge (ClkxCI) then
29
      DataBufxDS(3 downto 1) <= DataBufxDS(2 downto 0);
30
      DataBufxDS(0)          <= DataInxDI;
31
    end if;  
32
 end process FIR_ff;
33
 
34
   
35
FIR_comb:  process  (DataBufxDS)
36
  variable tmp : integer range 0 to 127;
37
    begin
38
      for c in 0 to 63 loop
39
        tmp := tmp + to_integer(unsigned(DataBufxDS(c)));
40
      end loop;
41
      MetrikxDO <= std_logic_vector(to_unsigned(tmp,3)); 
42
     end process  FIR_comb ;
43
end rtl;

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


Lesenswert?

R. K. schrieb:
1
 FIR_comb:  process  (DataBufxDS)
2
   variable tmp : integer range 0 to 127;
3
     begin
4
       for c in 0 to 63 loop
5
         tmp := tmp + to_integer(unsigned(DataBufxDS(c)));
6
       end loop;
Ich würde zur Sicherheit vor die for-Schleife noch ein
 tmp := 0;
einfügen. Denn sonst könnte die Variable blitzschnell zur 
kombinatorischen Schleife ausarten:
1
Xst:2170 - Unit preamble_correlator : the following signal(s) form a combinatorial loop: tmp
Zum Hintergrund:
http://www.lothar-miller.de/s9y/categories/36-Kombinatorische-Schleife

Das Schlimme daran:
das wirst du bei einer Simulation nicht feststellen, weil die Änderdung 
von tmp die Neuberechnung des Prozesses anstossen sollte, das aber nicht 
kann, weil Variablen nicht in die Sensitivliste genommen werden können.
Siehe dazu den recht lesenswerten 
Beitrag "Variable vs Signal"
Mit dem Fazit: Keine Variablen für Anfänger.  ;-)

von R. K. (uc_student)


Angehängte Dateien:

Lesenswert?

Danke. Das mit dem tmp := 0; sehe ich aus deinem Argument ein.

Wenn ich jedoch mein RTL anschaue, bin ich mir  eigentlich ziemlich 
sicher, dass ich nach einem Reset einen definierten Zustand von Null 
haben sollte. Oder liege ich hier falsch?

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


Lesenswert?

R. K. schrieb:
> dass ich nach einem Reset einen definierten Zustand von Null
> haben sollte. Oder liege ich hier falsch?
Für die Simulation: nach den Reset hat ein integer den maximal negativen 
Wert (integer'left).
Für die Hardware: schnurzegal. Denn nach dem Loop-Unrolling hast du 
einfach nur 63 Addierer (mit einer entsprechend langen und langsamen 
Carry-Chain!!!).


Ich würde da eine "Buchführung" empfehlen: wenn du eine '1' 
hereinbekommst, dann zählst du einen Zähler eins hoch. Wenn du eine '1' 
hinausgibst, dann zählst du den Zähler runter. Wenn Eingang=Ausgang, 
dann tust du nichts. Ergebnis: nur zwei kleine Addierer = schnelles 
Design.

von R. K. (uc_student)


Lesenswert?

Das mit der langen und langsamen Logik ist mir klar. Für den gegebenen 
Fall mit dem Moving Average gebe ich dir natürlich recht, da hilft die 
Buchführung.
Ich werde später jedoch eine Korrelation bestimmen müssen. Das ganze 
führt dann auf eine FIR -Struktur, welch dank dem rein logischen 
Referenzsignal (also 1er und -1er) immerhin einfach implementierbar ist. 
Jedoch bleibt meine grosse Logik.

von J. S. (engineer) Benutzerseite


Lesenswert?

R. K. schrieb:
> da hilft die Buchführung.

Buchführung? Seit wann heisst das denn so? Zu meiner Zeit hiess das 
Integrator (?)

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.