Forum: FPGA, VHDL & Co. Gleichspannungsanteil von Signal entfernen-VHDL


von Mark W. (kram) Benutzerseite


Lesenswert?

Hallo,

ich moechte von einem Signal den Gleichanteil entfernen. Also im Prinzip 
einen Hochpassfilter mit sehr tiefer Grenzfrequenz.
Sollte kein Komplexer Filter sein, irgendwas einfaches genuegt.

Bei folgendem Code bekomme ich irgendwie kein Augangssignal.
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
6
entity DC_BLOCK is
7
Port (   DATA_IN   : in std_logic_vector(13 downto 0); --signed
8
    CLK     : in std_logic;
9
    RESET     : in std_logic;
10
    DATA_OUT   : out std_logic_vector(13 downto 0) --signed
11
  );
12
end DC_BLOCK;
13
14
architecture ARCH of DC_BLOCK is
15
16
signal ERROR       : unsigned(6 downto 0);
17
signal DATA_IN_DELAY   : signed(13 downto 0);
18
signal DATA_OUT_TEMP   : signed(13 downto 0);
19
20
constant MAX : signed(22 downto 0) := "00011111111111111111111";
21
constant MIN : signed(22 downto 0) := "11100000000000000000000";
22
23
begin
24
25
-- Single pole, high-pass filter
26
27
DATA_OUT <= std_logic_vector(DATA_OUT_TEMP);
28
29
DC_BLOCK_PROCESS : process (CLK, RESET)
30
31
variable DIFF : signed(20 downto 0);
32
variable PROD : signed(20 downto 0);
33
variable OUT_SCALED: signed(22 downto 0);
34
35
begin
36
    if (RESET = '1') then
37
    ERROR <= "0000000";
38
        DATA_IN_DELAY   <= "00000000000000";
39
        DATA_OUT_TEMP   <= "00000000000000";
40
    elsif rising_edge (CLK) then
41
    DATA_IN_DELAY <= signed(DATA_IN);
42
        DIFF := (signed(DATA_IN) - DATA_IN_DELAY) & "0000000"; -- scale data up by 128 because pole is scaled by 128
43
44
    -- multiply by pole value of 127
45
        PROD := DATA_OUT_TEMP & "0000000"; -- multiply by 128
46
        PROD := PROD - DATA_OUT_TEMP; -- subtract data_out_temp to produce a multiply of 127
47
48
        OUT_SCALED := (PROD(20) & PROD(20) & PROD) + DIFF;
49
        OUT_SCALED := OUT_SCALED + signed('0' & ERROR);
50
51
    -- saturate on overflow
52
    if (OUT_SCALED > MAX) then
53
      OUT_SCALED := MAX;
54
    elsif (OUT_SCALED < MIN) then
55
      OUT_SCALED := MIN;
56
        else
57
      OUT_SCALED := OUT_SCALED;
58
    end if;
59
60
    ERROR <= unsigned(OUT_SCALED(6 downto 0)); -- keep fractional part by taking remainder of integer division
61
        DATA_OUT_TEMP <= OUT_SCALED(20 downto 7); -- divide by 128
62
  end if;
63
end process DC_BLOCK_PROCESS;
64
65
end ARCH;

: Bearbeitet durch User
von Urs Urbach (Gast)


Lesenswert?

Schreib mal formelmäßig hin, was du da berechnen willst. Mir knotet sich 
das Hirn zusammen, wenn da beim Nullsetzen von 7 lsb der Kommentar 
"multiply with 128" steht.

Und schon mal ne Simulation aufsetzen um Waves hier als 
Zwischenergebniss einzustellen.

Sussignale und variablen bitte im Namen unterscheidbar machen, also alle 
Signale beginnen mit s_  und alle Variablen mit v_

So ist es reichlich schwer die tatsächlichen aktuellen Wertzuweisungen, 
von denen im nächsten Taktzyklus leicht zuunterscheiden.

von -gb- (Gast)


Lesenswert?

Urs Urbach schrieb:
> Mir knotet sich das Hirn zusammen, wenn da beim Nullsetzen von 7 lsb der
> Kommentar "multiply with 128" steht.

