Forum: FPGA, VHDL & Co. formell korrekte Summation von Einzelbits


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Herbert (Gast)


Lesenswert?

Wie addiere ich die einfallenden bits einer seriell gesampelten 
Schnittstelle so, dass ich die Summe der Bits bekomme, die 1 sind? Die 
bits, die einfallen, werden in einen seriellen Vektor input (15 downto 
0) geschoben. Ich möchte einen Mehrheitsentscheid machen bei 12 und 4, 
um eine Hysterese zu bekommen und habe folgendes hingeschrieben:
1
daten_summe <= std_logic_vector ( unsigned (empty_vector)
2
                                + unsigned (input(15 downto 15))
3
                                + unsigned (input(14 downto 14))
4
usw
5
                                + unsigned (input( 0 downto  0)) );

- der leere Vektor ist der Platzhalter für die Länge
- das "downto" habe ich einsetzen müssen, weil er ein einzelnes Bit 
nicht nimmt.

Obwohl es Phasen gibt, in denen bis zu 16 bits High sind, zählt die 
Summe nur bis 8 hoch. Es scheint einen versteckten ÜBerlauf zu geben, 
den ich mir nicht erklären kann, weil der empty_vector 6 downto 0 ist, 
also genug Platz hat. als der noch nicht drin war, addierte der 
Konstrukt nur bis maximal 3.

Ich bin mir bewusst, dass das kein sauberes VHDL ist, orientiere mich 
aber diesbezüglich an vorhandenem Code des Vorgängers.

Da es aber nicht funktionert, die Frage:

*****************************************************
Wie formuliert man dies am Schlauesten (und richtig)?
*****************************************************

Eine Flankenerkennung mit signed, bei denen die einen Bits abgezogen 
werden, funktioniert genau so (wenig): Es entstehen positive und 
negative Maxima, die die Flanken markieren, aber auch dort entsteht nur 
die halbe der maximalen Amplitude. Bei einer perfekten Flanke müsste 
aber 0*8 -1*8 = -8 rauskommen. Angezeigt werden aber maximal -4 und +4.

Ich habe auch probiert, vor den unsigned noch eine Null voranzustellen, 
also:

+ unsigned ('0' & input(15 downto 15)) und
+ unsigned ("0000" & input(14 downto 14))

aber ohne Verbesserung.

Simuliert wurde mit Vivado-SIM.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Kannst du mal den kompletten Code posten in dem man auch die Signal 
Deklaration sieht? Gerne auch als minimales Beispiel falls der Code 
etwas groesser ist.

Generell wuerde ich das Ganze mit einer for-Loop und einer if Abfrage 
loesen. Viel einfacher und besser lesbar, die Hauptarbeit nimmt dir eh 
das Synthese Werkzeug ab. Hier ein bisschen Inspiration:

https://vhdlguru.blogspot.com/2017/10/count-number-of-1s-in-binary-number.html

Ich wuerde direkt Variante 1 bevorzugen, sofern es Ressourcen und 
Geschwindigkeit erlauben.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Generell summiert man die  Bits einer seriellen Schnittstelle am Besten 
dann auf, wenn man jedes einzelne empfängt. Nicht erst danach, wenn alle 
empfangen sind.

Wenn das Kind aber schon im Brunnen liegt, dann sieh dir das an:
http://www.lothar-miller.de/s9y/archives/86-Einsen-im-Vektor-zaehlen.html

: Bearbeitet durch Moderator
von Herbert (Gast)


Lesenswert?

Tobias B. schrieb:
> Kannst du mal den kompletten Code posten in dem man auch die
> Signal Deklaration sieht?
Das ist zuviel Zeug zum posten, daher habe ich es ja herauskopiert. Die 
Signale sind alles std_logic.

Tobias B. schrieb:
> Generell wuerde ich das Ganze mit einer for-Loop und einer if Abfrage
> loesen.
Wie soll das gehen? Die Loop wird doch nur während der Synthese 
ausgeführt. Es müsste sich auch explizit hinschreiben lassen. Ausserdem 
müsste ich ja ERST wissen wie es aussieht, um es dann in den Loop packen 
zu können. Das spart ja nur Beschreibung.

Lothar M. schrieb:
> Generell summiert man die  Bits einer seriellen Schnittstelle am Besten
> dann auf

