Weißbuch

Vergleich zwischen FPGA RTL und HLS C/C++ anhand eines Netzwerkbeispiels

Übersicht

Die meisten FPGA-Programmierer glauben, dass High-Level-Tools immer größere Bitströme als "Preis" für die höhere Produktivität ausgeben. Aber ist das immer so? In diesem Beitrag zeigen wir ein Beispiel aus der Praxis, bei dem wir eine allgemeine Netzwerkfunktion, RSS, sowohl mit traditionellen RTL/Verilog-Tools als auch mit High-Level-Synthese (HLS) auf derselben Hardware erstellt haben. Was wir herausfanden, war überraschend: Der HLS-Ansatz verbrauchte tatsächlich weniger FPGA-Gatter und Speicher. Dafür gibt es einen Grund. Lesen Sie weiter für Details.

Der HLS-Ansatz für die FPGA-Entwicklung besteht darin, nur die Teile der Anwendung zu abstrahieren, die sich leicht in einer C/C++-Umgebung ausdrücken lassen. Der HLS-Toolflow ist durch die Verwendung von Vivado- (Xilinx) oder Intel- (Quartus) Tools für praktisch jede BittWare-Karte verfügbar.

Um an der HLS erfolgreich zu sein, ist es wichtig, die Teile Ihrer Bewerbung zu erkennen, die gut zu Ihnen passen werden. Die Leitlinien umfassen:

  • Zielanwendungen sind im Allgemeinen IP-Blöcke, die zunächst in einer Hochsprache definiert werden. Ein mathematischer Algorithmus würde sich gut eignen oder, wie in unserem RSS-Block, die Verarbeitung eines Netzwerkprotokolls.
  • Eine weitere Kategorie von Anwendungen sind Blöcke, die schlecht definiert sind und daher mehrere Implementierungsrunden erfordern können. Der größte Vorteil besteht hier darin, dass das HLS-Tool den resultierenden nativen FPGA-Code automatisch in eine Pipeline einbinden kann, wodurch oft weniger Stufen entstehen als bei einer schnellen manuellen Codierung einer Pipeline. Wenn es an der Zeit ist, die handkodierte Pipeline zu modifizieren, können sich Verzögerungsänderungen an einem parallelen Pfad auf alle anderen auswirken. Die Verwendung des HLS-Tools zur automatischen Erstellung einer zweiten Pipeline von Grund auf beseitigt diese Probleme.
  • Schließlich erleichtert ein HLS-Flow die Portierung von Code zwischen FPGA-Marken und Geschwindigkeitsstufen. Das liegt daran, dass HLS automatisch die entsprechende Anzahl von Pipeline-Stufen generiert - etwas, das Sie bei der Arbeit mit Verilog oder VHDL manuell festlegen müssen.

Die derzeitigen Beschränkungen von HLS liegen eindeutig darin, dass sein Anwendungsbereich auf IP-Blöcke beschränkt ist. Das Anwendungsteam benötigt nach wie vor RTL für andere Komponenten, obwohl ein Benutzer durch den Einsatz von Produkten wie der SmartNIC Shell von BittWare für die RTL-Teile in der Lage sein könnte, seine einzigartige Anwendung vollständig in HLS zu definieren. Es sollte auch beachtet werden, dass HLS eine schlechte Wahl für die einfachsten Codes oder größere Designs ist, die hauptsächlich aus voroptimierten Komponenten bestehen.

Unsere Anwendung: RSS-Vernetzung auf einem FPGA

Was ist RSS? RSS steht für "Receiver Side Scaling". Es handelt sich um einen Hash-Algorithmus zur effizienten Verteilung von Netzwerkpaketen auf mehrere CPUs. RSS ist eine Funktion moderner Ethernet-Karten und implementiert im Allgemeinen den von Microsoft definierten Toeplitz-Hash.

Die Umgebung, in der unsere RSS-Anwendung läuft, ist die SmartNIC Shell von BittWare. Die SmartNIC Shell wurde entwickelt, um Anwendern einen Vorsprung bei der Entwicklung einer FPGA-basierten Netzwerkanwendung zu verschaffen. Sie bietet den Benutzern eine optimierte FPGA-basierte 100G-Ethernet-Pipeline, einschließlich DPDK für die Host-Interaktion. Alles, was der Anwender tun muss, ist, seine Anwendung als IP-Block einzubinden.

In diesem Fall war BittWare auch der Anwender, der als unsere Anwendung eine FPGA-Implementierung von RSS erstellt hat. Sowohl das Team, das RSS mit dem traditionellen RTL-Ansatz erstellte, als auch das HLS-Team konnten SmartNIC Shell als FPGA-Ethernet-Framework verwenden und sich auf die RSS-Anwendung selbst konzentrieren.