Nein, er hängt 7 Nullen an. Das ist tatsächlich mal 128.

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


Lesenswert?

Mark W. schrieb:
> Bei folgendem Code
Hast du den selber geschrieben oder irgendwie irgendwo gefunden?

> bekomme ich irgendwie kein Augangssignal.
Sondern was stattdessen? Und wie stellst du das fest?

von Urs Urbach (Gast)


Lesenswert?

-gb- schrieb:
> Urs Urbach schrieb:
>> Mir knotet sich das Hirn zusammen, wenn da beim Nullsetzen von 7 lsb der
>> Kommentar "multiply with 128" steht.
>
> Nein, er hängt 7 Nullen an. Das ist tatsächlich mal 128.

Nein ist es nicht, Multiplikation wird (in VHDL zu 99,9%) mit Shift 
realisiert. Ich seh da in der kommentierten Zeile kein Shift.

von Samuel C. (neoexacun)


Lesenswert?

Urs Urbach schrieb:
> Nein ist es nicht, Multiplikation wird (in VHDL zu 99,9%) mit Shift
> realisiert. Ich seh da in der kommentierten Zeile kein Shift.

Dann würde mich mal interessieren, wie die 99,9% Shifts in VHDL denn 
aussehen sollten.

von Urs Urbach (Gast)


Lesenswert?

Samuel C. schrieb:
> Urs Urbach schrieb:
>> Nein ist es nicht, Multiplikation wird (in VHDL zu 99,9%) mit Shift
>> realisiert. Ich seh da in der kommentierten Zeile kein Shift.
>
> Dann würde mich mal interessieren, wie die 99,9% Shifts in VHDL denn
> aussehen sollten.

google mit keyword "vhdl shift register"

von Oli (Gast)


Lesenswert?

-gb- schrieb:
> Nein, er hängt 7 Nullen an. Das ist tatsächlich mal 128.

Ja, tut er, allerdings glaube ich, dass es insgesamt mit der Schieberei 
nicht zu passen scheint. Da scheinen unterschiedliche Skaliserungen zu 
wirken. Solange sich der TE aber weigert, dieser Aufforderung 
nachzukommen:

Urs Urbach schrieb:
> Schreib mal formelmäßig hin, was du da berechnen willst. Mir knotet sich
> das Hirn zusammen,

wird das undurchsichtig bleiben.

Die Thematik hier ist auch eher ein Rundungs-/Rechenproblem. Hat mit 
VHDL nix am tun.

Der TE wird darüber hinaus auch noch die Frage zu lösen haben, welche 
Bandbreite sein Superfilter haben soll, um "Gleichspannung" zu 
unterdrücken. Das ist - wie wir alle wissen - eine Definitionsfrage.

Einen minimalen Offset kriegt man mit dem tieffrequentesten Filter nicht 
wech ....

von Mark W. (kram) Benutzerseite


Angehängte Dateien:

Lesenswert?

Anbei die Simulation.

Den Code habe ich aus dem Internet geladen und wollte ihn mal 
ausprobieren und sehen wie sich das verhaelt. Das einzige was ich so 
gefunden habe.

Also mathematisch will ich das arithmetische Mittel vom Signal abziehen. 
Also erst arithmetisches Mittel berechnen und dann vom Eingangssignal 
abziehen.
Oder anders ausgedrueckt eine Hochpassfilter.

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Oli schrieb:
> Ja, tut er, allerdings glaube ich, dass es insgesamt mit der Schieberei
> nicht zu passen scheint.

Habe ich auch nicht behauptet. Ich habe nur geschrieben, dass da 
wirklich mit 128 multipliziert wird.

Aber wo wir schon dabei sind, in einem anderen Thread, dem hier 
Beitrag "FIR parallel und seriell braucht zu viele LUTs." , wurde mir von Dergute W. 
(derguteweka) dieser DC Remover gezeigt:
https://www.dsprelated.com/showarticle/58.php