Das geht nicht weil durch die Summation ein FIR-Filter realisiert ist, 
dass mit jedem Bit neu ausgeführt wird. Also FIR (Bit 1 ... Bit 16), 
dann FIR (Bit 2 ... Bit 17), also müssen alle gespeichert werden.

Es muss doch möglich sein, eine VHDL Zeile zu schreiben, die 16 Werte 
summiert! Wenn ich normale Vektoren mit mehr, als einem Bit habe, kann 
ich das ja auch so schreiben. Es sind halt nur diesmal Vektoren mit der 
Länge 1.

-------------------------

Auf der Seite Lothars ersehe ich aber das Problem:

Man muss die Bits vor der Addition auf die komplette mögliche Breite 
des Ergebnissen aufziehen, um sie zu additionsfähigen Vektoren zu 
machen. Offenbar verschluckt er sonst Stellen. Der dort abgebildete Code 
sieht nämlich so aus:
1
  process (i) 
2
  variable cnt : unsigned (4 downto 0);
3
  begin
4
     cnt := "00000";
5
     for c in 0 to 15 loop
6
       cnt:=cnt+unsigned'("0000"&i(c)); 
7
     end loop;
8
     o <= std_logic_vector(cnt);
9
  end process;

Das werde ich ausprobieren!

Allerdings ist ja auch das ein "Rechnen mit Vektoren", was eigentlich 
vermieden werden soll. Meines Wissens geht das besser mit Integers / 
Naturals und im Bezug auf das Skalieren mit 'resize'. Allerdings habe 
ich es trotz Studium der Syntax damit nicht hinbekommen.

Wenn da noch jemand eine Idee hat ... gerne.

von Gustl B. (-gb-)


Lesenswert?

Herbert schrieb:
> Das geht nicht weil durch die Summation ein FIR-Filter realisiert ist,
> dass mit jedem Bit neu ausgeführt wird. Also FIR (Bit 1 ... Bit 16),
> dann FIR (Bit 2 ... Bit 17), also müssen alle gespeichert werden.
>
> Es muss doch möglich sein, eine VHDL Zeile zu schreiben, die 16 Werte
> summiert!

Du bekommst die Bits seriell rein? Z. B. von einem Mikrophon das die als 
PDM ausgibt? Du willst die dann tiefpassfiltern mit einem einfachen 
gleitenden Mittelwert über z. B. 16 Bits?

Dann nehme ein Schieberegister der Länge 16 und eine Summe mit 4 Bits.
Schiebe in jedem Takt.
Addiere in jedem Takt das neu ankommende Bit und subtrahiere das letzte 
Bit aus dem Schieberegister.
1
signal sr: std_logic_vector(15 downto 0);
2
signal summe: unsigned(3 downto 0);
3
4
begin
5
6
process begin
7
   wait until rising_edge(clk);
8
   sr <= sr(14 downto 0) & BIT_NEU;
9
   summe <= summe - unsigned("000" & sr(15)) + unsigned("000" & BIT_NEU);
10
end process;

Du könntest deinen 1 Bit Eingang aber auch einfach als std_logic_vector 
deklarieren.

BIT_NEU : in std_logic_vector(0 downto 0);

Dann kannst du das intern direkt als unsigned interpretieren lassen:

... + unsigned(BIT_NEU);

Alternativ kannst du das Bit auch als std_logic lassen und quasi für 
eine Entscheidung verwenden:

if BIT_NEU = '1' then
   summe <= summe +1;
end if;

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Herbert schrieb:
> Es muss doch möglich sein, eine VHDL Zeile zu schreiben, die 16 Werte
> summiert!
Warum "muss" es das? Warum meinst du, dass 1 einzige Zeile effizienter 
umgesetzt wird als 10 Zeilen?
Schreib halt eine Funktion countones() und füttere die mit dem Vektor.
Dann steht da summe <= countones(input);

> Allerdings ist ja auch das ein "Rechnen mit Vektoren", was eigentlich
> vermieden werden soll.
Juckt doch nicht. Der Overhead wird sowieso wegoptimert.

> Das geht nicht weil durch die Summation ein FIR-Filter realisiert ist,
> dass mit jedem Bit neu ausgeführt wird.
> Also FIR (Bit 1 ... Bit 16), dann FIR (Bit 2 ... Bit 17)
Bei dieser Rechnung muss ich doch nur das erste und das letzte Bit 
bearbeiten (z.B. einen "Einser-Zähler" inkrementieren bzw. 
dekrementieren), die in der Mitte bleiben ja gleich und ändern die Summe 
nicht.

