Forum: Mikrocontroller und Digitale Elektronik Fehlerwahrscheinlichkeit


von Error (Gast)


Lesenswert?

hallo,

eine kurze Frage zur Fehlerwahrscheinlichkeit beim Einsatz von Zeigern 
auf ein Array.

Gibt es für einen Controller nachweisbar einen Unterschied in der 
Fehlerwahrscheinlichkeit, wenn man einen Zeiger "wild" in einem Feld 
herumsprignen lässt im Vergleich zu einem stetigen Inkrementieren?

Man könnte ja beispielsweise alle Elemente eines Arrays aufaddieren 
wollen und den Zähler im ersten Fall der Reihe nach von [0] bis [n-1] 
laufen lassen, im zweiten Fall in zufälliger Reihenfolge, wobei 
natürlich jedes Feld nur einmal ausgelesen werden soll.

Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht?

von Error_2 (Gast)


Lesenswert?

Wenn du sicherstellst, dass jedes Elemant nur einmal gelesen wird, dann 
sollte es keinen Unterschied machen, ob durchgehend aufsteigend oder 
wild durch einander gelesen wird.
Mir stellen sich nur die Fragen:
1) Echte Zufallszugriffe beim Mikrocontroller? Wie willst du das 
umsetzen?
2) Warum sollte sich jemand die Arbeit machen mit Zufallszugriffen + 
check, auf Einzelzugriff pro Element, wenn er doch einfach alle Elemente 
der Reihe durchgehen kann.

von Falk B. (falk)


Lesenswert?

@Error (Gast)

>eine kurze Frage zur Fehlerwahrscheinlichkeit beim Einsatz von Zeigern
>auf ein Array.

Der Zugriff auf ein Array über einen Index ist sicherer, weil man den 
Index vor jedem Zugriff auf den gültigen Bereich prüfen kann.


 Beim Zugiff über einen einfachen Pointer mit Post/pre Dekrement etc. 
geht das nicht!

>Gibt es für einen Controller nachweisbar einen Unterschied in der
>Fehlerwahrscheinlichkeit, wenn man einen Zeiger "wild" in einem Feld
>herumsprignen lässt im Vergleich zu einem stetigen Inkrementieren?

Hmm, der formale Nachweis könnte einfacher und sicherer sein, weil 
deterministisch und einfacher strukturiert.

>laufen lassen, im zweiten Fall in zufälliger Reihenfolge, wobei
>natürlich jedes Feld nur einmal ausgelesen werden soll.

Dann brauchst du aber einen Algorithmus, der alle Indizes zufällig, aber 
dennoch jeweils einmalig erzeugt. Machbar, aber nicht einfacher.

>Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht?

Theoretisch vielleicht nicht, praktisch schon.

von Karl H. (kbuchegg)


Lesenswert?

Error schrieb:

> Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht?

?

Wenn du da unterschiedliche Ergebnisse kriegst, dann ist entweder 
tatsächlich in der CPU irgendwas grauslich kaputt (das würde sich aber 
auch an anderen Stellen bemerkbar machen) oder aber (die viel 
wahrscheinlichere Variante): du hast ganz banal einen Fehler im 
Programm.

Für die CPU ist ein Adressierung über Pointer nichts irgendwie 
spezielles. 2 Zahlen werden addiert. Erst danach trennen sich die Wege 
und in dem Fall wird das Ergebnis auf den Adressbus gelegt und vom 
Speicher der Wert von dieser Stelle abgerufen. Letzteres passiert aber 
auch, wenn du sequentiell durch das Array durchgehst. Der einzige 
Unterschied: die Addition, die in dem einen Fall aus irgendeinem 
berechnetem Wert besteht und im anderen Fall aus einer schnöden Addition 
mit einer Konstanten.

von maveric00 (Gast)


Lesenswert?

Hallo,

