Welche Tools oder Standards können verwendet werden, um die Zuverlässigkeit von eingebettetem C-Code zu verbessern?

Normalerweise programmiere ich PICs in C, normalerweise für Schaltwandler. Ich habe von verschiedenen statischen Analysewerkzeugen und Standards wie MISRA C gehört , die verwendet werden können, um die Zuverlässigkeit von Code zu verbessern. Ich möchte mehr wissen. Welche Standards oder Tools könnten für meinen Kontext geeignet sein?

Wie eingestellt sind Sie auf die C-Sprache?
Ich könnte überredet werden, zu etwas anderem zu wechseln, wenn es einen sehr guten Grund dafür gäbe. Aber es müsste ein sehr guter Fall sein.
„Ein sehr gutes Argument“ für den Umstieg von C ist auf die Schnelle nicht zu finden, für den PIC womöglich noch gar nicht. Für AVR, ARM oder MSP430 wäre Ada einen ernsthaften Blick wert (trotz der Negativität, die es anzieht, wie Sie sehen können!) Und für High Rel ist SPARK einen Blick wert.
Als Hintergrundinformationen könnten Sie diese interessant finden: SPARK vs. MISRA-C spark-2014.org/entries/detail/… und diese fortlaufende Fallstudie: spark-2014.org/uploads/Nosegearpaper_1.pdf
Vielleicht ist es besser, Zeit zu investieren, um Argumente für den Wechsel vom PIC zu etwas Modernem zu machen ... Besonders wenn Sie die Art von unternehmenskritischen Systemen entwerfen, für die MISRA und SPARK ursprünglich gedacht waren.

Antworten (5)

Die Validierung von eingebettetem Code ist schwierig, insbesondere wenn es um Teile mit begrenzten Ressourcen wie PICs geht. Sie haben oft nicht den Luxus, in Testfällen zu programmieren, aufgrund der Speicherbeschränkungen des Teils und der oft "Echtzeit" -Programmierung, die auf dieser Art von Geräten durchgeführt wird.

Hier sind einige meiner Richtlinien:

  1. Schreiben Sie eine Spezifikation, wenn keine vorhanden ist: Wenn Sie nicht nach einer Spezifikation codieren, dokumentieren Sie, was Ihr Code leisten soll, was gültige Eingaben sind, was erwartete Ausgaben sind, wie lange jede Routine dauern sollte, was kann und was nicht verprügelt usw. - eine Theorie der Funktionsweise, Flussdiagramme, alles ist besser als nichts.

  2. Kommentieren Sie Ihren Code: Nur weil etwas für Sie offensichtlich ist, bedeutet das nicht, dass es für jemand anderen offensichtlich (oder richtig) ist. Klartextkommentare sind sowohl für die Überprüfung als auch für die Wartbarkeit des Codes erforderlich.

  3. Codieren Sie defensiv: Fügen Sie nicht nur Code für normale Eingaben hinzu. Behandeln Sie fehlende Eingaben, Eingaben außerhalb des zulässigen Bereichs, mathematische Überläufe usw. – je mehr Ecken Sie mit Ihrem Codedesign abdecken, desto weniger Freiheitsgrade hat der Code bei der Bereitstellung.

  4. Verwenden Sie statische Analysetools: Es kann demütigend sein, wie viele Fehlertools wie PC-Lint in Ihrem Code finden können. Betrachten Sie einen sauberen statischen Analyselauf als guten Ausgangspunkt für ernsthafte Tests.

  5. Peer-Reviews sind unerlässlich: Ihr Code sollte sauber und gut dokumentiert sein, damit er effizient von einer unabhängigen Partei überprüft werden kann. Überprüfen Sie Ihr Ego an der Tür und prüfen Sie ernsthaft jede Kritik oder jeden Vorschlag.

  6. Testen ist unerlässlich: Sie sollten Ihre eigene Validierung durchführen und den Code unabhängig validieren. Andere können Ihren Code auf eine Weise knacken, die Sie sich unmöglich vorstellen können. Testen Sie jede gültige Bedingung und jede ungültige Bedingung, die Ihnen einfällt. Verwenden Sie PRNGs und speisen Sie Mülldaten ein. Tun Sie alles, um Dinge zu beschädigen, reparieren Sie sie und versuchen Sie es erneut. Wenn Sie Glück haben, können Sie Ihren Code im Debug-Modus ausführen und einen Blick auf Register und Variablen werfen - wenn nicht, müssen Sie schlau sein und LEDs / digitale Signale umschalten, um eine Vorstellung vom Zustand Ihres Codes zu bekommen Gerät. Tun Sie alles Notwendige, um das Feedback zu erhalten, das Sie benötigen.

  7. Schauen Sie unter die Haube: Scheuen Sie sich nicht, den von Ihrem C-Compiler generierten Maschinencode zu betrachten. Sie können (werden?) Stellen finden, an denen Ihr schöner C-Code in Dutzende, wenn nicht Hunderte von Operationen gesprengt wurde, wo etwas, das sicher sein sollte (da es nur eine Codezeile ist, richtig?), so lange braucht, um mehrere Interrupts auszuführen gefeuert und die Bedingungen ungültig gemacht haben. Wenn etwas schrecklich ineffizient wird, refaktorisieren Sie es und versuchen Sie es erneut.