Die RSS-Implementierung von BittWare

Unsere FPGA-basierte RSS-Implementierung basiert auf C-Code, der im DPDK-Quellcodebaum zu finden ist, wobei die Testfunktion für diesen Code ebenfalls im Baum verfügbar ist. Unsere RSS-Anwendung verwendet außerdem eine Umleitungstabelle mit 64 Einträgen anstelle der üblichen Tabelle mit 128 Einträgen. Wichtig für diese HLS-Studie ist, dass die Funktion, die wir in das FPGA übertragen, zunächst in C definiert ist. Damit ist unser wichtigstes Kriterium für den HLS-Erfolg erfüllt - eine Definition in C oder C++.

Pakete mit Tupeln gruppieren

Ziel der RSS-Funktion ist es, die Pakete auf die CPUs zu verteilen und dabei zusammengehörige Paketströme zusammenzuhalten. Verschiedene Toeplitz-Schlüsselmengen bieten unterschiedliche Verteilungsmuster. Unabhängig vom Schlüsselsatz verwendet unsere RSS-Funktion jedoch die Quell- und Ziel-IP-Adresse sowie die Quell- und Ziel-Ports eines jeden Pakets als Eingabe. Diese vier Komponenten zusammen werden als 4-Tupel bezeichnet.

Beachten Sie, dass wir für unsere RSS-Anwendung davon ausgehen, dass das 4-Tupel bereits geparst und zu den Metadaten des Pakets hinzugefügt wurde. Ein anderes SmartNIC-Shell-Modul übernimmt diese Funktion der Paketklassifizierung. Wir nennen dieses Modul unseren "Parser" und es wird Gegenstand eines separaten BittWare-Whitepapers sein.

Unsere RSS-Implementierung akzeptiert derzeit ein 96-Bit-Feld zur Klassifizierung - genug für das 4-Tupel aus IPv4-Quelle/Ziel und Port. Der Parser liefert Null für Felder, die im Paket nicht vorhanden sind; wenn ein Paket keine IP-Nutzlast enthält, ist das volle 96-Bit-Tupelfeld Null.

Viele RSS-Implementierungen verwenden ein 5er-Tupel anstelle eines 4er-Tupels. Dies würde zusätzliche 8-Bits erfordern, um die Protokollnummer unterzubringen. HLS-Benutzer von RSS können diese Änderung mit geringfügigen Änderungen am Quellcode leicht umsetzen. Diese Fähigkeit zur schnellen Anpassung von 4-Tupel auf 5-Tupel ist ein Beispiel für das zweite Kriterium für den Erfolg von HLS - eine Voraussetzung für mehrere Implementierungsrunden.

FPGA-Entwicklungswerkzeug Begriffe

Bei der Entwicklung von IP für die Ausführung in einem FPGA werden traditionell Sprachen verwendet, die ursprünglich für die ASIC-Entwicklung entwickelt wurden. Es gibt keinen Konsens darüber, wie man diese Sprachen nennen soll. Manchmal wird "RTL" verwendet, was für Register Transfer Language steht. Andere wiederum verwenden "HDL", was für Hardware-Definitionssprache steht. Die beiden bekanntesten HDLs sind VHDL und SystemVerilog. BittWare verwendet intern beide Sprachen, aber unsere größeren Projekte verwenden SystemVerilog und dessen vollständige Verifikationsfunktionen.

Die Verifizierung ist ein wichtiger Teil des FPGA-Designprozesses und ein kritischer Teil des ASIC-Designs. Steigende Kosten in der ASIC-Industrie führten dazu, dass fortschrittliche Verifizierungssprachen und -techniken benötigt wurden, um den Erfolg des ersten Durchlaufs auf dem Silizium zu gewährleisten. Dieser Bedarf veranlasste die Verilog-Sprache dazu, Funktionen von HVLs (High Level Verification Languages) zu integrieren, die schließlich in den aktuellen SystemVerilog IEEE 1800 Standard übergingen. Die moderne ASIC-Verifikation hat sich auch in Richtung einer universellen Verifikationsmethodik (UVM) entwickelt, die eine Standardmethode für die Erstellung von Testbenches bietet.