Falk Brunner schrieb:
> Der Zugriff auf ein Array über einen Index ist sicherer, weil man den
> Index vor jedem Zugriff auf den gültigen Bereich prüfen kann.
>
>  Beim Zugiff über einen einfachen Pointer mit Post/pre Dekrement etc.
> geht das nicht!

Da möchte ich doch wiedersprechen. Auch bei einem Post/Pre-Dekrement 
kann geprüft werden, ob der Pointer noch in den gültigen Bereich zeigt - 
zumindest insoweit, dass keine anderen Speicherbereiche betroffen sind, 
häufig auch exakt:
1
struct ArrayTest {
2
          int ArrayBeginn ;
3
          int Array[100] ;
4
          int ArrayEnde ;
5
       } ;
6
7
int SumArray (struct ArrayTest *Sum)
8
{
9
  int *Pointer ;
10
  int i ;
11
  int Summe ;
12
13
  Pointer = Sum->Array ;
14
  Summe = 0 ;
15
16
  for (i=0;i<100;i++) {
17
    if ((Pointer<=&(Sum->ArrayBeginn))||(Pointer>=&(Sum->ArrayEnde))) {
18
       printf ("Fehler\n") ;
19
       exit(0) ;
20
    } ;
21
    Summe += *(Pointer++) ;
22
  } ;
23
  return (Summe) ;
24
} ;

Alternativ braucht man auch keine Zählvariable, sondern bricht ab, wenn 
man das Ende erreicht hat:
1
int SumArray (struct ArrayTest *Sum)
2
{
3
  int *Pointer ;
4
  int Summe ;
5
6
  Summe = 0 ;
7
8
  for (Pointer=Sum->Array;Pointer<&(Sum->ArrayEnde);Pointer++) {
9
    if ((Pointer<=&(Sum->ArrayBeginn))||(Pointer>=&(Sum->ArrayEnde))) {
10
       printf ("Fehler\n") ;
11
       exit(0) ;
12
    } ;
13
    Summe += *Pointer ;
14
  } ;
15
  return (Summe) ;
16
} ;

oder ohne Struktur (wenn bekannt ist, dass das Array 100 Elemente 
enthält:
1
int SumArray (int *Array)
2
{
3
  int *Pointer ;
4
  int Summe ;
5
6
  Summe = 0 ;
7
8
  for (Pointer=Array;Pointer<&(Array[100]);Pointer++) {
9
    if ((Pointer<Array)||(Pointer>&(Array[100]))) {
10
       printf ("Fehler\n") ;
11
       exit(0) ;
12
    } ;
13
    Summe += *Pointer ;
14
  } ;
15
  return (Summe) ;
16
} ;

Dabei sind allerdings zur vollständigen Sicherheit noch weitere 
Kleinigkeiten zu beachten, wie z.B. dass die Überprüfung des Pointers 
sowie das Auslesen des Wertes zusammen als eine Atomic Operation 
durchgeführt werden muss.

Ich würde sogar so weit gehen, dass das sequenzielle Auslesen des 
Speichers mit Pre- oder Postinkrement sogar weniger fehleranfällig ist, 
da im Fall der Indizierung zwei Variablen fehlerhaft sein könnten (der 
Pointer und der Index) und eine (oder mehrere) zusätzliche Operation(en) 
(Addition des Index auf den Pointer) notwendig sind.

Insgesamt unterscheiden sich die beiden Varianten allerdings nur 
marginal, wenn sie richtig implementiert werden. Handelt es sich um 
sicherheitsrelevante Anwendungen, kann man über einen Speicherschutz 
nachdenken (entweder in Hardware - ECC - oder in Software - Redundante, 
möglichst komplementäre Ablage, oder sogar durch redundante, sich im 
Wertebereich unterscheidende Berechnung). Dann ist aber auch ein 
Befehlssatzcheck sowie mindestens ein externer Watchdog angeraten.

Schöne Grüße,
Martin

von maveric00 (Gast)


Lesenswert?

Hallo,

kleine Korrektur (typischer C-Fehler ;-)):

