mikrocontroller.net

Forum: FPGA, VHDL & Co. VHDL: Kombinatorische Schaltung oder Multiplexer


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 D. C. (4tmeldriver)


Bewertung
0 lesenswert
nicht lesenswert
Hallo und schöne Feiertage,

ich habe zwei Prozesse geschrieben, die genau das selbe tun: Einer 
Ziffer die jeweiligen Segmente einer 7-Segment-Anzeige zuordnen.
Für die erste Variante habe ich für jedes Segment aussagenlogische 
Formeln mittels KV-Diagramm entwickelt und für die zweite Variante habe 
ich stur mittels des case-Konstrukts die entsprechenden Bits zugewiesen.
Hier der Code beider Varianten:
Variante 1:
showSegments : process (number)
  begin
    segments(0) <= not(number(3) or number(1) or (number(2) and number(0)) or (not number(2) and not number(0)));
    segments(1) <= not(not number(2) or (number(1) and number(0)) or (not number(1) and not(number(0))));
    segments(2) <= not(number(2) or number(0) or (not number(1) and not number(0)));
    segments(3) <= not(number(3) or (not number(2) and not number(0)) or (not number(2) and number(1)) or (number(2) and not number(1) and number(0)) or (number(1) and not number(0)));
    segments(4) <= not((not number(2) and not number(0)) or (number(1) and not number(0)));
    segments(5) <= not(number(3) or (not number(1) and not number(0)) or (number(2) and not number(1)) or (number(2) and not number(0)));
    segments(6) <= not(number(3) or (not number(2) and number(1)) or (not number(1) and number(2)) or (number(2) and not number(0)));
    segments(7) <= '1';
  
  end process showSegments;

Variante 2:
showSegments : process (number)
  begin
    case number is
      when "0000" => segments <= "11000000"; -- "0"     
      when "0001" => segments <= "11111001"; -- "1" 
      when "0010" => segments <= "10100100"; -- "2" 
      when "0011" => segments <= "10110000"; -- "3" 
      when "0100" => segments <= "10011001"; -- "4" 
      when "0101" => segments <= "10010010"; -- "5" 
      when "0110" => segments <= "10000010"; -- "6" 
      when "0111" => segments <= "11111000"; -- "7" 
      when "1000" => segments <= "10000000"; -- "8"     
      when "1001" => segments <= "10010000"; -- "9"
      when others => segments <= "11111111"; -- "Error"
    end case;
  end process showSegments;

Beide Code-Schnipsel funktionieren genauso gut.
Der Lesbarkeit nach hat Variante 2 auf jeden Fall die Nase vorn. Das 
führt mich zu meiner Frage: Welche Variante würde man praktisch der 
anderen vorziehen? Ist eine sparsamer oder wird das ohnehin alles 
optimiert von der Synthese?

Gruß und frohes Fest.

