www.mikrocontroller.net

Forum: FPGA, VHDL & Co. Ratschlag / Verbesserung zu vhdl-Progrämmchen


Autor: Hartmut K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen!

#ACHTUNG: längerer Post.


Ich experimentiere im Moment mit einem xc9572 cpld. In diesem Post geht 
es um ein kleines Programm zu dem ich gerne Meinungen, 
Verbesserungsvorschläge und allgemeine vhdl Tips möchte.


Auf der Platine ist das cpld, 8 Dioden, 2 7-Segmentanzeigen die paralles 
an I/O Pins hängen und jeweils von einem Transistor 'scharf' geschaltet 
werden. Die Transistoren haben wieder 2 I/Os. Außerdem habe ich 2 
taster, die mit einem 74er entprellt sind um mir da Code zu sparen.

Das Programm soll einfach die 1 je nach Taste nach links oder rechts 
schieben und dan Inhalt des Schieberegisters an den Dioden ausgeben 
sowie an den (beiden zunächst) 7-Segmentanzeigen darstellen, welche 
Diode eigentlich an ist.

So weit so gut. Das Programm tut auch was es soll, aber ich habe das 
Gefühl das das nicht der Weisheit letzter Schluss ist. Deshalb einige 
Fragen an euch:

- Wie würde man die Aufgabe anders (richtiger?) programmieren?

- Ist es bei vhdl analog zu C möglich so umständlichen Code zu 
produzieren, daß weniger performante Hardware herauskommt oder massig 
Ressourcen verschwendet werden? Oder sind die Optimierer tatsächlich in 
der Lage aus jedem Gurkencode die selbe Hardware zu machen?

- wie strukturiert man eigentlich ein vhdl Programm. Also analog zu : in 
C main-Schleife mit zyklischen Aufgaben und ggf. Interrupts die den 
Ablauf beeinflussen.

-würde man sinnvoll eine eigene entity 7-Segment Ausgabe programmieren 
und die in einem Programm als component verwenden? Ich überblicke gerade 
nicht, wie das aussehen würde, aber das component Konzept ist mir im 
Prinzip bekannt. Da müsste man die Ausgabe entity in der architecture 
des "Hauptprogrammes" an gemeinsame Signale Knoten?

-wirklich viel scheint man in ein cpld nicht zu bekommen? Der Bericht 
sagte mir im Grunde, daß mit dem bischen schon 30% voll sind. Oder ist 
das eine proggrammierunsfrage (s.o.)?

-wird man in einem reaken etwas komplexeren Design nicht wahnsinnig vor 
internen Signalen? Ich habe das Gefühl daß ich zu fast jedem 
Ausgangssignal irgendwo ein internes Signal habe und das bei components 
noch schlimmer würde?


Damit auch alle wissen, was ich verbrochen habe und worüber die Fragen 
sind der Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity test1_code is
    Port ( D     : out STD_LOGIC_VECTOR (7 downto 0);          --LED Zeile
        DISP   : out STD_LOGIC_VECTOR (6 downto 0) := "0000000";  --7-Segment Anzeige
        DISP_L : out STD_LOGIC;                        --en links
        DISP_R : out STD_LOGIC;                        --en rechts 
        clk    : in  STD_LOGIC;                        --Global clk
           T     : in  STD_LOGIC;                         --Taste links
        T2     : in  STD_LOGIC);                        --Taste rechts
end test1_code;

architecture Behavioral of test1_code is
  signal D_int : STD_LOGIC_VECTOR (7 downto 0):="00000001";
  signal T_old :STD_LOGIC;
  signal T2_old:STD_LOGIC;
  signal toggleDisp:STD_LOGIC;
begin
  
  main_clock: process(clk)
  begin
    if rising_edge(clk) then
      if (T='1' and T_old='0') then      
        D_int <= (D_int(6 downto 0) & D_int(7));            --nach links schieben
      elsif (T2='1' and T2_old='0') then
        D_int <= ( D_int(0) & D_int(7 downto 1));            --nach rechts schieben
      end if;          
      T_old <= T;
      T2_old<= T2;
      toggleDisp <= not toggleDisp;                      --aktives 7-Seg wechseln
    end if;
  end process main_clock;
  
  DISP_L <= toggleDisp;                              --entweder links an
  DISP_R <= not toggleDisp;                            --oder rechts
  D<=D_int;                                      --Schiebestand an Diodenreihe 

  with D_int select                                  --Schiebestand an die Segmente
    DISP <= "1111001" when "00000001",
            "0100100" when "00000010",
            "0110000" when "00000100",
          "0011001" when "00001000",
          "0010010" when "00010000",
          "0000011" when "00100000",
          "1111000" when "01000000",
            "0000000" when "10000000",
          "1111111" when others;