Das habe ich versucht nachzubauen und den ganz einfachen aus Figure 1b 
habe ich auch hinbekommen, aber den aus Figure 3a leider nicht. Aber ich 
habe jetzt hier mal Code hochgeladen, vielleicht hilft das ja.

von Samuel C. (neoexacun)


Lesenswert?

Urs Urbach schrieb:
> google mit keyword "vhdl shift register"

Hm, mit diesem Suchbegriff kann ich leider nichts besseres finden. Bitte 
erleuchte mich doch mit einer konkreten Quelle, anstatt irgendetwas vor 
dich hinzuschwurbeln.

von Klaus (Gast)


Lesenswert?

Hallo,

man sieht nicht viel von deiner Simulation. Hast du einen Clock?

Klaus

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Meine Testbench aus oben sweept einen Sinus durch. Hier mal eine 
korrigiertere Version von meinem DC Remover, aber sieht immer noch nicht 
perfekt aus.

von Mark W. (kram) Benutzerseite


Lesenswert?

Klaus schrieb:
> Hallo,
>
> man sieht nicht viel von deiner Simulation. Hast du einen Clock?
>
Ja, ganz oben. 50 MHz. Ist nur etwas zusammengestaucht.

von Mark W. (kram) Benutzerseite


Lesenswert?

Gustl B. schrieb:
> Oli schrieb:
>> Ja, tut er, allerdings glaube ich, dass es insgesamt mit der Schieberei
>> nicht zu passen scheint.
>
> Habe ich auch nicht behauptet. Ich habe nur geschrieben, dass da
> wirklich mit 128 multipliziert wird.
>
> Aber wo wir schon dabei sind, in einem anderen Thread, dem hier
> Beitrag "FIR parallel und seriell braucht zu viele LUTs." , wurde mir von Dergute W.
> (derguteweka) dieser DC Remover gezeigt:
> https://www.dsprelated.com/showarticle/58.php
>
> Das habe ich versucht nachzubauen und den ganz einfachen aus Figure 1b
> habe ich auch hinbekommen, aber den aus Figure 3a leider nicht. Aber ich
> habe jetzt hier mal Code hochgeladen, vielleicht hilft das ja.

Danke, das sieht gut aus. Sowas habe ich gesucht, gleich mit Erklaerung.

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


Lesenswert?

Mark W. schrieb:
> Den Code habe ich aus dem Internet geladen und wollte ihn mal
> ausprobieren und sehen wie sich das verhaelt.
Welche Warnungen bekommst du denn, wenn du die Simulation startest? Und 
wer macht dir da im Ergebins ein "X" für ein "U" vor?

> Das einzige was ich so gefunden habe.
Nun gut, jetzt musst du ihn im Simulator einfach mal debuggen, und 
schauen, wo es klemmt, dass du als Ergebnis nur "X" bekommst. Der erste 
Schritt wäre nun, dass du untersuchst, was dieses "X" überhaupt 
bedeutet. Als Tipp: du kannst auch interne Signale deines Moduls ansehen 
und kontrollieren, ob die Zuweisungen wie gewünscht erfolgen.

> Anbei die Simulation.
Häng doch deine Testbench auch mal an, dann kann man leichter 
nachvollziehen, was du da machst.

von -gb- (Gast)


Lesenswert?

Die testbench geht trivial. Einen konstanten wert ungleich Null anlegen 
und zumindest nach etwas Zeit sollte aus dem filter null rauskommen.

Der einfachste filter ist also die Multiplikation mit Null. Ist aber 
doof.
Sehr einfach ist dann noch ein beliebig langes fir filter bei dem die 
Summe der Koeffizienten null ergibt. Im einfachsten Fall +1 und -1.

von Achim S. (Gast)


Lesenswert?

-gb- schrieb:
> Die testbench geht trivial.

Aber anhand der Testbench des TO ließe sich ggf. der Fehler erklären, 
der zu den Xen in seiner Simulation führt. Wahrscheinlich nutzt er 
Data_Out einerseits als Ausgang der simulierten DC_Block Komponente und 
macht andererseits parallel dazu eine Zuweisung auf Data_Out in seiner 
Testbench. Diese multiple drivers führen zu den beobachteten X im 
Simulationsergebnis.