in der letzten Funktion muss es natürlich
1
if ((Pointer<Array)||(Pointer>&(Array[99]))) {

heißen.

Schöne Grüße,
Martin

von Error (Gast)


Lesenswert?

...aber ist nicht ein Pointer nichts anderes als ein Datum auf dem 
Adressbus?

Wenn ich nun ein Array derart auslese, dass ich den Adressbus in Form 
eines Gray-Codes belege, also so, dass sich nach Möglichkeit von Adresse 
zu Adresse immer nur ein Bit ändert, dann stelle ich mir schon vor, dass 
insgesamt weniger Bitfehler auftreten würden...

...Oder verliert der Adressbus nach jedem Zugriff seine Daten absolut 
vollständig?

von Peter D. (peda)


Lesenswert?

Ich bevorzuge auch die Index-Variante. Dann kann man die Zugriffe bequem 
absichern:
1
  if( idx >= sizeof(reply_time) / sizeof(uint16_t) )    // array guard
2
    return ERROR1;
3
  reply_time[idx] = time;

von Karl H. (kbuchegg)


Lesenswert?

Error schrieb:
> ...aber ist nicht ein Pointer nichts anderes als ein Datum auf dem
> Adressbus?

Ein Arrayzugriff über Index ist aber auch nichts anderes als ein Datum 
auf dem Adressbus. Lediglich die Schreibweise ist eine andere.

> Wenn ich nun ein Array derart auslese, dass ich den Adressbus in Form
> eines Gray-Codes belege, also so, dass sich nach Möglichkeit von Adresse
> zu Adresse immer nur ein Bit ändert, dann stelle ich mir schon vor, dass
> insgesamt weniger Bitfehler auftreten würden...

Da dürfen sowieso keine Bitfehler auftreten!
Auf dieser Ebene muss alles 100% perfekt funktionieren, ansonsten ist 
das alles unbrauchbar. Wenn von der CPU der Befehl kommt, das Byte von 
der Adresse 100 zu lesen, dann muss da auch das Byte von der Adresse 100 
gelesen werden. Und wenn der nächste Befehl von 105 lesen will, dann 
muss auch von 105 gelesen werden.

Was tust du denn wenn 2 Variablen einmal zufällig hintereinander im 
Speicher liegen und bei einer Programmänderung kommen sie getrennt 
voneinander zu liegen? Das eine mal funktioniert der Zugriff und das 
andere mal nicht? Oder wie?
Wenn die CPU das Byte von einer bestimmten Adresse anfordert, dann muss 
das klappen (sofern es den Speicher überhaupt an dieser Adresse gibt). 
Und zwar 100% und zu jeder Zeit. Alles andere ist ein 
Zufallszahlengenerator aber kein Computer.

von maveric00 (Gast)


Lesenswert?

Hallo,

Error schrieb:
> ...Oder verliert der Adressbus nach jedem Zugriff seine Daten absolut
> vollständig?

So ist es. Gray-Code würde sich nur dann anbieten, wenn er extern 
überwacht wird. Dann kann man aber auch einen einfachen Zähler 
einsetzen. Insgesamt eignet sich der Gray-Code nicht wirklich zur 
Fehlererkennung, da die Hamming-Distanz weiterhin nur 1 beträgt. 
Allerdings ist er gut geeignet (und dafür auch entwickelt worden), um 
bei einer parallelen Datenübertragung (oder Sensorauswertung) 
ungewünschte Zwischenzustände zu vermeiden. Ein Einsastzgebiet sind z.B. 
digitale Weg- oder Winkelencoder.

Schöne Grüße,
Martin

von maveric00 (Gast)


Lesenswert?

Hallo,

Karl Heinz Buchegger schrieb:
> Und zwar 100% und zu jeder Zeit. Alles andere ist ein
> Zufallszahlengenerator aber kein Computer.

Naja, bei komplexeren Microcontrollern kommt man schon auf Fehlerraten 
im Bereich von 1000-3000 FIT, also 1 bis 3 Fehler alle 10^6 
Betriebsstunden. Wenn man entsprechend viele davon einsetzt (z.B. im 
Automobilbereich), dann muss man sich hier schon Gedanken machen, wie 
mit Fehlern umzugehen ist - und dabei gibt es dann kein "darf nicht" 
oder "kann nicht".

Oder um es 'mal so auszudrücken: Wenn in jedem Fahrzeug im Schnitt 3 
sicherheitsrelevante Steuergeräte verbaut sind (Motor, ABS, Airbag), 
kommt es bei 43 Mio PKW in Deutschland bei einer durchschnittlichen 
Betriebszeit von rund 500 Stunden im Jahr zu rund 60.000 Fehlern im 
Jahr.

Die ISO 26262 schreibt vor, dass man für wirklich sicherheitskritische 
Fehler, die in mindestens 10% der Fahrsituationen zu einem tödlichen 
Unfall führen können und in denen der Fahrer nicht reagieren kann (ASIL 
D) einen Diagnostischen Deckungsgrad von 99% erreichen muss. Damit wären 
aber 600 Fehler pr Jahr potentiell tödlich, wovon dann statistisch 
mindestens 60 tatsächlich mit einem tödlichen Unfall enden müssten.

Gut, dass das die Steuergeräte nicht wissen, und die tatsächliche Rate 
erheblich darunter liegt...

Besonders deswegen, da in modernen Fahrzeugen in der Regel von den bis 
zu 70 eingesetzten Steuergeräten 5 bis 10 als sicherheitsrelevant 
einzustufen sind.

Insofern macht es schon Sinn sich zu überlegen, wie man die Berechnungen 
in einem µC auch auf niedrigster Ebene überprüfen kann - die angedachte 
pseudozufälligen Zugriffe sind jedoch dafür leider nicht geeignet.

Schöne Grüße,
Martin

von Uwe B. (derexponent)


Lesenswert?

Hi Martin,

wenn du davon ausgehen "musst", das in der CPU beim
Zugriff auf die Register oder den Speicher Fehler auftreten,
dann spielt es keine Rolle in welcher Weise du darauf zugreifst

der Fehler wird in jedem Fall auftreten

die einzigste Möglichkeit solche Fehler zu erkennen
und evenentuell zu korrigieren besteht darin,
das ganze mehrfach redundant auszulegen

das kannst du dann beliebig "Kompliziert" gestallten
(mehrere Zugriffe, mehrere CPUs, mehrere Compiler usw)

Gruss Uwe

von oszi40 (Gast)


Lesenswert?

Uwe B. schrieb:
> das ganze mehrfach redundant auszulegen

Dann wäre noch näher zu untersuchen wie sich das Gerät nach kurzem 
Spannungsausfall verhält. Entweder fängt es definiert bei Punkt NULL an 
oder irgenwo mittendrin?? Da nützen auch 100 CPUs nichts wenn die 
Strategie falsch war.

von Jürgen D. (poster)


Lesenswert?

Na da wird es schon Techniken geben die das wieder sincron bekommen.
In der Luftfahrt sind ja auch redundante Systeme üblich.
Und da glaube ich nicht das sich alle Triebwerke im Flug abschalten und 
eine Windows Loginfenster im Amaturenfeld erscheint :)