end Behavioral;

Autor: Klaus Falser (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> - Wie würde man die Aufgabe anders (richtiger?) programmieren?
Richtiger ist bei einem Programm dieser Größenordnung Geschmackssache, 
aber man könnte es  z.B. strukturieren, eben mit Untermodule, die nur 
eine bestimmte Funktion haben, z.B. Schieberegister, 7-Segment-Anzeige 
usw.

> - Ist es bei vhdl analog zu C möglich so umständlichen Code zu
> produzieren, daß weniger performante Hardware herauskommt oder massig
> Ressourcen verschwendet werden? Oder sind die Optimierer tatsächlich in
> der Lage aus jedem Gurkencode die selbe Hardware zu machen?
Jain.
Die Verwendung von Struktur und Untermodulen bringt jedenfalls keine 
Leistungseinbußen.

> - wie strukturiert man eigentlich ein vhdl Programm.
Man lagert zusammengehörende Signale und Funktionalität in Untermodule 
aus.
> Also analog zu : in C main-Schleife mit zyklischen Aufgaben
> und ggf. Interrupts die den Ablauf beeinflussen.
VHDL funktioniert nun wirklich komplett anders.

> -würde man sinnvoll eine eigene entity 7-Segment Ausgabe programmieren
> und die in einem Programm als component verwenden? Ich überblicke gerade
> nicht, wie das aussehen würde, aber das component Konzept ist mir im
> Prinzip bekannt. Da müsste man die Ausgabe entity in der architecture
> des "Hauptprogrammes" an gemeinsame Signale Knoten?
Ja

> -wirklich viel scheint man in ein cpld nicht zu bekommen? Der Bericht
> sagte mir im Grunde, daß mit dem bischen schon 30% voll sind. Oder ist
> das eine proggrammierunsfrage (s.o.)?
Ein 9572 ist schon wirklich klein, dass füllt sich schnell. Für 
komplexere Designs gibt es FPGAs. Mit Programmierstil hat das (fast) 
nichts zu tun.

> -wird man in einem reaken etwas komplexeren Design nicht wahnsinnig vor
> internen Signalen? Ich habe das Gefühl daß ich zu fast jedem
> Ausgangssignal irgendwo ein internes Signal habe und das bei components
> noch schlimmer würde?
Deshalb wird das ganze ja strukturiert. Ein C/C++ Programm ohne 
Unterprogramme hätte ja auch unübersichtlich viele Variablen.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> -wirklich viel scheint man in ein cpld nicht zu bekommen?
Ein 9572 hat nur 72 FFs, die sind schnell weg. CPLDs sind daher nur für 
sehr begrenzte Aufgaben geeignet (z.B. als SPI-Slave oder 
Adressdecoder).

Um wirklich größere Hardwareaufgaben zu bewältigen brauchst du ein FPGA. 
Dort gibts FFs wie Sand am Meer ;-)

     if (T='1' and T_old='0') then      
       :
     end if;
     T_old <= T;
So nicht!
Du mußt externe Signale mit 2 FF einsynchronisieren.
Nichtbeachten dieser Regel führt zu sproradischen Fehlern, d.h. dein 
Design hat z.b. jede Minute/Stunde/Tag ein Fehlverhalten. Stichworte: 
Metastabilität, Taktdomänenübergang
  main_clock: process(clk)
Es gibt keinen Main-Prozess. Der clk ist der Master-Takt, der in allen 
(getakteten) Prozessen verwendet wird. D.h. es gibt nur 1 Takt. Später 
darfst du auch mal (wenns unbedingt sein muß) mehrere Takte verwenden 
;-)

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ist es bei vhdl analog zu C möglich so umständlichen Code zu
> produzieren, daß weniger performante Hardware herauskommt oder massig
> Ressourcen verschwendet werden?
Ja.
Allerdings kommt es hier nicht (bzw. nur eingeschränkt) auf die Anzahl 
der Prozesse und Zuweisungen an. Es kommt auf die Denkweise an:

Nehmen wir mal diesen Code 1:
architecture Behavioral of test1_code is
  signal cnt : integer range 0 to 200 := 0;
begin
  
  process begin
    wait until rising_edge(clk);
    if (cnt=100) then    
       cnt <= 0;  
    else
       cnt <= cnt+1;
    end if;
  end process;

end;

Und diesen Code 2:
architecture Behavioral of test1_code is
  signal cnt : integer range 0 to 200 := 0;