Die unterschiedliche Wirtschaftlichkeit der FPGA-Entwicklung sowie die Möglichkeit, Designs sofort im Labor zu testen, haben die Einführung von UVM bei FPGA-Entwicklern verlangsamt. Nichtsdestotrotz hat die wachsende Komplexität von High-Density-FPGAs viele Gruppen dazu veranlasst, die gleiche Verifizierungsmethodik zu übernehmen, die auch in ASIC-Design-Flows verwendet wird. Bei BittWare haben wir einen fließenden Ansatz für die FPGA-Verifikation. Wir verwenden oft einfache Testbenches, die auf den SystemVerilog- oder VHDL-Funktionen basieren, die in preiswerten oder kostenlosen Simulatoren verfügbar sind. Bei Bedarf erstellen wir jedoch auch moderne Testbenches, die auf dem vollen Funktionsumfang von SystemVerilog und UVM basieren.

HLS, oder High Level Synthesis, ist die Bezeichnung für Sprachen, die auf einer höheren Abstraktionsebene als HDL arbeiten. In der Praxis bezieht sich HLS gewöhnlich auf spezialisierte Versionen von C oder C++. Es gibt jedoch auch andere HLS-Sprachen. Zum Beispiel ist einige IP von Drittanbietern, die BittWare vertreibt, in BlueSpec geschrieben. Alle diese HLS-Tools verfügen in der Regel über eine einfache Möglichkeit, auf Knopfdruck eine Testbench auf Modulebene zu generieren. Auf der Systemebene ist UVM nach wie vor erforderlich.

Und schließlich ist OpenCL heute die höchste Stufe. Dabei handelt es sich um eine parallele Programmiersprache, die für GPU-Chips entwickelt und für die FPGA-Welt umgewidmet wurde. Heute wird OpenCL fast ausschließlich im HPC-Bereich (High-Performance Computing) eingesetzt, um Rechenalgorithmen zu implementieren, die schneller laufen, als ein Intel-basierter Server sie ausführen kann.

HLS-Codierung für Leistung

Obwohl die Verwendung von HLS einen softwareähnlichen Toolflow bietet, muss der Entwickler immer noch hardwarenahe Konzepte wie Pipelining und Iterationsintervalle erlernen, die er beim Schreiben von C-Code für herkömmliche Prozessoren möglicherweise nicht kennengelernt hat.

HLS-Code wird vor allem zur Entwicklung von IP-Komponenten für eingebettete Designs verwendet, in der Regel in Pipelines. Unsere RSS-Anwendung ist da keine Ausnahme. Für RSS besteht die minimale Leistungsanforderung darin, dass jedes 512-Bit-Eingangswort schnell genug verarbeitet wird, um mit einer gesättigten 100 Gb/s-Netzwerkschnittstelle Schritt zu halten. Dies entspricht der Verarbeitung eines neuen Wortes pro Taktzyklus bei einer Frequenz von 300 MHz. Diese Frequenz ist insofern eine Herausforderung, als selbst die schnellsten FPGAs mit Frequenzen von nicht viel mehr als 400 MHz arbeiten. Es liegt auf der Hand, dass wir bei jedem Takt ein neues Wort verarbeiten müssen.

Damit wird das Konzept des Iterationsintervalls (II) eingeführt, das sich auf die Anzahl der Taktzyklen bezieht, die für die Fertigstellung eines bestimmten Teils der Logik in einer Pipeline erforderlich sind. Für das RSS-Modul benötigen wir bei jedem Takt ein Ergebnis, also ein II von 1. Daher müssen wir verstehen, wie man Code schreibt, der diese Anforderung nicht verletzt.

Zu den Ursachen für die Erhöhung von II gehören die folgenden:

  • Abhängigkeiten zwischen den Schleifen entstehen, wenn die nächste Ausgabe einer Pipeline das zukünftige Ergebnis einer anderen Variablen in einer Pipeline erfordert, z. B. bei einer Rekursion. Einfache rekursive Operatoren wie Akkumulatoren sind zulässig, da FPGAs die Logik enthalten, um diese Berechnungen innerhalb eines einzigen Taktzyklus durchzuführen. Für kompliziertere Rekursionen werden jedoch höhere II-Werte benötigt.
  • Das RSS-Design erfordert, dass jede Stufe der Pipeline innerhalb von 3,3ns abgeschlossen wird. Die HLS-Tools fügen bei Bedarf eine Registrierung ein, um sicherzustellen, dass jede Stufe diese Zeitvorgabe erfüllt. Dies ist jedoch nicht immer möglich, wenn die kombinatorische Logik nicht pipelinefähig ist. Beispiele für tiefe kombinatorische Logik können Indexierungsberechnungen für mehrere verschachtelte Schleifen sein.
  • Die II erhöht sich, wenn die Ziel-Taktfrequenz zu hoch ist und die Routing-Pfade der FPGA-Fabric einfach zu lang sind, um die Timing-Anforderungen zu erfüllen. Eine Lösung für dieses Problem wäre die Aufteilung der Logik in zwei Pfade, die mit der halben Taktfrequenz laufen.