von Uwe B. (derexponent)


Lesenswert?

>Dann wäre noch näher zu untersuchen wie sich das Gerät nach kurzem
>Spannungsausfall verhält. Entweder fängt es definiert bei Punkt NULL an
>oder irgenwo mittendrin?? Da nützen auch 100 CPUs nichts wenn die
>Strategie falsch war.

mir ging es nur um das Eingangsthema vom TE :

"Fehlerwahrscheinlichkeit beim Einsatz von Zeigern"

und wollte damit sagen das es meiner Meinung nach egal ist
wie die Software den Zugriff handelt, wenn von einem Hardwarefehler
in der CPU ausgegangen wird.

und ich glaube auch nicht das sich bei 100 CPUs zu einem Zeitpunkt x
bei allen genau der gleiche Fehler zeigt...
man hat dann halt 99 CPUs die richtig funktionieren und die müssen die 
"defekte" dann halt erkennen und ausklammern

aber wie gesagt, ist ein beliebig kompliziertes Thema

von maveric00 (Gast)


Lesenswert?

Hallo,

in der Praxis wird man wie so oft ein abgestuftes Verfahren einsetzen; 
je nach maximaler Kritikalität mit unterschiedlicher Tiefe und 
Komplexität. Ein Beispiel könnte so aussehen:

Programm läuft auf einem µC; dieser überprüft die ihm zugängliche 
Peripherie und sich selber so weit wie möglich (AD-Wandler durch 
Referenzspannungen, Speucher durch ECC oder redundante Ablage, 
Befehlssatz durch Durchführen einer Kontrollrechnung, Programmablauf).

Er wiederum wird von einem externen Element überwacht (kleiner 
Zweitprozessor, Dual-Core-Lockstep, intelligenter Watchdog), welches bei 
einer Abweichung des Verhaltens (auch das Zeitverhalten) des 
Hauptprozessors vom Erwarteten  unabhängig von diesem einen sicheren 
Zustand herstellen kann (je nach der Komplexität des sicheren Zustands 
richtet sich dann auch die Komplexität der Überwachungseinheit).

Ergänzend wird die Versorgungsspannung der beiden überwacht und bei 
Unter- oder Überspannung der sichere Zustand ausgelöst.

Dies entspricht dann im Groben dem VDA-E-Gas-Konzept; die Details 
auszuarbeiten ist allerdings knifflig, und dabei kann es dann durchaus 
auch auf die Art des Zugriffs ankommen, da dieser zumindest bei dem 
Befehlssatzcheck berücksichtigt sein muss (register indirect; register 
indirect with index und/oder Art des Pipelinings). Ganz interessant wird 
die Zugriffsmethode natürlich dann, wenn Caches mit im Spiel sind. Dabei 
dürfte die zufällige Methode dann tatsächlich eine leicht höhere 
Fehlerwahrscheinlichkeit haben, da es dann häufiger zu Cache-Miss und 
Cache-Fill-Szenarien kommt.

Zum Thema 100 Prozessoren: Die in der Luft-/Raumfahrt üblichen Methodik, 
mehrere Systeme parallel arbeiten zu lassen, sind natürlich (unter der 
Voraussetzung, dass sie nicht redundant, sondern divergent ausgeführt 
sind) ebenfalls zielführend. Hier ergeben sich dann - neben den 
gewaltigen Kosten - ganz andere Problematiken bezüglich Synchronisation 
und der Entscheidung, wer nun recht hat...

Insofern kann ich mich meinem Vorredner anschließen: "ist ein beliebig 
komplexes Thema" ;-)

Schöne Grüße,
Martin

von Birge Bit (Gast)


Lesenswert?

Error schrieb:
> Man könnte ja beispielsweise alle Elemente eines Arrays aufaddieren
> wollen und den Zähler im ersten Fall der Reihe nach von [0] bis [n-1]
> laufen lassen, im zweiten Fall in zufälliger Reihenfolge, wobei
> natürlich jedes Feld nur einmal ausgelesen werden soll.
>
> Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht?

Ja.
Ich habe häufig mit parallelen Interfaces am FPGA zu tun (DDR-RAM, ADC).
Die Testpattern die die meisten Fehlern provozieren sind, die aus einem 
Pseudo-Noise- Generator. Wenn man genauer hinschaut da sind die 
Arbeitsphasen am kritischsten bei dem die meisten bits wackeln.
also sechsel von adresse 0x0000 auf 0xFFFF und zurück wäre auch so was 
kritisches. Da sackt auch die betriebsspannung ab, das kann schon in 
kritische bereiche (-5%) gehen. Beliebt sind auch Muster wie 01010, da 
kann das Übersprechen zwischen den leitungen Bits kippen lassen.

MfG

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.