Einführung
Als FPGA-Designer benötigt man vielfältige Fähigkeiten, die ich sicherlich nicht auf einer Seite zusammenfassen kann. Es gibt aber ein paar allgemeine Grundsätze, die gerade in diesem Beruf wichtig sind. Ich habe versucht, sie in den fünf goldenen Regeln für das FPGA-Design zusammenzufassen.
Regel Nr. 1: Wisse, was du tust
Das ist die erste Regel und die am schwersten einzuhaltende. Sie bedeutet, zu verstehen, was jede Codezeile, jede Randbedingung oder jedes Konfigurationskommando bedeutet und welche Auswirkungen es hat. Es bedeutet, die zugrunde liegende Theorie des Logikentwurfs zu verstehen und zu wissen, wie die Entwicklungswerkzeuge auf das reagieren, womit wir sie füttern.
Es ist das Gegenteil von Arbeiten nach Versuch und Irrtum – etwas, das die Hersteller von Hightech-Entwicklungswerkzeugen indirekt fördern, indem sie Debugger, Simulatoren und eine ganze Reihe von Werkzeugen anbieten, die mit „Das geht ganz einfach“ beworben werden.
Es ist nicht ungewöhnlich, Software zu entwickeln, indem man etwas hinschreibt, einen Testlauf mit dem Debugger macht, schaut, wie es lief, etwas korrigiert und das Ganze wiederholt. Dabei werden auch ab und zu Code-Schnipsel aus Beispielen aus dem Internet in die Software kopiert. Diese iterative Arbeitsweise kann bei einfacher Software durchaus produktiv sein, etwa bei Webseiten oder Smartphone-Apps: Kleine Fehler sind nicht weiter schlimm und können behoben werden, sobald sie auftreten – so die landläufige Einstellung.
Je anspruchsvoller die Software ist, desto schneller führt diese Methode allerdings in eine Sackgasse. Und beim FPGA-Design beißen sich iterative Entwicklungsmethoden schnell und heftig in den Schwanz. FPGA-Designwerkzeuge helfen keineswegs dabei, schwerwiegende Fehler zu verhindern oder vor ihnen zu warnen. Du kannst es ganz und gar vermasseln – die Werkzeuge werden dir nicht im Weg stehen.
Regel Nr. 2: Garantiere, dass es funktioniert
Oder: „Es funktioniert“ ist nicht gut genug.
Ein FPGA ist ein Stück Elektronik, kein Computer. Wenn bestimmte Entwurfsprinzipien befolgt werden (siehe Regel Nr. 1), ist es trotzdem zuverlässig und reproduzierbar. Wenn nicht, kann es durchaus fiese Tricks spielen – aus welchem Grund auch immer oder ohne jeden ersichtlichen Grund.
Wenn kein sichtbarer Fehler vorliegt, beweist das nicht, dass es kein Problem gibt – das gilt in jedem Bereich. Wenn dein Installateur zum Beispiel seine Arbeit beendet hat und kein Wasser aus dem Rohr tropft, heißt das nicht, dass die Arbeit korrekt ausgeführt wurde. Das Rohr kann später anfangen zu lecken, weil der Wasserdruck gestiegen ist oder weil jemand versehentlich daran gestoßen hat.
Und trotzdem tappen wir alle in die Falle, einen schnellen oder auch einen ausführlicheren Test zu machen und die Sache für erledigt zu halten, wenn alles in Ordnung zu sein scheint. Das ist bei einem leckenden Wasserrohr einigermaßen akzeptabel, bei der Entwicklung einer einfachen Website oder sogar bei Software, die nicht Teil einer kritischen Anwendung ist. Aber bei FPGAs ist das einfach nicht gut genug.
Ein FPGA-Design zu entwickeln bedeutet zu garantieren, dass es funktionieren wird. Es bedeutet, die Entwicklungswerkzeuge mithilfe der Techniken, die sie genau für dieses Ziel anbieten, dazu zu bringen, einen funktionssicheren Bitstrom (bitstream) zu erzeugen.
Es geht darum, sich selbst Beweise zu führen – wie mathematische Beweise –, dass der Logikentwurf korrekt ist, statt in Kategorien wie „Wenn das passiert, dann mach jenes“ zu denken. Die Logik sollte auch in den bizarrsten Grenzfällen funktionieren, nicht weil diese Möglichkeiten einzeln berücksichtigt wurden, sondern aus demselben Grund, aus dem ein korrekter mathematischer Ausdruck unter allen Umständen wahr bleibt.
Die Tatsache, dass es am Ende funktioniert, ist kein Grund zu feiern. Es ist das natürliche Ergebnis korrekter Arbeit.
Regel Nr. 3: Simuliere mit Bedacht (oder gar nicht)
Eine der häufigsten Klagen von FPGA-Neulingen lautet, dass ihre Simulation perfekt funktioniere, ihr Design also in Ordnung sei und das Problem deshalb woanders liegen müsse. Das ist natürlich Unsinn.
Zunächst einmal sollten wir also klarstellen: Wenn der Verilog-Codierungsstil nicht bestimmten strengen Regeln folgt, können Simulation und Hardware völlig unterschiedliche Dinge tun (natürlich gilt dasselbe für VHDL). Siehe Regel Nr. 1.
Aber Simulationen decken nur einen begrenzten Zeitraum und eine begrenzte Zahl von Szenarien ab, selbst wenn sie korrekt durchgeführt werden. Selbst bei einem mittelgroßen Design dauert es eine Ewigkeit, zu simulieren, was im FPGA beispielsweise innerhalb von 100 ms passiert. Außerdem setzt der echte Lauf auf der Hardware die Logik oft Szenarien aus, die sich der Designer nicht vorgestellt und daher auch nicht simuliert hat. Ein Design, das die Simulation perfekt durchlaufen hat, kann sich auf der Hardware als komplettes Fiasko herausstellen – einfach weil das FPGA viel länger läuft, als die Simulation abdeckt, oder weil unerwartete Dinge passiert sind.
Erschwerend kommt hinzu, dass sich Fehler auf der Hardware schwer finden lassen, weil alles parallel geschieht. Anders als beim Software-Debugging gibt es selten eine Ereignissequenz, der man folgen kann, geschweige denn die Möglichkeit, einen Einzelschritt auszuführen. Es gibt zwar Werkzeuge, um Signale im FPGA zu verfolgen, aber es ist nicht immer einfach herauszufinden, welche Signale man verfolgen soll und welche Bedingung als Trigger für die Erfassung der Trace-Daten dienen soll.
Setz dir also das Ziel, Simulationen so wenig wie möglich einzusetzen. Wenn du Simulationen dazu verwendest, dein Design hin und her zu korrigieren, damit es „irgendwie funktioniert“, stehen die Chancen gut, dass du Ärger bekommst, sobald du es auf der Hardware ausprobierst.
Versuche stattdessen, das FPGA-Design so zu schreiben, dass es vom ersten Lauf an einwandfrei funktioniert. Das erfordert etwas gründliches Nachdenken vor der ersten Codezeile und ebenso, dass du weißt, was du tust (siehe Regel Nr. 1 oben). Die Simulation sollte nur bestätigen, dass du es richtig gemacht hast, oder vielleicht einen dummen Tippfehler aufdecken. Dieses Ziel zu erreichen, ist ein Prozess des Lernens und der Verbesserung, aber es zahlt sich aus. Am Ende werden Simulationen zur Zeitverschwendung, weil sie nichts mehr zu reparieren finden.
Das spart nicht nur Zeit, sondern es gibt auch weniger Fehler, die auf der Hardware behoben werden müssen, und sie sind leichter zu finden.
Mir ist durchaus bewusst, dass die übliche erste Lektion im FPGA-Design lautet: „Erst simulieren, dann auf der Hardware testen.“ Aber das ist gut für die erste Lektion.
Regel Nr. 4: Fordere dein Glück nicht heraus
Oder: Halte dich an den bewährten Codierungsstil.
Verilog ist keine Programmiersprache, wenn es für die Synthese verwendet wird. Der Hauptunterschied besteht darin, dass der Compiler (oder Interpreter) bei syntaktisch korrektem Code in einer Programmiersprache garantiert genau das ausführt, was die Syntax bedeutet. Oder er meldet im schlimmsten Fall einen Fehler.
Synthesewerkzeuge (englisch: synthesizer) erzeugen dagegen Logik, die auf einer begrenzten Menge von Logikelementen basiert. Es ist daher recht einfach, Verilog-Code zu schreiben, der zu unzuverlässiger Logik führt – oder sogar zu Code, der sich auf einem FPGA gar nicht implementieren lässt. Erschwerend kommt hinzu: Wenn es keine Möglichkeit gibt, den Verilog-Code als Logik auf dem FPGA umzusetzen, erzeugen Synthesewerkzeuge oft eine Logik mit anderem Verhalten. Nicht selten geschieht das, ohne dass das Synthesewerkzeug eine Warnung ausgibt. Mit anderen Worten: Die Logik auf dem FPGA verhält sich anders als erwartet (also als simuliert) – und das ohne jede Warnung.
Und um es noch schlimmer zu machen: Fehler in Synthesewerkzeugen sind deutlich häufiger als in Compilern. Ein kreativer Codierungsstil kann solche Fehler auf jeden Fall ans Licht bringen.
Alles oben Gesagte gilt natürlich auch für VHDL.
Der einzige Weg, Ärger dieser Art aus dem Weg zu gehen, ist also, einen Codierungsstil zu übernehmen, der von vielen anderen verwendet wird. Die Frage, die man sich stellen sollte, lautet: „Wenn das Synthesewerkzeug bei diesem Code-Schnipsel durcheinanderkommt – wie viele werden außer mir noch sauer?“ Wenn die Antwort „sehr viele Leute“ lautet, bist du auf der sicheren Seite.
Sich an einen bewährten Codierungsstil zu halten, hat noch einen weiteren Vorteil: Portabilität. Ob es dir passt oder nicht: Heute arbeitest du mit einem Synthesewerkzeug, und morgen findest du dich mit einem völlig anderen FPGA und anderen Entwicklungswerkzeugen wieder.
Um eine Vorstellung davon zu bekommen, wie ein bewährter Codierungsstil aussieht, schau dir den Verilog-Code an, den die Werkzeuge für ihre eingebauten IP-Kerne (IP core) erzeugen. Es gibt Unterschiede zwischen verschiedenen Autoren, aber manche Codierungsmuster sind häufiger zu sehen als andere. Genau diese sollte man nachahmen.
Es ist fast unnötig zu erwähnen, dass man nach dem RTL-Paradigma arbeiten sollte. Das ist nicht nur ein bewährter Codierungsstil, sondern genau das erwarten die FPGA-Werkzeuge.
Lehrbücher und Tutorials über Verilog können in die Irre führen, denn sie erheben meist den Anspruch, vollständig zu sein. Deshalb behandeln sie oft viele syntaktisch korrekte Möglichkeiten, die aber selten verwendet werden und manchmal nicht einmal für die Synthese geeignet sind.
Regel Nr. 5: Timing ist alles
Logikentwurf ist keine Softwareprogrammierung. Es reicht nicht, in Verilog auf Leitungen und in Registern die richtigen Werte zu erzeugen – es ist nicht weniger wichtig, dass sie zur richtigen Zeit am richtigen Ort erscheinen. Und außerdem, dass die Takte korrekt behandelt werden.
Das sind mehr oder weniger die wichtigsten Themen:
- Überlege genau, wie und wann die Daten durch verschiedene Stufen von Pipelines fließen, insbesondere wenn Pipelines ganz oder teilweise angehalten werden können. Eigentlich gilt das für jeden Datenfluss.
- Stelle sicher, dass die Timing-Vorgaben (timing constraints) korrekt sind. Sieh dir insbesondere die Datenblätter externer Bauelemente an und rechne nach. Diese Seite behandelt die Überprüfung des Timings.
- Achte auf die Takte: Sind sie ab dem ersten Moment, in dem sie verwendet werden, stabil? Ist ihr Jitter (jitter) für ihren Zweck gering genug?
- Taktdomänenübergang (clock domain crossing): Ist eine Resynchronisationslogik erforderlich? Wenn ja, garantiert sie eine ausfallsichere Übertragung des Signals von einer Taktdomäne in die andere? Mehr dazu hier.
Kurz gesagt: Anders als bei Computerprogrammen geht es beim Logikentwurf nicht nur darum, was passieren wird, sondern auch wann es passiert.
Zusammenfassung
Niemand hat behauptet, dass FPGA-Design einfach ist, und keine dieser fünf Regeln ist leicht zu befolgen. Jede von ihnen erfordert sowohl Wissen als auch ein gewisses Maß an Selbstdisziplin. Und dennoch lohnt es sich auf jeden Fall, sich an diese Regeln zu halten. Das erspart eine Menge Frustration, besonders in der letzten Projektphase, in der alles einfach nur funktionieren soll.