> also müssen alle gespeichert werden.
Sehe ich zwar anders, aber du hast das Modul in ganzer Pracht vor dir 
und hast das sicher untersucht.

von Herbert (Gast)


Lesenswert?

Lothar M. schrieb:
> ei dieser Rechnung muss ich doch nur das erste und das letzte Bit
> bearbeiten (z.B. einen "Einser-Zähler" inkrementieren bzw.
> dekrementieren), die in der Mitte bleiben ja gleich und ändern die Summe
> nicht.

Es steckt aber noch ein spike Filter drin, eine Reinterpreation der 
doppelten Bits aufgrund eines zu hohen Sampletaktes und mehr. Man kann 
es NICHT einfach nur durch zählen machen, sonst täte ich das ja. Die 
Schaltung die das zuvor erledigte, war sogar ein CIC.

Genau deshalb geht auch gustels Vorschlag nicht. Ich habe es nun drin 
und es geht. Danke.

von Christoph Z. (christophz)


Lesenswert?

Herbert schrieb:
> Tobias B. schrieb:
>> Generell wuerde ich das Ganze mit einer for-Loop und einer if Abfrage
>> loesen.
> Wie soll das gehen? Die Loop wird doch nur während der Synthese
> ausgeführt.

Das ist synthesefähig solange die Teile innerhalb der Loop nicht 
Ergebnisse der vorhergehenden Iteration benötigen (Unabhängig sind). Der 
Synthesizer macht einfach eine loop-unroll.

> Das spart ja nur Beschreibung.

Richtig erkannt.

von Jürgen S. (engineer) Benutzerseite


Lesenswert?

Lothar M. schrieb:
>> Das geht nicht weil durch die Summation ein FIR-Filter realisiert ist,
>> dass mit jedem Bit neu ausgeführt wird.
>> Also FIR (Bit 1 ... Bit 16), dann FIR (Bit 2 ... Bit 17)
> Bei dieser Rechnung muss ich doch nur das erste und das letzte Bit
> bearbeiten (z.B. einen "Einser-Zähler" inkrementieren bzw.
> dekrementieren), die in der Mitte bleiben ja gleich und ändern die Summe
> nicht.

Damit würden aber Werte am Rand genau so bewertet wie jene in der Mitte 
der Schlange. Bei einem echten FIR ist das nicht der Fall. Hier offenbar 
schon, da gleitender Mittelwert, wenn ich das richtig interpretiere. 
Gleichwohl muss man sich alle Werte der Vergangenheit speichern, um 
jeweils zu wissen, welcher abzuziehen ist.

Wenn es dann kein bewertender Filter ist, wäre natürlich auch so lösbar, 
wie beim CIC, indem nur Summiert und abgetastet wird. Durch die 
Differenzbildung der jeweils letzten Samples wird die komplette Historie 
mit eliminiert.

Die eigentliche Frage des TE würde ich so lösen, dass die Bits in einen 
Vektor verwandelt werden und vor der Rechnung mit resize angepasst 
werden. Das ist der formell korrekte Weg. Führt aber zu keinem anderen 
Ergebnis :-)

von Gustl B. (-gb-)


Lesenswert?

Jürgen S. schrieb:
> Damit würden aber Werte am Rand genau so bewertet wie jene in der Mitte
> der Schlange. Bei einem echten FIR ist das nicht der Fall. Hier offenbar
> schon, da gleitender Mittelwert, wenn ich das richtig interpretiere.

Doch, das ist ein echter FIR. Nur haben eben alle Koeffizienten den 
gleichen Wert. Das Verhalten ist also eher schlecht, aber man spart sich 
Multiplikationen.

Statt nur 1sen zu summieren könnte man ja auch aus jeder 1 einen Wert 
mit z. B. 8 Bits machen, also alle Bits mit 1 füllen. Und dann könnte 
man auch einen besseren FIR mit Multiplikationen rechnen. Ist wohl eine 
Abwägung.

von Jürgen S. (engineer) Benutzerseite


Lesenswert?

Gustl B. schrieb:
> Doch, das ist ein echter FIR. Nur haben eben alle Koeffizienten den
> gleichen Wert.

Formell ja, aber ich hatte natürlich einen FIR mit individuellen Koeffs 
im Sinn.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.