+1 Alle fundierten Ratschläge. Ich würde erwarten, dass jeder professionelle Firmware-Entwickler nur lächelt und nickt, wenn er dies liest.
Ein wichtiger Aspekt von Peer-Reviews ist, dass es bei der Überprüfung um den Code geht, nicht um den Programmierer. Wenn Sie Ihren Code mit Begriffen wie „zuerst mache ich das, dann mache ich das“ analysieren, sind Sie wahrscheinlich in Schwierigkeiten. "Zuerst macht der Code dies, dann macht er das" ist die richtige Denkweise. Und auch für Reviewer gilt: nicht „warum hast du das gemacht?“, sondern „warum macht der Code das?“.
Sie könnten auch Folgendes hinzufügen: 1. Verwendung der zyklomatischen Komplexitätsprüfung 2. Versionskontrollsoftware

Die meisten Techniken zum Erstellen zuverlässiger Software auf einem PC sind auch auf die Embedded-Entwicklung anwendbar. Es ist hilfreich, Ihre Algorithmen vom hardwarespezifischen Code zu trennen und diese separat mit Komponententests, Simulationen, statischen Analysen und Tools wie Valgrind zu testen. Auf diese Weise gibt es viel weniger Code, der nur auf der Hardware getestet wird.

Ich würde C nicht aufgeben. Sprachen wie Ada können zwar einige geringfügige Garantien bieten, aber es ist leicht, in die Falle zu tappen, zu glauben, dass die Sprache mehr verspricht, als sie tatsächlich tut.

Valgrid könnte jedoch für den PC etwas relevanter sein als für eine 8-Bit-MCU :)
Leider sind einige Techniken zum Erstellen guter Software auf PC-Ebene für kleine Mikros sehr ungeeignet, und einige Praktiken, die im PC-Bereich als schlecht und falsch gelten, sind in einer eingebetteten Umgebung durchaus akzeptabel.

MISRA-C ist in der Tat sehr nützlich, um die allgemeine Codequalität zu verbessern und Fehler zu minimieren. Stellen Sie einfach sicher, dass Sie jede Regel lesen und verstehen, die meisten davon sind gut, aber ein paar von ihnen machen keinen Sinn.

Hier eine Warnung. Das MISRA-Dokument geht davon aus, dass der Leser jemand mit umfassenden Kenntnissen der Sprache C ist. Wenn Sie keinen solchen hartgesottenen C-Veteranen in Ihrem Team haben, sich aber für einen statischen Analysator entscheiden und dann blind jeder gegebenen Warnung folgen, wird dies höchstwahrscheinlich zu Code mit geringerer Qualität führen , da Sie möglicherweise die Lesbarkeit verringern und versehentlich Fehler einführen. Ich habe das schon oft erlebt, Code in MISRA-Konformität umzuwandeln ist keine triviale Aufgabe.

Es gibt zwei Versionen des MISRA-C-Dokuments, die anwendbar sein können. Entweder MISRA-C:2004, das immer noch der aktuelle De-facto-Standard der Embedded-Industrie ist. Oder das neue MISRA-C:2012, das den C99-Standard unterstützt. Wenn Sie MISRA-C noch nie verwendet haben, würde ich Ihnen empfehlen, letzteres zu implementieren.

Beachten Sie jedoch, dass sich Tool-Anbieter normalerweise auf MISRA-C:2004 beziehen, wenn sie sagen, dass sie eine MISRA-Prüfung haben (manchmal beziehen sie sich sogar auf die veraltete Version von MISRA-C:1998). Soweit ich weiß, ist die Toolunterstützung für MISRA-C:2012 noch eingeschränkt. Ich denke, nur einige statische Analysatoren haben es bisher implementiert: Klocwork, LDRA, PRQA und Polyspace. Könnte mehr sein, aber Sie müssen auf jeden Fall überprüfen, welche Version von MISRA unterstützt wird.

Bevor Sie sich entscheiden, können Sie natürlich damit beginnen, das MISRA-Dokument zu lesen und zu sehen, was es beinhaltet. Es kann für 10 £ bei misra.org gekauft werden , was im Vergleich zu den Preisen für ISO-Standards recht erschwinglich ist.

Mathworks (die MATLAB-Leute) haben ein statisches Codeanalysetool namens Polyspace .

Neben statischer Codeanalyse, Lint und dergleichen würde ich eine sorgfältige Definition und Gestaltung von Schnittstellen (mit einem formellen Überprüfungsprozess) und eine Analyse der Codeabdeckung vorschlagen.