Der Hauptteil des Codes durchläuft eine Schleife über die Anzahl der erforderlichen Wörter des Eingabetupels und erstellt einen neuen Hashwert. Für das Beispiel hier verwenden wir ein Eingabetupel von 3 Wörtern für einen Hash-Wert von 96 Bits.

Dieser Code implementiert das Herzstück der RSS-Berechnung. Er bleibt gegenüber dem aus dem DPDK-Quellbaum extrahierten Original unverändert. Im Fall dieses RSS-Blocks bestand also die gesamte Portierungsarbeit in der Definition der AXI-Schnittstellen des Blocks und dem Hinzufügen von Pragma-Anweisungen zu Definitionen wie II.

Wenn die Eingangslänge eine Konstante ist, kann das FPGA die beiden Schleifen vollständig abwickeln, um einen vollständigen Pipeline-Code zu erstellen.

Um die IP-Komponente in das Smart-NIC-Framework zu integrieren, müssen die Schnittstellen und die Steuerebene definiert werden, ebenso wie die Logik zum Lesen und Schreiben auf externen Schnittstellen. Das Smart-NIC-Framework verwendet das AXI-Schnittstellenprotokoll für die Kommunikation zwischen den Komponenten.

Durch die Definition von AXI-Schnittstellen und das Hinzufügen von Pragma-Anweisungen sind zu viele Codezeilen entstanden, um sie hier in einer Abbildung darzustellen. Die vollständige Quellcodedatei ist bei BittWare erhältlich.

Es gibt ein Problem mit der Endianness, da die Konstanten des Xilinx-Compilers in Intel-Byte-Reihenfolge (Little-Endian) vorliegen, während Netzwerkprotokolle die Netzwerk-Byte-Reihenfolge (Big-Endian) verwenden. Dies wirkt sich nicht auf die Leistung oder die Ressourcennutzung aus, erfordert aber, dass die Endianness der Eingabedaten geändert wird, bevor sie in HLS verarbeitet werden.

Native Programmierung vs. HLS: Die Ergebnisse

Der Grund, warum wir zwei FPGA-RSS-Implementierungen haben, ist, dass unsere erste Version in Verilog geschrieben wurde. Dies geschah unter der Annahme, dass die native FPGA-Codierung immer die geringste Ressourcennutzung zur Folge hat, die wir bewerten. Ein Ingenieur von BittWare hat diese Entscheidung jedoch in Frage gestellt und RSS in HLS neu implementiert, um diesen Ansatz zu testen. Er hatte Recht und BittWare hat nun das RSS-Modul und das Parser-Modul in unserer SmartNIC Shell durch HLS-Code ersetzt.

Zwei RSS-Implementierungen

CharakteristischVerilogHLS C
CLBs44,4352,385
BRAM121
Register52,3524,843
Code-Zeilen650459

Der größte Unterschied zwischen den beiden Implementierungen besteht darin, dass die Verilog/RTL-Version FIFOs verwendet und die HLS-C++-Version nicht. Wir waren überrascht, dass die Ressourcennutzung durch den Wechsel zu HLS tatsächlich gesunken ist - etwas, das wir nicht in allen Fällen erwarten würden.

Wie sieht es mit der Zeitersparnis aus? Grob gesagt, benötigten wir für die native RTL-Version einen Monat, während der HLS-Code in einer Woche fertig war.

Intel HLS gegenüber Xilinx HLS

In diesem Beispiel wird Xilinx HLS verwendet. Ein entscheidender Vorteil der Verwendung von Hochsprachen ist jedoch ihre Fähigkeit, die zugrunde liegenden Unterschiede zwischen verschiedenen Technologiearchitekturen bis zu einem gewissen Grad zu abstrahieren. Intel verfügt auch über einen entsprechenden Compiler, der auch C++ zu Gates-RTL-Code kompiliert.

Um den gleichen Code mit dem Intel i++ Compiler zu kompilieren, sind einige subtile Änderungen an den Datentypen und Änderungen an #pragmas erforderlich. Der größte Unterschied zwischen Intel und Xilinx ist die Verwendung von Avalon-Streaming-Schnittstellen für Intel und AXI für Xilinx. Dies würde eine einfache Shim-Schnittstelle erfordern, um von einer zur anderen zu konvertieren.

Co-Simulation

