Mittelwertfilter

OP #3138489
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;
Moderator (Firma: Titel) Persönliche Seite #3138530
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...  ;-)
Gast #3138535
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
Gast #3138540
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.
Moderator (Firma: Titel) Persönliche Seite #3138545
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;
Moderator (Firma: Titel) Persönliche Seite #3138590
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.
Gast #3138591
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?
Gast #3138602
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...
OP #3138608
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
Moderator (Firma: Titel) Persönliche Seite #3138615
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;
Angehängte Dateien:
Gast #3138616
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?
Gast #3138620
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...)
OP #3138631
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);
OP #3138674
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;
Moderator (Firma: Titel) Persönliche Seite #3138739
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.  ;-)
Moderator (Firma: Titel) Persönliche Seite #3138780
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.
OP #3138814
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren