- Akku Tests und Balkonkraftwerk Speicher         
Seite 2 von 2 ErsteErste 12
Ergebnis 11 bis 16 von 16

Thema: NOP in "C"

  1. #11
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    27.08.2013
    Ort
    Region Basel
    Alter
    66
    Beiträge
    2.435
    Anzeige

    LiFePo4 Akku selber bauen - Video
    Hallo Siro,

    1. Wie du erkannt hast darf ein Compiler einen Funktion auch inline generieren. Es gibt dafür sogar das Schlüsselwort inline in Manchen C-Compilern und in C++.

    2. In ANSI-C wurde definiert, dass compiler- und prozessorspezifische Erweiterungen, welche nicht Bestandteil von ANSI-C sind, mit einem, oder zwei, _ anfangen. Dies gilt auch für entsprechende Variablen und Konstanten.
    Damit ist recht schnell ersichtlich, wenn solche Erweiterungen in einem Programm verwendet werden.
    Das wird wichtig, wenn man Code portieren will, bzw. wenn der selbe Code auf unterschiedlichen Prozessoren laufen soll (kommt bei mir öfters vor, besonders bei Übertragungsprotokollen. Wenn man nur ein Code-Modul zu pflegen hat, wird das Leben einfacher).

    3. Grundsätzlich ist nop(); eine Funktion ohne Parameter. NOP ist eine Spezialfall, da es der einzige Maschinenbefehl ist, welcher ausser dem Verbrauch von CPU-Takten keinerlei Wirkung hat. Alle anderen Maschinenbefehle haben irgendwelche Effekte und benötigen teilweise auch Parameter. Für nop(); hat man jetzt nicht extra einen syntaktischen Sonderfall einführen wollen. Es wäre auch eine CPU denkbar, welche als Parameter von nop(); die Anzahl CPU-Takte übernimmt, welche "verbraten" werden sollen.

    4. Dass dein Ablauf und der vom Compiler erzeugte nicht ganz übereinstimmen ist normal, für optimierende Compiler!
    Der stellt dir auch mal Schleifen um oder rollt sie auf. Die Programmlogik bleibt dabei aber erhalten. Bei den meisten CPUs versucht der Compiler auch Speicher- und IO-Zugriffe zu optimieren, d.h. nur einmal darauf zuzugreifen und dann mit einer Kopie im Register zu arbeiten, was meist schneller ist.
    Das kann natürlich besonders bei IO-Ports in die Hose gehen

    while (IO_bit)
    ;

    Wenn hier der der Compiler entdeckt, dass IO-bit in der Schleife nicht verändert wird, arbeitet er mit eine Kopie
    Deshalb gibt es das Schlüsselwort volatile, dann weiss der Compiler, dass er bei jeder Verwendung von IO_bit auch auf das entsprechende Port zugreifen muss.
    Man kann aber die Optimierung auch ganz abschalten, dann entspricht der Code mehr deinen Erwartungen, wird aber grösser und langsamer.

    MfG Peter(TOO)

  2. #12
    Erfahrener Benutzer Robotik Visionär Avatar von oberallgeier
    Registriert seit
    01.09.2007
    Ort
    Oberallgäu
    Beiträge
    8.686
    Zitat Zitat von Siro Beitrag anzeigen
    ich vermute, daß in deinem Programmcode ... fehlt ...
    Ohhh Mannomann wie Recht Du nur hast. Sorry und vor Allem: Danke!
    Ciao sagt der JoeamBerg

  3. #13
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    05.11.2007
    Beiträge
    1.076
    Ich danke Dir erstmal für deine ausführliche Beschreibung Peter.

    zu 1.
    Ich dachte "inline" bedeutet dass jetzt ein Assembler-Code folgt.
    Upps, da bin ich anscheinend noch auf dem Holzwege.

    zu 2.
    Das war mir auch neu mit den Unterstrichen und gibt natürlich Sinn. Beim Portieren ist leicht zu ersehen wo man evtl. Prozessorspezifisch Hand anlegen muss.
    Dafür ist ja dann (meist) die Datei "intrinsics.h zuständig, welche oberallgeier vergessen hatte.

    zu 3.
    Mit der nop Implementierung ist natürlich Prozessorspezifisch. Mit der Übergabe von Parametern für die Zyklenzeit wäre tatsächlich sinnvoll, habe ich noch garnicht dran gedacht.
    Dann gäben die Klammern auch wieder Sinn.

    zu 4.
    Die Optimierung der Compiler hat mich damals und manchmal auch heute noch, völlig aus der Bahn geworfen, ganz besonders beim Debuggen.
    Oftmals sieht der generierte Assemblercode völlig anders aus als erwartet.
    Der Code wurde aber bisher immer richtig ausgeführt, auch wenns merkwürdig aussah.
    Da muss man plötzlich völlig umdenken als Assemblerprogrammierer.

    Schön, daß ich helfen konnte und hab auch gleich noch was gelernt

    Siro

  4. #14
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    27.08.2013
    Ort
    Region Basel
    Alter
    66
    Beiträge
    2.435
    Hallo Siro,

    ANSI-C = Standard-C gibt es erst seit 1990, davor gab es nur die Beschreibung von K&R, welche aber vieles nicht definierte.
    Im Prinzip konnte jeder Compilerhersteller machen was er wollte und das ganze C nennen.
    Erst mit C90 entstand dann die erste verbindliche Norm.

    Zitat Zitat von Siro Beitrag anzeigen
    zu 1.
    Ich dachte "inline" bedeutet dass jetzt ein Assembler-Code folgt.
    Upps, da bin ich anscheinend noch auf dem Holzwege.
    Manche Compiler haben es ursprünglich so verwendet, in den Standard wurde es erst mit C99 aufgenommen. Wobei es von C++ übernommen wurde.

    Die ursprünglichen C-Compiler unter Unix bestanden aus mehreren eigenständigen Programmen, wobei die Ausgab eines Programms die Eingabe für das nächste war:

    1. Präprozessor.
    Das war eine Stufe, welche eigentlich nur Text finden und ersetzen konnte und keine Ahnung von der C-Syntax hatte. Damit der PP "seine Befehle" finden kann, fangen diese mit # an.
    #include <stdio.h>
    bewirkt eigentlich nur, dass die Datei "stdio.h" an dieser Stelle in die Ausgabe kopiert wird.
    Der PP funktioniert auch mit anderen Sprachen oder man konnte damit auch Bücher erstellen: einfach alle Kapitel mit #include zusammenlinken und dazwischen die Kapitelüberschriften einfügen

    2. C-Compiler.
    Diese Stufe hat dann aus dem C-Source einen Assembler-Source gemacht.
    Normalerweise besteht der CC aus zwei Teilen, dem Frontend, welches aus C eine interne Baumstruktur erstellt und dem Backend, welches aus dem Baum dann den prozessorspezifischen Assembler-Source erzeugt. Das hat den Vorteil, dass für eine neue CPU-Architektur nur das Backend neu geschrieben werden muss, alles davor muss nicht geändert werden.
    Das "alte" inline war da recht einfach zu implementieren. Alles was zwischen den Anführungszeichen steht, wird direkt in den Assembler-Source kopiert

    Die einfache Registeroptimierung findet im Backend statt, das Aufrollen und umstellen von Schleifen macht man an der Internet Baumstruktur.

    3. Optimierer.
    Diese Stufe war ursprünglich nur optional und optimierte den Assembler-Source.

    4. Assembler.
    Schlussendlich wurde dann alles vom Assembler in Maschinencode übersetzt. Dieser Assembler wurde auch alleine für die Assemblerpogrammierung verwendet.

    5. Lint.
    Das war ein optionaler Pass, zwischen PP und Compiler, welcher auch die falsche Verwendung von Variablen entdeckt hat, also z.B. das Zuweisen eines int an einen char und generelle Unsauberkeiten, welche heute meistens als Warning ausgegeben werden..

    Heute ist dies oft in einem einzigen Programm zusammengefasst, aber auch moderne Compiler können noch die Zwischendateien, nach dem PP und den Assembler-Source ausgeben.

    Zitat Zitat von Siro Beitrag anzeigen
    zu 2.
    Das war mir auch neu mit den Unterstrichen und gibt natürlich Sinn. Beim Portieren ist leicht zu ersehen wo man evtl. Prozessorspezifisch Hand anlegen muss.
    Dafür ist ja dann (meist) die Datei "intrinsics.h zuständig, welche oberallgeier vergessen hatte.
    "intrinsics.h" ist nur für prozessorspezifische Erweiterungen zuständig.
    Es gibt aber auch Bibliotheksspezifische Erweiterungen, welche dann Compilerabhängig sind.
    Diese werden dann, sofern sie sich in den definierten Standard-Bibliotheken befinden, auch mit dem _ "markiert".

    Zitat Zitat von Siro Beitrag anzeigen
    zu 4.
    Die Optimierung der Compiler hat mich damals und manchmal auch heute noch, völlig aus der Bahn geworfen, ganz besonders beim Debuggen.
    Oftmals sieht der generierte Assemblercode völlig anders aus als erwartet.
    Der Code wurde aber bisher immer richtig ausgeführt, auch wenns merkwürdig aussah.
    Da muss man plötzlich völlig umdenken als Assemblerprogrammierer.
    Oft hat der Sourcelevel-Debugger dann auch seine Probleme, den Source-Code dem Assembler-Code zuzuordnen.
    Deshalb produziert man meist auch zwei Versionen, Debug und Retail.
    Debug optimiert dann weniger und oft wird auch noch zusätzliche Code eingefügt für z.B. ein Laufzeitüberwachung des Stacks. Oft gibt es dazu auch noch separate Bibliotheken.
    Der Code ist natürlich grösser und langsamer und manche Fehler treten genau dann gar nicht auf Zusätzlich enthält dann die Datei der Debugversion auch noch die ganzen Symbole für den Debugger.
    Lustig wird's auch, wenn die Optimierung Fehler erzeugt Da kann es dann sehr hilfreich sein, den Assembler-Source ansehen zu können, zumal der meist auch mit dem C-Source kommentiert wird.

    MfG Peter(TOO)

  5. #15
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    05.11.2007
    Beiträge
    1.076
    Nun muss ich mich mal für die detailierte Ausführung von Peter bedanken.

    { ein bischen vom Thema abschweifend, ich bitte um Vergebung, aber es wird ja grad interessant und gehört auch irgendwie dazu }

    Lint hab ich noch nie gehört, natürlich gleich mal gegoogelt.
    Hab ich aber verstanden: Typprüfungen, statische Codeanalyse....
    Die Funktionsweise von Lint wurde also in die heutigen Compiler integriert.

    Mit dem Frontend und Backend erklärt jetzt auch so einiges. Da ich grad mühsamst versuche einen Crosscompiler für die ARM Struktur zum Laufen zu bringen. Der C-Compiler (Frontend) erzeugt also NICHT den prozessorspezifischen Code und ist damit noch "portabel" während der Backend den Prozessor spezifischen Assemblercode erzeugt.
    Ist dann im GCC der Front und der Backend enthalten ? Ich vermute mal ja, denn "DEN" GCC gibt es meiner Meinung nach nicht, sondern "Der" GCC wird prozessorspezifisch gebaut und deshalb heist er dann z.B arm-none-eabi-gcc.exe
    Dieser beinhaltet dann den allgemein gültigen Frontend und den ARM spezifischen Backend.


    Das führt uns zum eigentlichen Thema NOP zurück:

    Der NOP wird dann also vom Backend prozessorspezifisch implementiert, wobei sich der Compiler am "intrinsics.h" Code bedient.

    Ich hoffe, ich habe es so richtig verstanden, ansonsten korrigiert mich bitte.

    Siro

  6. #16
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    27.08.2013
    Ort
    Region Basel
    Alter
    66
    Beiträge
    2.435
    Hallo Siro,

    Im Prinzip hast du alles richtig verstanden.

    Nur ist in der Praxis die Trennung zwischen Front- und Backend nicht ganz so sauber wie in der Theorie.
    Es gibt Prozessorabhängige Schlüsselwörter, diese sind im wesentlichen in intrinsics.h abgelegt. (Intrinsic = spezifisch). Das Frontend muss also wissen für welches Backend es arbeitet, bzw. sind diese Teile im Backend abgelegt.
    Das Frontend macht also nur die lexikalische Überprüfung nach Standard-C-Norm, wird das Schlüsselwort nicht erkannt, wird dann eine entsprechende Erweiterung im Backend aufgerufen. Erkennt diese dann auch kein gültiges Wort, wird eine Fehlermeldung erzeugt.
    So ähnlich geht es dann auch bei der syntaktische Analyse weiter, das Schlüsselwort wurde bereits als intrinsic erkannt und markiert, also muss die gültige Syntax auch aus dem Backen kommen.

    Jetzt wird dir vielleicht auch klar, wieso nop(); als Funktion geschrieben wird. Das Backend kann die Standardfunktionen für die syntaktische Analyse im Frontend verwenden und wird wesentlich einfacher. Das Backend muss nämlich alle syntaktischen Analysen, welche vom Standard abweichen, selber ausführen.

    Die Methode C mit intrinsic zu erweitern ist relativ schlank und einfach.

    Versucht man das Ganze, bei einem modernen Compiler, über Inline-Assembler zu machen, wird es recht schnell hässlich. Zuerst einmal muss man im Backend einen kompletten Assembler einbauen mit der ganzen lexikalischen und syntaktischen Analyse. Dann ist er interne Zwischencode nicht mehr Prozessorabhängig, was wiederum zu jeder Menge Ausnahmebehandlungen führt, besonders an den Schnittstellen zwischen unabhängigen Code-Teilen und den Assembler-Teilen. Der Assembler-Teil braucht definierte Übergabekonventionen für z.B. lokale Variablen. Dadurch geht dann aber auch das Registermodell der CPU in den Zwischencode ein, um nur einige Probleme zu nennen. ;-(

    MfG Peter(TOO)

Seite 2 von 2 ErsteErste 12

Ähnliche Themen

  1. "RS232-Kabel" oder "USB-ISP-Kabel" zum Programmieren des RN-Control 1.4
    Von Soeren7 im Forum Schaltungen und Boards der Projektseite Mikrocontroller-Elektronik.de
    Antworten: 14
    Letzter Beitrag: 25.07.2012, 15:54
  2. Antworten: 2
    Letzter Beitrag: 15.06.2011, 22:18
  3. LPC1114 (Cortex M0): "sei()" und "cli()"
    Von Jaecko im Forum ARM - 32-bit-Mikrocontroller-Architektur
    Antworten: 1
    Letzter Beitrag: 02.07.2010, 13:25
  4. "Soft-Reset?" und "Finger-Interrupt?"
    Von trapperjohn im Forum Asuro
    Antworten: 8
    Letzter Beitrag: 11.06.2008, 00:02
  5. ASM: was machen "swap" und "cbr" genau?
    Von RHS im Forum AVR Hardwarethemen
    Antworten: 3
    Letzter Beitrag: 18.08.2004, 18:16

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

fchao-Sinus-Wechselrichter AliExpress