von Duke Scarring (Gast)


Angehängte Dateien:

Lesenswert?

Mark W. schrieb:
> ich moechte von einem Signal den Gleichanteil entfernen. Also im Prinzip
> einen Hochpassfilter mit sehr tiefer Grenzfrequenz.
> Sollte kein Komplexer Filter sein, irgendwas einfaches genuegt.

Reicht das ?!?:
Tiefpass = (neuer Wert + alter Wert) / 2
Hochpass = (neuer Wert - alter Wert) / 2
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
6
entity filter is
7
    port
8
    (
9
        clk         : in    std_ulogic;
10
        --
11
        data_in     : in    signed( 7 downto 0); 
12
        data_out    : out   signed( 7 downto 0)
13
    );
14
end entity filter;
15
16
17
architecture rtl of filter is
18
19
    signal buf      : signed( 8 downto 0)   := ( others => '0');
20
21
begin
22
23
    process
24
    begin
25
        wait until rising_edge( clk);
26
27
        data_out    <= resize( (data_in - buf) / 2, 8);
28
        buf         <= resize( data_in, 9);
29
    end process;
30
31
end architecture rtl;

Duke

von Mark W. (kram) Benutzerseite


Angehängte Dateien:

Lesenswert?

Lothar M. schrieb:
> Mark W. schrieb:
>> Den Code habe ich aus dem Internet geladen und wollte ihn mal
>> ausprobieren und sehen wie sich das verhaelt.
> Welche Warnungen bekommst du denn, wenn du die Simulation startest? Und
> wer macht dir da im Ergebins ein "X" für ein "U" vor?
>
>> Das einzige was ich so gefunden habe.
> Nun gut, jetzt musst du ihn im Simulator einfach mal debuggen, und
> schauen, wo es klemmt, dass du als Ergebnis nur "X" bekommst. Der erste
> Schritt wäre nun, dass du untersuchst, was dieses "X" überhaupt
> bedeutet. Als Tipp: du kannst auch interne Signale deines Moduls ansehen
> und kontrollieren, ob die Zuweisungen wie gewünscht erfolgen.
>
>> Anbei die Simulation.
> Häng doch deine Testbench auch mal an, dann kann man leichter
> nachvollziehen, was du da machst.

Hier die Fehlermeldung:
# KERNEL: WARNING: NUMERIC_STD.">": metavalue detected, returning FALSE
# KERNEL: Time: 10 ns,  Iteration: 1,  Instance: /testbench/uut, 
Process: dcBlockProc.

Davon gibt es jede Menge, alle 10ns die gleiche Meldung.

Anbei die beiden relevanten Dateien. Ich habe jetzt die TB angepasst, 
sodass ich signed als Ein- und Ausgang behalten kann. Vorher hatte ich 
es auf std_logic_vector umgewandelt.

Hat es etwas mit dem Einbinden bestimmter Bibliotheken zu tun? Die ware 
naemlich nicht dabei.

von Mark W. (kram) Benutzerseite


Lesenswert?

Duke Scarring schrieb:
> Mark W. schrieb:
>> ich moechte von einem Signal den Gleichanteil entfernen. Also im Prinzip
>> einen Hochpassfilter mit sehr tiefer Grenzfrequenz.
>> Sollte kein Komplexer Filter sein, irgendwas einfaches genuegt.
>
> Reicht das ?!?:
> Tiefpass = (neuer Wert + alter Wert) / 2
> Hochpass = (neuer Wert - alter Wert) / 2
>
Oh, danke. Das hoert sich fuer mich vielversprechend an.
Jetzt habe ich "Futter" fuer die naechsten Tage. :-)
Auch das von Gustl, wo ich gerade dran bin.

von Oli (Gast)


Lesenswert?

Mark W. schrieb:
> Also mathematisch will ich das arithmetische Mittel vom Signal abziehen.
> Also erst arithmetisches Mittel berechnen und dann vom Eingangssignal
> abziehen.

Denkanstoss:

Das mathematische Mittel über welchen Zeitraum?
Die letzten 7 Samples? Oder die letzten 7000?

von Mark W. (kram) Benutzerseite


Lesenswert?

Oli schrieb:
> Mark W. schrieb:
>> Also mathematisch will ich das arithmetische Mittel vom Signal abziehen.
>> Also erst arithmetisches Mittel berechnen und dann vom Eingangssignal
>> abziehen.
>
> Denkanstoss:
>
> Das mathematische Mittel über welchen Zeitraum?
> Die letzten 7 Samples? Oder die letzten 7000?

Ja, guter Tip. Hatte ich mir auch schon ueberlegt. Vermutlich je mehr 
Samples man nimmt, desto besser wird es funktionieren.

Ich weiss aber noch nicht genau, wie ich es am Ende einbinde.
Ich bekomme staendig ADC Daten(40MSPS). Auf jeweils 4000 Samples muesste 
es angewendet werden. Zusammen mit noch anderen Auswertungen.

Oder ich binde es irgendwo on-the-fly ein. Weit vorne, am Besten gleich 
nach dem ADC.

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


Lesenswert?

Mark W. schrieb:
> Hier die Fehlermeldung:
> # KERNEL: WARNING: NUMERIC_STD.">": metavalue detected, returning FALSE
> # KERNEL: Time: 10 ns,  Iteration: 1,  Instance: /testbench/uut,
> Process: dcBlockProc.
Irgendwie logisch, wenn man davon ausgeht, dass die lokalen Signale zu 
Beginn 'U' sind und damit dann gerechnet wird.
Ich würde denen mal Defautlwerte geben:
1
signal error : unsigned(6 downto 0) := (others=>'0');
2
signal data_in_dly : signed(13 downto 0) := (others=>'0');
3
signal dat_out : signed(13 downto 0) := (others=>'0');
Genau das kann man aber im Simluator einfach herausfinden...

Mark W. schrieb:
> Ich bekomme staendig ADC Daten(40MSPS).
> Auf jeweils 4000 Samples muesste es angewendet werden.
Und zwar durchlaufend? Also vom Sample 0..3999, dann von 1..4000, dann 
von 2..4001, dann von 3..4002 usw.?

Oder darfst du da willkürlich 4000 Samples aus dem Datenstrom 
"herausschneiden" und darauf herumhampeln?

Mark W. schrieb:
> Also mathematisch will ich das arithmetische Mittel vom Signal abziehen.
Für das "richtige" artihmetische Mittel musst du aber die letzten 4000 
werte speichern, um immer den ältesten aus der Summe entfernen zu 
können.

Einfacher wäre da ein gleitender PT1-Mittelwert, bei dem der Mittelwert 
aus der Summe abgezogen wird. So wie z.B. dort: 
http://www.lothar-miller.de/s9y/categories/58-Filter


BTW: nimm die nächstliegende Zweierpotenz als Puffergröße. Das macht die 
Hardware unheimlich viel einfacher und schneller. In deinem Fall wären 
das 4096.

von Oli (Gast)


Lesenswert?

Mark W. schrieb:
> Ich bekomme staendig ADC Daten(40MSPS). Auf jeweils 4000 Samples muesste
> es angewendet werden

Macht eine Grenzfrequenz von ca. 10kHz bei einem FIR.
5kHz sind dann schon Gleichstrom?

von Mark W. (kram) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Irgendwie logisch, wenn man davon ausgeht, dass die lokalen Signale zu
> Beginn 'U' sind und damit dann gerechnet wird.
> Ich würde denen mal Defautlwerte geben:
>
1
> signal error : unsigned(6 downto 0) := (others=>'0');
2
> signal data_in_dly : signed(13 downto 0) := (others=>'0');
3
> signal dat_out : signed(13 downto 0) := (others=>'0');
4
>
> Genau das kann man aber im Simluator einfach herausfinden...

Damit gehts. :-) Bin halt noch Anfaenger.