Sobald die Funktion verifiziert ist, ist es eine triviale Aufgabe, die Co-Simulationsumgebung für die zyklusgenaue RTL-Simulation aufzurufen. Vivado-HLS generiert automatisch eine RTL-Testbench, die durch Vektoren aus dem ursprünglichen C++-Code gesteuert wird. Die einzige Änderung, die der Anwender vornehmen muss, ist die Behandlung von Endlosschleifen oder blockierenden Schnittstellen in seinem Entwurf. Das RSS-Modul ist so konzipiert, dass es als Teil einer Firmware-Pipeline unendlich lange läuft. Daher würde die Simulation nie abgeschlossen werden und die Co-Simulation würde hängen bleiben. Um dies zu vermeiden, ändern wir die "while (1)"-Hauptschleife des RSS-Codes so, dass sie eine feste Länge hat, die lang genug ist, um alle Eingaben von der Testbench zu verarbeiten und alle erforderlichen Ausgaben zu erzeugen.

Die Co-Simulation bietet ein zusätzliches Maß an Sicherheit, dass die RTL-Datei vom Tool korrekt generiert wurde und die Timing-Eigenschaften des Moduls mit den ursprünglichen Entwurfsparametern übereinstimmen.

Der Co-Simulationsfluss ist auch als Teil des Intel HLS Tool Stacks verfügbar.

HLS-Gebäude nach IP-Blöcken

Der HLS-Werkzeugfluss muss über eine integrierte Kenntnis der verwendeten Schnittstellenprotokolle verfügen. IP-Blöcke von BittWare verwenden im Allgemeinen Advanced Extensible Interface (AXI) zur Kommunikation. Konkret handelt es sich um einen AXI4-Stream zur Weiterleitung von Paketdaten und AXI4-Lite als Steuerebene.

Für 100 GbE verwendet BittWare eine AXI4-Stream-Schnittstelle, die 512 Bit breit ist und mit 300 MHz getaktet wird. Die mit jedem Paket verbundenen Metadaten folgen auf einem eigenen Bus, der am Ende eines Pakets gültig ist, wenn das TLAST-Signal der Paketdaten aktiviert wird. Die Paket-Metadaten entwickeln sich zwischen Blöcken und zwischen Releases. Sie enthalten normalerweise Informationen über:

  • Die Nummer des physikalischen Ethernet-Anschlusses, über den das Paket angekommen ist
  • Alle Fehler, die der MAC im Zusammenhang mit dem Paket identifiziert hat
  • Ein Zeitstempel im 80-Bit-IEEE-1588-Format oder manchmal in einem verkürzten 64-Bit-Format
  • Ein "deleted"-Bit, um anzuzeigen, dass das Paket bei der nächsten Gelegenheit aus dem Datenstrom entfernt werden muss
  • Eine Zahl, die wir normalerweise "Warteschlange" nennen, um ein Ziel für das Paket anzugeben. Sie wird von einem der IP-Blöcke in der Pipeline berechnet (vielleicht sogar von diesem Block)

Unsere Steuerungsebene für den RSS-Block umfasst:

  • Ein Aktivierungs-/Deaktivierungsbit
  • Zwanzig 16-Bit-Schlüssel für den Toeplitz-Hash
  • Eine Umleitungstabelle mit 64 Einträgen

SmartNIC Shell-Blockdiagramm einer Beispielimplementierung des SmartNIC Shell-Frameworks. Hier wird der RSS-Block durch eine HLS-Implementierung ersetzt.

Schlussfolgerung

Die heutigen High-Level-FPGA-Entwicklungstools sind darauf ausgelegt, die Markteinführungszeit und die Abhängigkeit von Hardware-Ingenieuren zu verringern. Die Annahme, dass der Einsatz dieser Tools immer einen Kompromiss bei der Anwendungsleistung bedeutet - entweder bei der Geschwindigkeit oder bei den Siliziumressourcen - ist jedoch falsch.

Wir haben festgestellt, dass die Verwendung von HLS zur Entwicklung von IP-Blöcken für die SmartNIC Shell von BittWare die Entwicklungszeit von etwa einem Monat auf eine Woche reduziert hat. Außerdem haben wir festgestellt, dass weniger Gatter für die Implementierung benötigt werden.

Der Quellcode für den RSS-Block ist für Besitzer von XUP-P3R-Boards und Benutzer der SmartNIC Shell verfügbar. Er ist ein hervorragendes Beispiel dafür, wie AXI-Schnittstellen in HLS-Code verwendet werden können. Wenden Sie sich an einen BittWare-Mitarbeiter, um mehr zu erfahren.

HLS ist zusätzlich zu den nativen Entwicklungstools für alle BittWare FPGA-Boards verfügbar. Wir bieten auch eine Reihe von Boards an, die OpenCL-Entwicklung unterstützen - klicken Siehier, um mehr zu erfahren.