Hello World – CPLD

Dieser Beitrag wurde am 09.11.2022 aktualisiert.

Heute möchte ich euch zeigen, wie man einen Logikschaltkreis programmiert. Für alle, die boolsche Algebra hassen, sei gesagt: Dafür gibt es doch Computer, die machen das.

Man sieht, dass man jede logische Funktion in Form einer Tabelle in einem Speicher abbilden kann. So können wir jedes beliebige Logikgatter erzeugen. Der Inhalt dieser Speicher bildet die Konfiguration, also das, was wir später frei bestimmen können.
Nun brauchen wir noch Blöcke, die Ergebnisse der Logik zwischenspeichern können. Das erfolgt über einen externen Takt. Hier das Zeichen für ein D-Flipflop. Das Signal, das während der steigenden Flanke an D angelegen hat, liegt bis zum nächsten Takt an Q an. Der Vollständigkeit halber gibt es noch einen Ausgang, der das Gegenteil davon ausgibt.

D-FF
D-Flip Flop

Diese Logikgatter und Ergebnisspeicher sind Teil sogenannter Macrozellen. Diese Zellen können durch Logik und Zwischenspeichern sogenannte Zustandsmaschinen abbilden. Davon gibt es dann eine ganze Menge und wenn alles verkabelt ist, können wir in unserem Beispiel einen Zähler oder auch Addierer (wir addieren immer 1 dazu) bauen. Fertig ist der CPLD.

CPLD Architecture
CPLD Architecture

Bei der Programmierung wird man heutzutage von umfangreichen Entwicklungsumgebungen unterstützt, mit denen es möglich ist, in einer Hochsprache (z.B. VHDL oder Verilog) die Aufgabe zu beschreiben. Der Vorteil eines solchen Logikbausteins ist, dass alle Operationen parallel verarbeitet werden. Ein Nachteil ist: Bei komplexen Problemen wird es meist schwer zu verstehen, was da alles gleichzeitig passiert und sich beeinflusst.

Der Große Bruder des CPLD ist der FPGA. Dieser hat mehr Logikzellen, fest integrierte Multiplikatoren und interne RAM Blöcke. Als Einstieg reicht jedoch ein CPLD völlig aus.

Das Board hat 5 LEDs. D1 ist die Anzeige für die Versorgunsspannung.

FunktionCPLD – PINFunktion
LED – D1VCC IOEinfache Power LED
LED – D2 P34 I/O oder Tri-State Eingang
LED – D3P33I/O oder Globaler Reset Eingang
LED – D4P32I/O
LED – D4P31I/O
CLK 50MHzP1I/O oder Globaler Tackteingang

Das Hello World soll die LEDs etwas blinken lassen. Das Bord hat einen 50MHz Quarz der an einen der drei Takteingänge angeschlossen ist. Damit das Ganze nun nicht mit 25MHz blinkt, programmieren wir einen 25 Bit Zähler. Der Zähler ist natürlich Binär, kennt also nur 1 und 0. Immer wenn wir ein Bit aufaddieren, wird aus 0 eine 1 und aus 1 eine 0. Dabei einsteht ein Übertrag, der auf das nächst höhere Bit aufaddiert wird. Ist der Zähler voll, fängt automatisch immer wieder von vorne an.
Die Bits von einem zum nächsten ändern immer halb so schnell wie der Vorgänger den Zustand. Und das lassen wir uns mit unseren LEDs anzeigen. Wir kopieren also die obersten Bits des Zählers (die unteren zappeln viel zu schnell) in die LEDs und los gehts.
Ach noch ganz kurz: D1, also LED1, ist immer an. Diese zeigt an, dass der Strom an ist.

Hier ist der Beispielcode in VHDL (Very high-speed integrated circuit hardware description language). Die Syntax erinnert etwas an Pascal. Für das Verständnis ist vielleicht zu sagen, dass hier kein sequenzieller Programmablauf beschrieben wird, sondern entweder entsteht eine logische Verknüpfung oder eine durch einen Takt verzögerte Zuweisung. Damit werden im wesentlichen ,,if-then“ oder ,,case“ Anweisungen benutzt. Abläufe werden durch Zustandsvariablen gesteuert. Das klassische Schlüsselwort ,,for“ gibt es zwar, aber es wird verwendet um zu beschreiben, wie zum Beispiel eine Gruppe von Bits an einem Stück verarbeitet oder umkopiert wird. Alles parallel eben.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity HelloWorld is
    Port ( CLK  : in   STD_LOGIC;
           LED2 : out  STD_LOGIC;
           LED3 : out  STD_LOGIC;
           LED4 : out  STD_LOGIC;
           LED5 : out  STD_LOGIC);
end HelloWorld;

architecture Behavioral of HelloWorld is
signal Counter : STD_LOGIC_VECTOR(24 downto 0);
begin
  process (CLK)
  begin
    if rising_edge(CLK) then
      Counter <= Counter + X"000001";
      LED2 <= Counter(24);
      LED3 <= Counter(23);
      LED4 <= Counter(22);
      LED5 <= Counter(21); 
    end if;
  end process;
end Behavioral;

Was jetzt noch fehlt ist die Zuweisung der Pins zu den LED Signalen und zum CLK. Das passiert nicht im VHDL Modu, sondern ist Teil der Synthese. Dieses Modul kann später ein Teil eines großen Projektes sein oder eben einfach an Pins gedrahtet werden. Die Zuodnung wird in einer UCF Datei festgelegt.

NET "CLK"  LOC = "P1"  ; 
NET "LED2"  LOC = "P34"  ; 
NET "LED3"  LOC = "P33"  ; 
NET "LED4"  LOC = "P32"  ; 
NET "LED5"  LOC = "P31"  ; 

Und hier sehen wir eine Übersicht über die generierte Logik. Ein Zähler und einen Block, der um einen Takt verzögert das Ergebnis an die Ausgänge kopiert.

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert




Enter Captcha Here :