begin
  
  process begin
    wait until rising_edge(clk);
    if (cnt>=100) then    
       cnt <= 0;  
    else
       cnt <= cnt+1;
    end if;
  end process;

end;

Beide Beschreibungen machen funktionell das selbe: sie zählen von 0 bis 
100 (jeweils einschliesslich). Nur wird einmal auf GLEICH und das andere 
mal auf GRÖSSER-GLEICH verglichen.


Und hier das Ergebnis. Auf einem Spartan 3 FPGA ergibt sich für
Lösung 1: Maximum Frequency: 187.875MHz
und
Lösung 2: Maximum Frequency: 215.943MHz
und damit immerhin 15% Performance-Unterschied   :-o

Klar, sagt jetzt jeder, dann nehmen wir immer die Gößer-Gleich Variante.

Aber, was passiert, wenn ich im Code 2 folgendes mache:
:
  signal cnt : integer range 0 to 110 := 0;  -- passen in 7 Bit --> nur 7-Bit Vergleich nötig
:
Dann wird die schnelle Lösung 2 noch einiges schneller, nämlich
Maximum Frequency: 232.693MHz
und damit 24% schneller als die exakt funktionsgleiche langsamere 
Beschreibung.




BTW:
Nimm statt dieser Synopsys-Libs
> use IEEE.STD_LOGIC_ARITH.ALL;
> use IEEE.STD_LOGIC_UNSIGNED.ALL;
besser die herstellerunabängige
> use IEEE.NUMERIC_STD.ALL;
Dann kanst du Vektoren explizit signed oder unsigned verarbeiten.
(Also nicht nur implizit über IEEE.STD_LOGIC_UNSIGNED.ALL und 
IEEE.STD_LOGIC_SIGNED.ALL)

Autor: Hartmutte K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Interessant. Hätte gedacht '=' wär schneller....
Wo wird die maximale Frequenz angezeigt die mit einem Design geht?

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Interessant. Hätte gedacht '=' wär schneller....
Ja, andere auch   ;-)

> Wo wird die maximale Frequenz angezeigt die mit einem Design geht?
Die Synthese gibt einen ersten Tip ab, das finale Timing kann im Static 
Timing Report untersucht werden.

Autor: Corni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und wie kommt man dan auf sowas? simples ausprobieren oder gibts da 
irgendwelche tricks, die man entweder weiß, oder auch nicht?
MfG
Corni

Autor: Jan M. (mueschel)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist aber auch ein ganz spezieller Fall, den Lothar da ausgegraben 
hat. Ich habe mal eine kurze Testreihe gemacht, indem ich als 
Vergleichswert 100 und 101, als Operator = und >=, und als Maximalwert 
für cnt 200 und 110 verwendet habe.

Vergl.  Opera.   Max.   Freq.
100     =        200    188.2
101     =        200    188.2
100     >=       200    216.4
101     >=       200    193.6

100     =        110    218.8
101     =        110    218.8
100     >=       110    218.8
101     >=       110    214.9


Der = Operator erzeugt also unabhängig vom Vergleichswert immer die 
selbe Laufzeit (klar, es müssen immer alle Bits verglichen werden), bei 
>= (oder auch <=) sind aber Optimierungen möglich, weil einzelne Bits 
gar nicht betrachtet werden müssen.
Der beste Code ergibt sich in jedem Fall mit einer korrekt angegebenen 
Größe des Counters - bei einem Maximalwert von 200 muss ja noch ein 
zusätzliches Bit betrachtet werden.

Ich würde zusammenfassen: Bei Vergleichen mit Zahlen, die viel kleiner 
als der Maximalwert sind oder ein sehr glatter Wert sind (im Binärformat 
natürlich), lohnt sich ein Vergleich auf Ungleichheit, im allgemeinen 
Fall ist Gleichheit aber einfacher.

Autor: Huch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
http://www.mikrocontroller.net/articles/VHDL_schni...

> Ein Vergleich ob eine Binärzahl größer oder kleiner als eine andere
> ist, benötigt in der Regel mehr Logikgatter, als der Vergleich zweier
> Binärzahlen auf Gleichheit. Sogar bis zum Dreifachen der Slices, wie
> ein Blick in das Kapitel "Slice Count" in dem Xilinx Library Guide
> beweist.

????