Vielleicht möchten Sie sich auch die Richtlinien für sicherheitskritisches Codedesign ansehen, darunter MISRA, aber auch die Standards UL1998 und IEC 61508.

Ich empfehle nicht, sich IEC 61508 zu nähern, es sei denn, Sie müssen. Es erwähnt zwar Software, aber es fehlen moderne, wissenschaftliche Quellen für seine Behauptungen. Dieser Standard kam 30 Jahre zu spät - wäre er wie die meisten seiner sogenannten "Quellen" in den 70er Jahren veröffentlicht worden, wäre er vielleicht nützlich gewesen.

Für eine vollständige Antwort auf diese Frage würde ich den Gedanken an „Code-Zuverlässigkeit“ unterdrücken und stattdessen an „Design-Zuverlässigkeit“ denken, da der Code nur der endgültige Ausdruck des Designs ist.

Beginnen Sie also mit den Anforderungen und schreiben und überprüfen Sie diese. Wenn Sie kein Anforderungsdokument haben, zeigen Sie auf eine zufällige Codezeile und fragen Sie sich: "Warum wird diese Zeile benötigt?" Die Notwendigkeit einer Codezeile sollte sich schließlich auf eine Anforderung zurückführen lassen, selbst wenn es so einfach/offensichtlich ist wie „das Netzteil muss 5 VDC ausgeben, wenn der Eingang zwischen 12 und 36 VDC liegt“. Eine Möglichkeit, darüber nachzudenken, ist: Wenn diese Codezeile nicht zu einer Anforderung zurückverfolgt werden kann, woher wissen Sie dann, dass es sich um den richtigen Code handelt oder dass er überhaupt benötigt wird?

Überprüfen Sie als Nächstes Ihr Design. Es ist in Ordnung, wenn es vollständig im Code steht (z. B. in Kommentaren), aber das macht es schwieriger zu wissen, ob der Code das tut, was wirklich gemeint ist. Beispielsweise kann der Code eine Zeile enthalten, die lautet: output = 3 * setpoint / (4 - (current * 5));Ist current == 4/5eine gültige Eingabe, die einen Absturz verursachen könnte? Was ist in diesem Fall zu tun, um die Division durch Null zu verhindern? Vermeiden Sie die Operation ganz oder verringern Sie stattdessen die Ausgabe? Wenn Sie in Ihrem Designdokument einen allgemeinen Hinweis zum Umgang mit solchen Grenzfällen haben, ist es viel einfacher, das Design auf einer höheren Ebene zu überprüfen. Die Codeinspektion ist jetzt einfacher, da überprüft werden muss, ob der Code dieses Design korrekt implementiert.

Außerdem sollte die Codeinspektion nach häufigen Fehlern suchen, die Ihre IDE nicht abfängt (Sie verwenden eine IDE, richtig?), wie z. B. „=“, wenn Sie „==“ meinten, fehlende Klammern, die die Bedeutung von „if“ ändern '-Anweisungen, Semikolons, wo sie nicht sein sollten, etc.

Während ich dies schreibe, fällt mir auf, dass es wirklich schwierig ist, jahrelanges Training/Erfahrung im Bereich Softwarequalität in einem einzigen Beitrag zusammenzufassen. Ich schreibe Code für medizinische Geräte und das Obige ist eine extrem vereinfachte Zusammenfassung unserer Herangehensweise.

Meines Wissens wird der Codeteil in einem medizinischen Gerät fast so getestet, als wäre es ein separates Gerät. Ist das genau?
@ScottSeidman Wahrscheinlicher ist, dass es auf Anforderungsbasis getestet wird, wie in dieser Antwort erwähnt. Für jede Anforderung sollten Sie ein Codemodul haben, und für jedes solche Codemodul sollten Sie einen Test haben. Im Wesentlichen hat also jede Anforderung einen entsprechenden Test und der Code ist das Mittel, um die Anforderung zu erfüllen. Diese Art der Anforderungsverfolgung ist in allen unternehmenskritischen Systemen gängige Praxis, lange bevor das Schlagwort „TDD“ auftauchte.
Ich bezog mich speziell auf FDA-Richtlinien wie fda.gov/downloads/RegulatoryInformation/Guidances/ucm126955.pdf Software erfordert tatsächlich mehr als Sie vielleicht denken, wenn sie Teil eines Medizinprodukts ist, beginnend mit der Planungsphase und den Designkontrollen.
Scott, so habe ich das noch nie gesehen, aber du hast Recht. Unsere Mitarbeiter der Software-Qualitätssicherung verifizieren die Software getrennt vom Rest des Systems (so weit wie möglich), bevor sie sie an eine andere Gruppe übergeben, die für die Systemverifizierung und -validierung verantwortlich ist.