VHDL Code Style und DO-254
Checkliste für FPGA (VHDL)-Designs (nach DO-254)
Wie bei anderen Programmiersprachen, wie z.B. C, existieren auch für VHDL eine Reihe von Richtlinien, um gut les- und wartbaren Code zu schreiben, der zu stabilen und funktionssicheren Schaltungen führt. Vieles davon ist Geschmackssache sowie Ergebnis firmeninterner Festlegungen, jedoch hat sich auf der Basis von opensource Projekten wie z.B. auf open cores ein gewissen de facto Standard entwickelt. Darüber hinaus existieren inzwischen verschiedene branchenspezifische Standards, die als Vorgabe dienen können. Diese haben teilweise empfehlenden- als auch verpflichtenden Charakter.
In der Luftfahrtbranche gibt es z.B. mit DO-254 einen anerkannten Standard, der nicht nur "Schönheitsregeln" wie Namenskonventionen enthält, sondern auch Methoden zur sicheren Umsetzung von Taktübergängen und FSMs empfiehlt.
Grundsätzliches zur DO-254
Die DO-254 ist die bekannteste Spezifikation zur Zertifizierung komplexer digitaler Schaltungen im Bereich der sogenannten Avionik (Fluggeräte). An solche Schaltungen werden üblicherweise höhere Sicherheitsanforderungen gestellt, als bspw. in der Consumer-Elektronik. Sie entsprechen in vielen Bereichen den Vorgaben, wie sie in der Fahrzeugbranche (Automotive) und der Medizintechnik bekannt sind - gehen aber oftmals auch darüber hinaus. Die Aufwände und Kosten für Simulation, Reviews, etc. steigen im Vergleich zu Designs ohne besondere Sicherheitsanforderungen auch um 50% und mehr. Daher ist es um so wichtiger, bereits in der Designphase auf die erhöhten Anforderungen Rücksicht zu nehmen und mehr in die korrekte Formulierung zu investieren - auch im Hinblick darauf, dass eventuelle Fehler in Tests nicht- oder nur schwerlich erkannt werden können.
Das Grundthema der DO-254 ist die Qualitätssicherung, d.h. die Frage, wie man eine Entwicklung gestalten sollte, sodass das Produkt am Ende des Entwicklungsprozesses das Vertrauen in dessen Funktion rechtfertigt. Deshalb finden sich in der DO auch weniger technische Ratschläge oder Designspezifikationen, sondern vermehrt Methoden und Prozessbeschreibungen, die - in der Entwicklung und Verifikation sinngemäß angewandt - zu einem definierten und akzeptierten Grad an Vertrauen in die Funktionsfähigkeit der Produktes führen.
Die DO-254 beginnt mit der Beschreibung, wie Anforderungen (die sogenannten "requirements") erfasst, verfeinert und in einem Dokument festgehalten werden sollen. Alle weiteren im Entwicklungsprozess erstellten Dokumente wie Testspezifikation und -ergebnisse verweisen direkt auf diese Requirements.
Die DO-254 war eine der ersten grundlegenden Spezifikationen für digitale Schaltungen und Entwicklungsmethoden, welche auch weitgehende Vorgaben für VHDL und Verilog gemacht hat. Im Umfeld der DO-254 sind damit früh "Ratschläge" für "saubere" Designs entstanden, die auch außerhalb des Avionik-Umfeldes nützlich sein können.
Dieser Artikel beschränkt sich auf die Darstellung der VHDL-Richtlinien wie von der DO-254 usergroup erarbeitet und in dem Dokument "Best Practice VHDL Coding Standards for DO-254 Programs" aus dem Jahr 2010 festgehalten.
Aufbau der Richtlinien
Die designrules sind in 4 Kategorien unterteilt, die Gruppe bestimmt den Prefix der Regel. Beispielsweise SS-10 für die Zehnte Regel in der Kategorie "Safe synthesis"
- SS - Safe Synthesis
- CP - Coding Practice
- CDC - Clock domain Crossing
- DR - Code Reviews
Jeder Regel ist ein Schweregrad (severity) zugeordnet:
- Note
- warning
- Error
Ja nach Risikograd des designs kann die Einhaltung der Regeln auf einzelne Schweregrade beschrebkt sein. Es ist bei einigen Regeln angegeben das in Einzelfällen Verletzung der Regel in der Schwere herabgestuft (von Error auf Warning) werden kann, wenn diese Fälle entsprechend dokumentiert und begründet werden.
VHDL Schreibstil - Coding Practice (CP)
Die Regeln dieser Kategorie stellen sicher, dass ein Kodier-Stil benutzt wird, der auf bewährter Designpraxis im sicherheitskritischen Bereich beruht.
Vermeide Fehler bei Typ_Deklarationen (CP1)
- Kontrolliere den Code auf inkorrekten Gebrauch von Typen, nichtpassende Begrenzungen oder Randbedingungen.
- Schweregrad: Error
Entity exa is (dat_i: IN dword);
Architecture behave of exa is
signal m_index : std_logic_vector ( 13 downto 0);
--..
m_index <= dat_i(M_index_lo to m_index_hi);
end exa;
Sobald diese Fehler bei der Kompilierung entdeckt werden sollte man sie sofort korrigieren.
Vermeide doppelte Signalzuweisungen (CP2)
- Ein Signal soll innerhalb eines Statementsbereich nur einmal zugewiesen werden.
- Schweregrad: Error
IF reset = 1 then
ausgang <= '1' ;
blablup <= blupbla;
ausgang <= '0';
else
Vermeide numerischen Werte mitten im Code (CP3)
- Für Re-Use und einfacher Portierbarkeit sollten keine hart-codierte Zahlenwerte benutzt werden.
- Schweregrad: Warning
Vermeide von der Breite eines Vector abhängige Zuweisungen (CP4)
- Für Zuweisung während Reset keine hart-codierten Zahlenwerte verwenden
- Schweregrad: Note
Statt
data <= "00000000";
besser:
data <= (others => '0');
Die Zuweisung sollte in einer Weise geschehen, dass sie unabhängig von der Bit-Anzahl im Vektor ist. Das verringert den Änderungsaufwand bei neuen Vektorbreiten und erleichtert so die Design-Portierung.
Stelle sicher, dass alle FSM einheitlich codiert sind (CP5)
- a: In einem Design sollte ein einheitlicher Stil für alle Zustandsautomaten verwendet werden.
- b: der FSM Zustand sollten nicht hart codiert sein, außer unvermeidbar.
- Schweregrad: Error
Stelle sicher, dass die Zustandsübergänge sicher sind (CP6)
- a: Ein Zustandsautomat sollte einen definierten Reset-Zustand haben.
- b: Alle ungenutzen (illegale oder undefinierte) Zustände sollen in einen definierten Zustand übergehen. In diesem Zustand soll die fehlerursache angemessen bearbeitet werden
- c: Es sollen keine unerreichbaren und keine Sackgassen-Zustände in einem Zustandsautomaten geben.
- Schweregrad: Error
TYPE T_STATE_TYPE is (NOP, --default and reset state
START_TR,
BRST_CONT,
BRST_END
-- FGH --auskommentierter Zustand
);
-- ..
CASE state_q IS
WHEN NOP =>
IF (tr_req = '1' AND rnw = '1' ) THEN
next_state <= START_TR;
END IF;
--...
WHEN BRST_CONT => --Fehler: kein hinführender Übergang
next_state <= BRST_END;
WHEN BRST_END => --Fehler: kein verlassender Übergang
IF tr_req = '0' THEN
status <= ABRT;
END IF;
WHEN OTHERS =>
next_state <= FGH; --Fehler FGH ist nicht definiert
END CASE;
Vermeide unstimmige Intervallgrenzen (missmatching ranges) (CP7)
- Bit-breiten an beiden Seiten einer Zuweisung, Vergleich oder Assoziation sollten übereinstimmen.
- Schweregrad: Warning
Stelle sicher, dass die Sensitivity list vollständig ist (CP8)
- Die Sensitivity list sollte nur im process verwendete Signale enthalten.
- Schweregrad: Warning
Stelle sicher, dass Unterprogramme ordentlich sind (CP9)
Jedes Unterprogramm soll:
- nur einen Entrittspunkt,
- keine Rekursion,
- nur Zugriff auf lokale Variablen/Signale haben.
- Schweregrad: Warning
Weise Werte vor Nutzung zu (CP10)
- Jedem Objekt (Signal, Variable, Anschluß) sollte eine Wert zugewiesen werden bevor es benutzt wird.
- Schweregrad: Warning
Vermeide offene Eingänge (CP11)
- Jeder Eingang sollte von einem Anschluss, Signal oder Konstante getrieben werden.
- Schweregrad: Error
Vermeide offene Ausgänge (CP12)
- Design Ausgänge sollten verbunden sein
- Schweregrad: Note
Deklariere Objekte vor Nutzung (CP13)
- Objekte sollten vor der Benutzung deklariert werden
- Schweregrad: Error
Vermeide Ungenutzte Deklarationen (CP14)
- Alle deklarierten Objekte sollten benutzt werden.
- Schweregrad: Warning
Taktübergange - Clock Domain Crossing (CDC)
Dieser Abschnitt (der eine einzelne Regel enthält - Anm.d. Autors) behandelt potentielle Probleme in Designs mit mehreren Takt-Domänen und den dadurch entstehenden asynchronen Taktübergängen
Analysiere alle asynchronen Takte (CDC-1)
- Für jedes Design das mehrere zueinander asynchrone Takte hat oder in dem interne Takte generiert werden, ist eine umfassende Analyse der Taktübergänge durchzuführen.
Fehlerhaft realisierte Signalübergänge führen regelmäßig zu undefinierten Signalen und in Einzelfällen zu Metastabilität von Daten. Damit entsteht ein unbestimmtes Schaltungsverhalten, das stark nachteiligen Einfluss auf den Betrieb des Gerät hat. Diese Richtlinie muss hier erwähnt werden, auch wenn die Analyse von Taktübergängen fern des Fokus von VHDL-Code-Style-check-tools und diesem Dokument ist.
Beim Design-Review muss nach möglichen Taktübergängen gesucht werden und wenn diese aufgefunden wurden, nachgewiesen werden, dass diese korrekt synchronisiert wurden. Fehlverhalten durch inkorrekt ausgeführte Taktübergänge sind mit Testläufen an realer Hardware nur extrem schwierig einzugrenzen, deshalb ist die Designanalyse äußerst wichtig. Es ist oft unmöglich, das Fehlverhalten im Debugging-Prozess konsistent zu reproduzieren. Erschwerend kommt die Wirkung von Umgebungseinflüssen während des Betriebes hinzu: unbekannte kapazitive Lasten, die Temperatur des Die's sowie Potentialschwankungen zB. der Masse. Im schlimmsten Fall zeigt sich der Fehler unter den Testbedingungen nie, sondern nur unter realen Einsatzbedingungen (Flug).
Sichere Synthese - Safe Synthesis (SS)
Vermeide implizierte Logik (SS1)
- Erlaube keinen Code der feed-throughs, Schieberegister und interne Tristate-Treiber impliziert.
Stelle ordentliche Case-Anweisungen sicher (SS2)
Case statements sollen:
- a) Vollständig sein,
- b) keine doppelten oder überlappenden Anweisungen enthalten,
- c) keine nichterreichbare Anweisungen enthalten,
- d) immer eine "when others" Regel enthalten.
- Schweregrad: Error
Vermeide Kombinatorische Rückkoppelungen (SS3)
- In einem kombinatorischen Prozess ist kein Lesen und Zuweisen desselben Signals zulässig.
- Schweregrad: Error
Kombinatorische Rückkopplungen verursachen race-conditions und führen so zu nicht vorhersehbaren Verhalten.
Beispiel f. Verletzung:
fred_s <= gated_in_s OR pulse_r; --comb. loop at fred_s
P_GATED_IN: PROCESS(en_i, fred_s, pulse_r)
BEGIN
gated_in_s <= fred_s AND en_i;
IF (en_i = '0') THEN
gated_in_s <= NOT(fred_s);
ELSIF (pulse_r <= '1') THEN
gated_in_s <= '0';
END IF;
END PROCESS P_GATED_IN;
Vermeide automatische Einführung von latches (SS4)
- Der Codierstil sollte den automatischen Einbau von Latches verhindern.
- Schweregrad: Warning
Vermeide mehrfache Signalkurven - Avoid Multiple Waveforms (SS5)
- Nur eine Waveform soll auf der rechten Seite einer Signalzuweisung stehen. Beispiel Fehler:
write <= '1' after clk_prd, '0' after 8*clk_prd;
- Schweregrad: Error
In der Erklärung wird ausgeführt, dass eine Signalkurve ("waveform") aus einer Wertzuweisung ("assignment Value expression") und einer optionalen Verzögerung ("assignment delay expression") besteht. Mehrfache Signalkurven sind nicht synthetisierbar und daher stimmt Simulation und die synthetisierte Hardware für diese nicht überein. Leider kennt der Autor keine ihn zufriedenstellende Übersetzung für "multiple waveform". "Waveform" kann hier Signalform, Signalkurve heißen , was "multiple" am besten wiedergibt ist weitaus unklarer. Wörterbücher bieten "mehrfach", "mannigfaltig", "vielwertig" an.
Vermeide mehrfache Signaltreiber (SS6)
*Ein Signal/Variable soll nur in einem einzigen sequentiellen Block zugewiesen werden.
- Schweregrad: Error
Vermeide uninitialisierte VHDL Konstante (SS7)
- Stelle sicher, dass alle VHDL Konstanten initialisiert werden.
- Schweregrad: Warning
Vermeide die Nutzung des Taktes als Data (SS8)
- Takt-Signale sollen nicht in einem Logik-pfad den Daten-Eingang eines FlipFlops treiben.
- Schweregrad: Fehler
Vermeide gemeinsame genutzte Takt und Resetsignal (SS9)
- Ein Signal sollte nicht sowohl als Takt und auch als Resetsignal benutzt werden.
- Schweregrad: Error
Vermeide geschaltetet Takte (SS10)
- Datensignale sollten nicht in einen Logik-Pfad benutzt werden, der den CLK-Eingang eines Registers treibt.
- Schweregrad: Warning
Vermeide intern generierte Takte (SS11)
- Vermeide intern generierte Taktsignale.
- Schweregrad: Warning
Vermeide intern generierte Reset (SS12)
- Vermeide intern generierte Reset, außer sie sind ordentlich isoliert.
- Schweregrad: Warning
Vermeide Resets unterschiedlicher Polarität (SS13)
- Ein und dasselbe Resetsignal sollte nicht mit unterschiedlicher Polarität oder Stil benutzt werden.
- Schweregrad: Warning
Vermeide Register ohne Reset (SS14)
- Alle Register sollten eine reset-Steuerleitung haben.
- Schweregrad: Warning
Alle Register in den designs sollten einen expliziten Resetanschluss haben. Ein Reset-Signal wird benutzt, um ein Gerät in einen determinierten Zustand zu versetzen. Dieses vom system initierte "Error recovery" Kommando wird als letzte Rettungsmöglichkeit für nicht antwortende Systeme benutzt. ("is used as the last resort recovery mechanism for a non-responsive device.").
Ausnahmen bspw. synchronizer flops und scan chains sind möglich, sollten aber in jedem einzelnen Fall dokumentiert und begründet ("justify") werden. Mehrdimensionale signal/register, die als memory implementiert sind, sind von dieser Warning ausgenommen.
Vermeide asynchrone deaktivierten Reset (SS15)
- Resets sollten synchron freigegeben werden.
- Schweregrad:Fehler
Vermeide Zuweisung bei der Initialisierung (SS16)
- Verwende keine Register Initialisierung.
- Schwerergrad: Error
Vermeide Logik ohne Treiber oder Nutzung (SS17)
- a) Jedes Register oder Latch muss benutzt und getrieben werden.
- b) Register und Latches die nur unbenutzte Logik treiben, müssen untersucht werden.
Schweregrad:Error
Unbenutzte Register, Latches und sonstige Logik sind einfach toter Code. Toter Code kann nachteilig sein wenn das Design wieder benutzt wird und der tote Code während der Portierung unbeabichtigt aktiv wird. Die Auswirkungen von totem Code bei der Erstellung von Sicherheitskritische Design ist nicht vorhersehbar und kann sich auch deutlich je nach verwendeten Synthese tool, Release-Version und soga Computersystemen unterscheiden. Damit ist eine konsistente Abschätzung der Folgen nicht möglich.
Stelle sicher das Register steuerbar bleiben (SS18)
- Jedes Register soll über seine Eingänge steuerbar sein.
- Schweregrad: Warning
Vermeide tiefe kombinatorische Verschaltungen (SS19)
- Kombinatorische Pfade sollen nicht tiefer, als eine vorgegebene maximale Logiktiefe sein.
- Schweregrad: Warning
Die Ermittlung des kritischen Pfades ist schwieriger, wenn der Pfad viele Hierachie-Ebenen durchquert. Lange kombinatorische Pfad - auch snake paths genannt- können Syntheseprobleme verursachen. Das Projekt-Team sollte eine Obergrenze für die Anzahl von Hierarchie-Ebenen die ein Pfad durchqueren kann festlegen. Jeder längere Pfad soll als Regelverletzung gemeldet werden und an einer passenden Stelle durch Einfügen eines Register verkürzt werden.
Anm. FPGAkuechle: Die Regel lautet im Original "Avoid Snake Paths"; allerdings ist im deutschen Sprachgebrauch Schlangenpfad oder ähnliches völlig ungebräuchlich und wurde nicht für die Überschrift übernommen. Kritischer Pfad ist der Pfad zwischen zwei Registern, der die längsten Laufzeit aufweist.
Anm. von J.S.: Der Hintergrund des Problems der langen Pfade liegt physikalisch u.a. auch darin, dass mit der Länge und dem Ausmaß von Kombinatorik zwischen zwei Registern die Verletzlichkeit der Information durch Einflüsse wie Strahlung überproportional steigt und damit der Einfluss von Registern, die ein Vergleichen von Werten und somit eine Korrektur mittels Redundanz ermöglichen, relativ reduziert wird. So sind Fehlersimulationen und Testmustermengen weniger gut zu optimieren und praktisch anzuwenden, wenn es um Systemtests oder um system recovery geht. Lange Pfade verschlechtern auch die sogenannte "Fehlerbeobachtbarkeit" im Sinne der Erkennung von "stuck at" - Fehlern in der Produktion. Der gesamte Sachverhalt bezieht sich aber überwiegend auf ASICs. Bei FPGAs ist die Länge eines Pfades allein durch die VHDL-Beschreibung weniger gut bis gar nicht zu kontrollieren. Damit ist die Sinnhaftigkeit der Regel IMO infragegestellt. Man kann zudem beobachten, dass bei FPGAs die Verbindungen (interconnections) insgesamt als solche schon anfällig sind und sich weniger Unterschied durch lange oder kurze Leitungen zeigt, d.h. die Beschreibung kann nur wenig Einfluss auf die Struktur im FPGA nehmen. Umgekehrt kann man bei "langsamen" Schaltungen mit mehr Kombinatorik eine massive Reduzierung von Registerzellen bewirken, die sich als sehr strahlungsanfällig erwiesen haben.
Stelle Grenze für Verschachtelungen sicher (SS20)
- Bedingte Verzweigungen sind nur bis zu einer maximalen Verschachtelungstiefe gestattet.
- Schweregrad: Warning
Stelle stets gleiche Richtung der Vektorindizierung sicher (SS21)
- Benutze die selbe Multi-Bit Vektor Indizierung ("downto" vers. "to") im gesamten Design einheitlich.
- Schweregrad: Warning
Code-Durchsichten - Design Reviews (DR)
Verwende Labels für Statements (DR1)
- Case-Konstrukte, Always und initial blocks sollen Labels haben.
- Schweregrad: Note
Vermeide gemischte Groß-/Kleinschreibung zur Unterscheidung (DR2)
- Namen sollen nicht allein durch Groß- und Kleinschreibung unterscheidbar sein.
- Schweregrad: Warning
Anmerkung von J.S. Aus meiner Sicht müsste hier beim Thema FPGA eigentlich der Schweregrad "Error" gesetzt werden, weil eine Unterscheidbarkeit in VHDL so gar nicht gegeben ist. Werden zwei verschiedene Signale oder Variablen so formuliert, wird nur eine erzeugt, was in eine Kollision mündet, die bei Berücksichtigung anderer Programmierregeln - vor allem der Behandlung einer Variablen oder eines Signals in nur einem Prozess - direkt entdeckt wird. Damit darf man das Kritarium aber nicht etwas wegfallen lassen, den unter der Annahme der Nichtbefolgung dieser Regel, z.B. in Testbenches, kann eine unbeabsichtigte Doppelreferenzierung durchaus unentdeckt bleiben und äußert sich dann nur über ein funktionelles Fehlverhalten. Dieses muss aber nicht unbedingt auffallen. Dieser Fall wäre also durch diese Regel mit dem Zusatz "error" grundsätzlich zu vermeiden. Um diese Regel praktisch umzusetzen, empfehlen sich alphabetisch geordnete Variablen- und Signallisten, die man im Rahmen der Code-Dokumentation generiert und bei der solche Doppelbelegungen optisch auffallen.
Stelle eindeutige Namensräume sicher (DR3)
- Der selbe Name soll nicht für verschiedenen Typen von Identifiers benutzt werden.
- Schweregrad: Error
Nutze "Trennenden Deklarations Stil" (DR4)
- Eine Zeile pro Deklaration.
- Schweregrad: Note
Nutze "Trennenden Statement Stil" (DR5)
- Eine Zeile pro Anweisung.
- Schweregrad: Note
Stelle einheitlichen Einrückungen sicher (DR6)
- Code soll einheitlich eingerückt sein.
- Schweregrad: Note
Vermeide Tabulatoren (DR7)
- Tabulatoren sollen nicht benutzt werden.
- Schweregrad: Note
Vermeide lange Files (DR8)
- Das Design soll in Unterdesigns gegliedert sein und Dateien sollen nicht nach Belieben lang sein.
- Schweregrad: Warning
Stelle über Hierarchien einheitliche Signalnamen sicher (DR9)
- Signale und Busse sollen über das gesamte Design einheitliche Namen haben.
- Schweregrad: Warning
Stelle einheitlichen Datei-Header sicher (DR10)
- Stelle einheitliche File Header sicher.
- Schweregrad: Warning
Stelle ausreichende Kommentardichte sicher (DR11)
- Code soll durch inline Kommantare ausreichend dokumentiert sein.
- Schweregrad: Warning
Stelle ordentliche Platzierung von Kommentaren sicher (DR12)
- Kommentare sollten so platziert sein das sie das Verständniss unterstützen.
- Schweregrad: Note
Stelle Firmenspezifische Namensstandard sicher (DR13)
- Jede Firma oder project soll ihre eigenen Codestandards definieren und durchsetzen.
- Schweregrad: Note
Literatur
- [DOUG] DO-254 User Group Position Paper DO254-UG-001 „Best PracticeVHDL Coding Standards for DO-254 Programs, modified 2010-Sep-13
- [Fulton] Randall Fulton, Ray Vandermolen „Airborne Electronic Hardware Design Assurance - A practitioners Guide to RTCA/DO-254“, CRC Press 2015 ISBN: 978-1482206050
- [Synopsys] Synopsys Inc. White paper „Understanding DO-254 Compliance for the verification of Airborne Digital Hardware“