@Ceos: ja stell mal rein....
Ich "tratsche mal noch ein bissle" 
Zum Thema Codetest statisch oder wie auch immer: wie Aussagekräftig ist das eigentlich ?
Ich hatte 15 Jahre lang den gleichen "funktionierenden" Code für die RS232 verwendet.
Der lief auf meinen PICs und auf auch dem PC mit INTEL 80xx
bis ich dann auf ARM umgestiegen bin, plötzlich traten merkwürdige Fehler auf:
Das beschreibt recht gut wie wenig Aussagekräftig ein Test, ja sogar ein Unittest sein kann, wenn er nicht direkt auf dem Zielsystem (Prozessor) ausgeführt wird.
Der Comiler hat seine Aufgabe richtig getan, aber die Systemarchitektur macht einem plötzlich einen Strich durch die Rechnung:
Wer lange Weile hat (is ja Wochenende) kann sich das gerne mal anschauen, was ich damals ermittelt habe:
Ein Zeichen aus dem Empfangspuffer holen:
Code:
U8 uart_getc(void)
{ U8 value;
if (rx_count)
{
value = rx_buffer[rx_out];
if (++rx_out >= UART_RX_BUFFER_SIZE) rx_out = 0;
rx_count--;
}
return value;
}
dazu muss man wissen, was der Compiler für einen Assembler Code generiert hat: aus rx_count-- wird beim LPC1768 (ARM Code)
LDR r0,[r2, #0x14] ; lade r0 mit dem Wert rx_count
SUBS r0,r0,#1 ; ziehe von r0 eins ab
STR r0,[r2,'0x014] ; speicher den Wert an rx_count
Nehmen wir an, rx_count steht auf 1. Nun gelangen wir in den Assemblercode 2.te Zeile. im r0 Register befindet sich dann der Wert 1. In dem Moment tritt ein Receive Interrupt auf. Die Software verzweigt sofort zur Interrupt Service Routine.
Dort steht folgender Code:
Code:
void uart_isr(void)
{
rx_buffer[rx_in]=databyte;
if (++rx_in >= UART_RX_BUFFER_SIZE) rx_in = 0;
rx_count++;
}
rx_count wird also am Ende der Interrupt Routine um 1 erhöht.
jetzt kehrt die Software zurück zur Routine uart_getc.
zur Info: rx_count stand auf 1 und wurde im Interrupt nun auf 2 erhöht. Im Register r0 steht aber noch der Wert 1, dieser wurde ja zuvor in der funktion uart_getc geladen. Nun zieht die Software in uart_getc eins ab. im r0 register steht nun 0. Dieser Wert wird dann zurück an die Speicherstelle rx_count geschrieben. Das wars es schon, alles durcheinander. Es befindet sich ein neues Zeichen im Puffer und rx_count steht auf 0.
Damit dies nun nicht mehr passiert, werden vor dem Verändern der variablen rx_count alle Interrupts gesperrt. Im Prinzip bräuchte man nur den RX Interrupt sperren. Dann wird der vollständige Code
rx_count-- in jedem Fall ausgeführt. Danach werden die Interrupts
wieder eingeschaltet.
Selbiges Problem tritt auch beim TX Interrupt auf.
Auch hier werden vor dem Verändern der Variablen tx_count alle Interrupts gesperrt.
Bei einem PIC zum Beispiel tritt dieses Problem normalerweise nicht auf, da er ein pre oder post increment direkt in einem Befehl ausführt, da kann also keiner "dazwischen" funken.
Darauf sollte man sich aber auch hier nicht verlassen, wer weis schon was der C-Compiler daraus macht.
Je nach verwendeten Prozessor kann es also funktionieren oder auch nicht. Wenn der Prozessor zum Beispiel einen 2 Assembler Befehl erzeugt, wäre auch kein Problem vorhanden: Beispiel:
hier kann das mov R0,@rx_count ; lade R0 Register mit der Adresse von rx_count
inc [R0] ; erhöhe die Speicherstelle worauf R0 zeigt
auch hier kein Promlem, weil das indirekte increment nicht unterbrochen werden kann.
da es in einem Befehl ausgeführt wird.
Bei einem Intel Prozessor passiert dies auch nicht, auch hier wird das increment in einer einzigen Anweisung ausgeführt. Sonst wäre der Fehler auch schon in meinen PC-Programmen vor 15 Jahren aufgetreten.
Code:
LEA EDI,dword ptr rx_count
inc dword ptr ES:[EDI]
Eine statische Codenalyse oder was auch immer für ein Testprogramm hätte diesen Fehler wohl nie aufdecken können.
Fazit: "atomarer code" war gestern, das geht kaum noch..
Heutztage bekomme ich teilweise nichtmal ein einziges BIT mehr gesetzt ohne das der Code nicht unterbrochen werden könnte.
Da werden teilwesie 5 Zeilen Assemblercode draus gebildet obwohl ich nur ein Bit setzten möchte.
Ich liebe aber solche Fehler, daran könnte ich mich ewig dran aufhalten.
Kein Witz, sowas dann zu finden macht mir echt Spass. Besser also DOKU 
----- P A U S E --
Schön fand ich auch, wo der C-Compiler mir Codezeilen weggelöscht hat, die ER für unnötig hielt:
Beim UART muss man das sogenennt DLAB Bit setzen um auf spezielle Register zuzugreifen,
das gab es schon in 90 Jahren so und es wurde tatsächlich in die modernen 32 Bitter übernommen.
Also setzte ich das Bit
dann meine Baudrate
und setze das Bit zurück
Der Compiler fragt sich: Wozu soll das gut sein, Du benutzt es doch garnicht und schmeist genau diese beiden Zeilen raus....
Das hat und hätte ein Pascal Compiler nie getan.
Natürlich kann und muss man dann gegensteuern. Ich brauche "volatile" und dann gehts.
Aber hier ist mir der Compiler manchmal zu selbständig. Man kann solche Optimierungen auch leider nicht abstellen.
Was sagt da die Code Anaylse dazu ? solche Fehler können doch garnicht erkannt werden ?
Zumal die der Compiler ja erst reinbaut...Das lief ja, bis er eigenmächtig Änderungen vorgenommen hat
Diese moderne, angebliche Intelligenz ist nicht immer wünschenswert.....
Lesezeichen