> Mark W. schrieb:
>> Ich bekomme staendig ADC Daten(40MSPS).
>> Auf jeweils 4000 Samples muesste es angewendet werden.
> Und zwar durchlaufend? Also vom Sample 0..3999, dann von 1..4000, dann
> von 2..4001, dann von 3..4002 usw.?
>
> Oder darfst du da willkürlich 4000 Samples aus dem Datenstrom
> "herausschneiden" und darauf herumhampeln?

Ja. Also 1...4000, 4001...8000, 8001...12000 usw.
> Mark W. schrieb:
>> Also mathematisch will ich das arithmetische Mittel vom Signal abziehen.
> Für das "richtige" artihmetische Mittel musst du aber die letzten 4000
> werte speichern, um immer den ältesten aus der Summe entfernen zu
> können.
>
> Einfacher wäre da ein gleitender PT1-Mittelwert, bei dem der Mittelwert
> aus der Summe abgezogen wird. So wie z.B. dort:
> http://www.lothar-miller.de/s9y/categories/58-Filter
>
>
> BTW: nimm die nächstliegende Zweierpotenz als Puffergröße. Das macht die
> Hardware unheimlich viel einfacher und schneller. In deinem Fall wären
> das 4096.
Schau ich mir mal an.

von Mark W. (kram) Benutzerseite


Lesenswert?

Oli schrieb:
> Mark W. schrieb:
>> Ich bekomme staendig ADC Daten(40MSPS). Auf jeweils 4000 Samples muesste
>> es angewendet werden
>
> Macht eine Grenzfrequenz von ca. 10kHz bei einem FIR.
> 5kHz sind dann schon Gleichstrom?
Es waere schon toll, wenn es bis 500Hz runter gehen wurde.

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


Lesenswert?

Mark W. schrieb:
> Ja. Also 1...4000, 4001...8000, 8001...12000 usw.
Wer hat diese sehr ungünstige "Filterlänge" definiert?

> Es waere schon toll, wenn es bis 500Hz runter gehen wurde.
Definieren "runter gehen": so, dass 500Hz als Offset herausgefiltert 
werden, oder so, dass 500Hz noch nicht als Offset weggefiltert werden?
Denn der Hinweis war, dass der 4000 Worte lange Filter Frequenzen 
unterhalb 5kHz als "Gleichwert" angesieht und "herausfiltert".

von Mark W. (kram) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Mark W. schrieb:
>> Ja. Also 1...4000, 4001...8000, 8001...12000 usw.
> Wer hat diese sehr ungünstige "Filterlänge" definiert?

Ist der Bereich fuer Echosignale. Also sind immer so 60 bis 150 us. 
Koennen auch nicht beliebig verlangert werden, weil dann schon der 
naechste "Schuss" kommt. Aber 4096 waeren ok.

>> Es waere schon toll, wenn es bis 500Hz runter gehen wurde.
> Definieren "runter gehen": so, dass 500Hz als Offset herausgefiltert
> werden, oder so, dass 500Hz noch nicht als Offset weggefiltert werden?
> Denn der Hinweis war, dass der 4000 Worte lange Filter Frequenzen
> unterhalb 5kHz als "Gleichwert" angesieht und "herausfiltert".
Also es geht wirklich nur um DC. Ich glaube es ist schnuppe, ob der 
Hochpassfilter die Grenzfrequenz bei 1kHz oder 500Hz hat. Nutsignale 
sind bei 5 Mhz, das sollte nur nicht bedaempft werden.

von Oli (Gast)


Lesenswert?

Mark W. schrieb:
> Also es geht wirklich nur um DC. Ich glaube es ist schnuppe, ob der
> Hochpassfilter die Grenzfrequenz bei 1kHz oder 500Hz hat.

Sicher nicht. Denn so simple Filter haben flache Flanken und damit ihre 
Wirkung starke Ausläufer. Ein einfache Mean, um ihn abzuziehn hat 
Oberwellen gemäß Rechteck-Fourier.

Nimm mal lieber ein IIR-Bandfilter um 5 MHz mit links und rechts 2 
Oktaven, oder besser 3. Wenn dein Nutzsignal nicht Sinus ist, dann 
entsprechen mehr.

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.