Autor: Jan M. (mueschel)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Grund für die höhere Komplexität von Ungleichheits-Vergleichen ist 
ganz einfach:
Für = kann man jedes Bit einzeln betrachten und stellenweise mit dem 
Sollwert vergleichen. Bei <,>,<= etc. müssen auch noch Querbeziehungen 
zwischen den Stellen berücksichtigt werden ("Bit2 darf nur dann gesetzt 
sein, wenn gleichzeitig Bit3 gesetzt und Bit4 nicht gesetzt ist").

z.B. für x = 14 (dezimal betrachtet, die Zehnerstelle sei x2, die 
Einerstelle x1) ergibt sich als logischer Ausdruck:
x2 = 1 und x1 = 4

Für x <= 14 aber
(x2 = 1 und x1 <= 4) oder (x2<1)

Bei größeren Stellenanzahlen wird der Unterschied noch deutlicher.

Jetzt gibt es aber noch Spezialfälle, wo der Vergleich auf Ungleichheit 
einfacher ist, z.B.  x <= 19, das wird einfach zu x2<=1, die Einerstelle 
braucht nicht betrachtet zu werden.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich habe mal eine kurze Testreihe gemacht...
Schön, wenn man andere ins Grübeln bringt ;-)
Ich habe auch Versuche gemacht und bin für größere Vergleicher-Breiten 
(ab 20Bit) zum diametralen Ergebnis gekommen:
Ein Vergleich auf GLEICH war marginal schneller.

> und als Maximalwert für cnt 200 und 110 verwendet habe.
Am wichtigsten ist eine richtig definierte Wortbreite. Wenn ich nur 7 
Bit brauche, macht es keinen Sinn, 8 Bit zu vergleichen.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Der Grund für die höhere Komplexität von Ungleichheits-Vergleichen ist
> ganz einfach:
Uberraschend ist aber (nach althergebrachter Denkweise), dass ein 
Abwärtszähler keinen Geschwindigkeitsvorteil bringt:
architecture Behavioral of test1_code is
  signal cnt : integer range 0 to 100 := 0;
begin
  
  process begin
    wait until rising_edge(clk);
    if (cnt=0) then  cnt <= 100;  
    else             cnt <= cnt-1;
    end if;
  end process;

end;
Der könnte doch ganz locker mit einem ordinären und schnellen OR-Gatter 
abgebildet werden (könnte man meinen).

Warum ist ein Vergleich auf 100 gleich schnell, wie einer auf 0?
Ganz einfach. Ein Vergleich von 7 Bit mit einem gespeicherten Wert ist 
eine Logische Funktion mit 7 Eingägnen. Sowas erfordert 2 LUT-Ebenen 
hintereinander:
       _____
0 ----|     |
1 ----|     |
2 ----|     |---.
3 ----|_____|   |    _____
                `---|     |
4 ------------------|     |
5 ------------------|     |----- A
6 ------------------|_____|
Mit was verglichen wird (0 oder sonstwas) ist dabei egal, weil die LUT 
auf diese Art jede logisache Kombination der 7 Eingangswerte abbilden 
können.

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Hartmut K.:

Dein Quelltext sieht auf den ersten Blick nicht zu lang aus, für die 
Aufgabe, die er erfüllen soll. ieee.std_logic_arith.all vs. 
ieee.numeric_std.all wurde schon erwähnt.

Ich würde statt den Bitstrings etwas lesbares hinschreiben, z.B. 
vordefinierte Konstanten:
constant seg_1 : std_logic_vector(6 downto 0) := "1111001";

>-wird man in einem realen etwas komplexeren Design nicht wahnsinnig vor
>internen Signalen? Ich habe das Gefühl daß ich zu fast jedem
>Ausgangssignal irgendwo ein internes Signal habe und das bei components
>noch schlimmer würde?

Ich verwende da gerne einen _in und einen _out record. Siehe auch 
[1]. Damit wird es übersichtlich und es läßt sich auch in einem 
komplexen Design ein Signal schmerzfrei hinzufügen.

Duke


[1] http://www-md.e-technik.uni-rostock.de/lehre/vlsi_...

Autor: bko (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

>Der Grund für die höhere Komplexität von Ungleichheits-Vergleichen ist
>ganz einfach:
>Für = kann man jedes Bit einzeln betrachten und stellenweise mit dem
>Sollwert vergleichen. Bei <,>,<= etc. müssen auch noch Querbeziehungen
>zwischen den Stellen berücksichtigt werden ("Bit2 darf nur dann gesetzt
>sein, wenn gleichzeitig Bit3 gesetzt und Bit4 nicht gesetzt ist")

bei CMOS-Logic Standart-Cell Logik Synthese stimmt dies 100%.

Aber der Spartan hat schnelle kaskadierbare Addierer und
Multiplexer (ADD1 und MUXCY). Diese werden von der ISE
Synthese auch für Vergleiche benutzt (habe es eben
mal ausprobiert).

bko

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]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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