von qwertzuiopü+ (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Auf einem FPGA würde ich davon ausgehen, dass die erste Variante in die 
zweite umgewandelt wird, da ja ein FPGA aus LUTs besteht. Für 
synthetisierte Logik aus einzelnen Gattern hingegen würde ich vermuten, 
dass die erste Variante näher am Endergebnis liegt.
Lesbarer ist aber auf jeden Fall die zweite - und ein Tool, welches es 
nicht schafft, daraus die jeweils bessere Variante zu erzeugen, ist sein 
Geld wohl nicht wert.

von Chuck Miller (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Deine Variante 1 beruecksichtigt ueberhaupt nicht, was denn in 
"Transistoren" gemessen die beste Implementierung waere. Bsp.: Ein 
4-fach NAND ist schneller als ein 4-fach NOR (in heutiger CMOS 
Implementierung), das hat zu tun mit der Ladungstraegerbeweglichkeit und 
anderen physikalischen Effekten. Auch koennte bei Variante 1 evtl. ein 
XOR oder XNOR Transistoren und damit Flaeche/Laufzeit sparen...

Variante 2 sollte ein halbwegs normaler Synthesizer optimieren koennen, 
ein danach folgender Mapper (auf die Zieltechnologie) macht den Rest. 
Beim FPGA halt typischerweise eine 4/5/6-Bit LUT (SRAM 1Bitx16/32/64). 
Also wirst du eigentlich immer Variante 2 hinschreiben wollen, weil: 
Besser lesbar!

Es ist aber durchaus interessant, wenn man sich mal so manche High-Level 
Statements auf Gatterebene runterbricht. Es hilft auch, die Komplexitaet 
eines Designs abschaetzen zu koennen. Und falls du nicht FPGA, sondern 
ASIC mit Standard-Zell machst, kommst du um solche Ueberlegungen eh' 
nicht rum...
Ein Klassiker ist das n-fach AND-OR, der Multiplexer. Dass der in 
Wirklichkeit im ASIC immer auf NAND runtergebrochen wird sollte man mal 
gesehen & verstanden haben.

von Vancouver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Die Logiksynthese wird beide Varianten zu einer Netzliste synthetisieren 
und diese anschließend optimieren. Dabei wird jeweils eine Architektur 
herauskommen, die für die Zieltechnologie am besten geeignet ist (außer 
du verbietest der Synthese ausdrücklich, etwas zu optimieren). Von 
deinen mühevoll hergeleiteten ORs und NOTs wird dabei vermutlich nichts 
übrig bleiben.
Insofern ist Variante 2 die weitaus bessere, weil sie einfacher zu 
verstehen und zu warten ist. Es macht überhaupt keinen Sinn, den Tools 
irgendwelche Optimierungsaufgaben anbnehmen zu wollen -  die können das 
besser als du. Also schreib deinen Code auf die einfachste Art und Weise 
und lass die Synthese etwas Brauchbares draus backen. Nur wenn das nicht 
klappt (was manchmal vorkommt), musst du ihr unter die Arme greifen.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Derek C. schrieb:
> ich habe zwei Prozesse geschrieben, die genau das selbe tun
Nein, das tun sie nicht.
Siehe Screenshots der Waveform von Variante1.png und Variante2.png.

Vancouver schrieb:
> Von deinen mühevoll hergeleiteten ORs und NOTs wird dabei vermutlich
> nichts übrig bleiben.
> Insofern ist Variante 2 die weitaus bessere,
Besser wäre "im Prinzip" die Variante 1, bei der ganz offensichtlich die 
"others" Bedingung wegfällt. Somit kann der Synthesizer in den 
undefinierten Zuständen "1010"..."1111" beliebige Bitmuster erzeugen, 
statt dort definierte "1111111" im "Error"-Fall ausgeben zu müssen. 
Deshalb hat der Synthesizer bei Variante 1 "im Prinzip" mehr 
Freiheitsgrade zur Optimierung.

Allerdings ist das hier schnurzegal: weil jedes einzelne Segment nur 4 
Eingänge hat und diese 4 Eingänge exakt auf eine 4er-LUT passt, werden 
in beiden Fällen für 8 Ausgänge in beiden Fällen genau 8 LUTs gebraucht.

> weil sie einfacher zu verstehen und zu warten ist.
Dann würde ich gleich auf die Variante mit einem "segment" Array und 
"number" als Index gehen. Damit kann ich mir das ganze unnötig 
ausschweifende Process-Gehampel sparen:
type segment_typ is array (0 to 15) of std_logic_vector(7 downto 0); 
constant segment : segment_typ :=(
"11000000","11111001","10100100","10110000",
"10011001","10010010","10000010","11111000",
"10000000","10010000","11111111","11111111",
"11111111","11111111","11111111","11111111");
:
:
   segments <= segment(to_integer(unsigned(number)));
Diese Variante braucht wie zu erwarten ebenfalls 8 LUTs, die Waveform 
habe ich als VarianteArray.png angehängt.

Das kann man übrigens einfach selber mal ausprobieren und sich dabei die 
Synthesizer-Logs und den RTL-Schaltplan mal genauer anschauen. Den dann 
auftretenden Effekt nennt man "Lernen"...  ;-)

: Bearbeitet durch Moderator
von svedisk ficklampa (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Damit kann ich mir das ganze unnötig ausschweifende Process-Gehampel
und ausschweifende und eklige Typkonvertierungen sparen:

  with number select
  segments <=  "11000000"  when "0000",
      "11111001"  when "0001",
      "10100100"  when "0010",
      "10110000"  when "0011",
      "10011001"  when "0100",
      "10010010"  when "0101",
      "10000010"  when "0110",
      "11111000"  when "0111",
      "10000000"  when "1000",
      "10010000"  when "1001",
      "11111111"  when "1010",
      "11111111"  when "1011",
      "11111111"  when "1100",
      "11111111"  when "1101",
      "11111111"  when "1110",
      "11111111"  when "1111";


Ein Wunder das der aus dem Array keinen ROM instanziiert hat!

von daniel__m (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Diese Variante braucht wie zu erwarten ebenfalls 8 LUTs

Hmm, bei mir sind es immer 7 LUTs (MSB ist immer '1').

Lothar M. schrieb:
> Variante 1, bei der ganz offensichtlich die
> "others" Bedingung wegfällt.

Alternativ könnte man in der Array-Variante die "11111111" Einträge mit 
"--------" modellieren (falls wirklich nur 10 Einträge genutzt werden). 
Xilinx zumindest nutzt es tatsächlich als "Don't Care" und setzt ein, 
was ihm optimal erscheint. Und, die Simulation sieht besser aus. Man 
erkennt sofort, wenn doch ungültige Nummern kommen.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
daniel__m schrieb:
> Hmm, bei mir sind es immer 7 LUTs (MSB ist immer '1').
Ich habs mit Lattice Diamond auf einem MachXO2 ausprobiert.

svedisk ficklampa schrieb:
> Ein Wunder das der aus dem Array keinen ROM instanziiert hat!
Eine LUT ist ein 16x1-ROM. Acht LUTs ergeben ein 16x8 ROM. Dafür einen 
vorbelegten RAM-Block mit x kBit zu nehmen, wäre offensichtlicher 
Overkill. Und zudem bräuchte ein zu einem ROM umfunktionierter RAM-Block 
noch einen Takt.

von svedisk ficklampa (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
> Lattice Diamond

Ja, ein Grund mehr bei Altera zu bleiben.

von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
Hast du das gemalt oder wurde das automatisch erzeugt? Diese 3-Input 
LUTs finde ich etwas seltsam. Nach dem Place und Route sieht das 
bestimmt anders aus.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
svedisk ficklampa schrieb:
> Ja, ein Grund mehr bei Altera zu bleiben.
Ich sehe da kein ROM...

> Ja, ein Grund mehr bei Altera zu bleiben.
Oder doch ein Grund weniger, bei Altera zu bleiben, denn wie vermutet 
bleiben auch bei Diamond zum Schluss in der Technology Schematic nur 7 
LUTs übrig.

von C.A. Rotwang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
qwertzuiopü+ schrieb:
> Auf einem FPGA würde ich davon ausgehen, dass die erste Variante in die
> zweite umgewandelt wird, da ja ein FPGA aus LUTs besteht.

Nicht nur und nicht jeder FPGA! Bspw. xilinx hat neben den LUT's 
einzelne Gatter und Multiplexor die zur Implementierung von Multiplexern 
heranzgezogen werden können.
Insbesonders bei "verteilten Multiplexern" ist das von Vorteil:

https://www.xilinx.com/support/documentation/white_papers/wp274.pdf

https://www.xilinx.com/support/documentation/application_notes/xapp522-mux-design-techniques.pdf

von svedisk ficklampa (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
> automatisch erzeugt?

[x]

> Diese 3-Input LUTs finde ich etwas seltsam.

Jo mei, wenn der Term halt nur von 3 abhaengt.
Er zeigt ja die zugehoerige Kombinatorik im Kasterl.

von Duke Scarring (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Derek C. schrieb:
> Welche Variante würde man praktisch der
> anderen vorziehen?
Ich bevorzuge die (maximal) lesbare Form. Optimieren kann man immer 
noch, wenn es tatsächlich nötig werden sollte.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity bin_to_segment is
    port
    (
        number      : in  unsigned( 3 downto 0);
        segments    : out unsigned( 7 downto 0)
    );
end entity bin_to_segment;


architecture rtl of bin_to_segment is

    constant SEG_A      : unsigned(7 downto 0) := "00000001";
    constant SEG_B      : unsigned(7 downto 0) := "00000010";
    constant SEG_C      : unsigned(7 downto 0) := "00000100";
    constant SEG_D      : unsigned(7 downto 0) := "00001000";
    constant SEG_E      : unsigned(7 downto 0) := "00010000";
    constant SEG_F      : unsigned(7 downto 0) := "00100000";
    constant SEG_G      : unsigned(7 downto 0) := "01000000";
    constant SEG_DP     : unsigned(7 downto 0) := "10000000";

    constant DIGIT_0    : unsigned(7 downto 0) := (SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F        );
    constant DIGIT_1    : unsigned(7 downto 0) := (        SEG_B + SEG_C                                );
    constant DIGIT_2    : unsigned(7 downto 0) := (SEG_A + SEG_B +         SEG_D + SEG_E +         SEG_G);
    constant DIGIT_3    : unsigned(7 downto 0) := (SEG_A + SEG_B + SEG_C + SEG_D +                 SEG_G);
    constant DIGIT_4    : unsigned(7 downto 0) := (        SEG_B + SEG_C +                 SEG_F + SEG_G);
    constant DIGIT_5    : unsigned(7 downto 0) := (SEG_A +         SEG_C + SEG_D +         SEG_F + SEG_G);
    constant DIGIT_6    : unsigned(7 downto 0) := (SEG_A +         SEG_C + SEG_D + SEG_E + SEG_F + SEG_G);
    constant DIGIT_7    : unsigned(7 downto 0) := (SEG_A + SEG_B + SEG_C                                );
    constant DIGIT_8    : unsigned(7 downto 0) := (SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F + SEG_G);
    constant DIGIT_9    : unsigned(7 downto 0) := (SEG_A + SEG_B + SEG_C + SEG_D +         SEG_F + SEG_G);
    constant ALL_OFF    : unsigned(7 downto 0) := (others => '0');

begin

  with to_integer( number) select
  segments <= 
      DIGIT_0 when 0,
      DIGIT_1 when 1,
      DIGIT_2 when 2,
      DIGIT_3 when 3,
      DIGIT_4 when 4,
      DIGIT_5 when 5,
      DIGIT_6 when 6,
      DIGIT_7 when 7,
      DIGIT_8 when 8,
      DIGIT_9 when 9,
      ALL_OFF when others;

end architecture rtl;

Außerdem versuche ich im Design alle Signale als high-Aktiv zu behandeln 
(1 = AN). Erst an der Schnittstelle nach außen wird ggf. invertiert, um 
- wie hier - low-Aktive LED-Segmente anzusteuern.

Duke

von Josef G. (bome) (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ich hab auch noch eine Version:
signal zahl :                  std_logic_vector(3 downto 0);
signal wert :                  std_logic_vector(7 downto 0);    --(.GFEDCBA) 

type feld is array(0 to 15) of std_logic_vector(7 downto 0);

constant tabelle : feld := (x"3f",x"06",x"5b",x"4f",x"66",x"6d",x"7d",x"07",
                            x"7f",x"6f",x"77",x"7c",x"39",x"5e",x"79",x"71");

wert <= tabelle(to_integer(unsigned(zahl)));

Beitrag "Re: 1 Byte in dual 7 Segment Anzeige übersetzen"

Ich hatte da immer an "distributed ROM" gedacht.
Dass das tatsächlich nur 8 oder 7 LUTs sind,
war mir bisher nicht bewusst.

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.

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