Synchronisierung von Zugriffen auf gemeinsame Daten



  • hustbaer schrieb:

    1. Siehe (2): C99 und/oder C++ schreiben bezüglich volatile nichts vor was mit Threads irgendwie in Zusammenhang stehen würde.

    das stimmt zwar, sie schreiben aber vor, dass eine 'volatile' variable nicht gecached werden darf. aus dem von mir geposteten link: A volatile object is also an appropriate model for a variable shared among multiple processes.

    hustbaer schrieb:

    Oder nochmal einfach: ohne ein "System" oder eine "Plattform"
    die definiert was "volatile" bedeutet hat "volatile" genau garkeine Bedeutung.

    was 'volatile' bedeutet, deiniert z.b. der C-standard, nicht das system. auf systemen, die sowieso nichts cachen, ist sowas wie 'volatile' natürlich überflüssig.
    🙂



  • ~fricky schrieb:

    rapso schrieb:

    volatile hat nicht viel mit synchronisation zu tun, es sagt dem compiler lediglich dass etwas nicht im register gelagert werden darf.

    teilweise richtig. 'volatile' sagt einem standardkonformen c-compiler, dass auf jeden fall das 'physikalische objekt', also die speicherzelle bzw. der i/o-ports bei schreib- und lesezugriffen angesprochen werden muss. wenn auf einem multiprozessor-system ein cache-flush dazu gehört, dann muss auch der gemacht werden.

    nein, das ist nicht was es bedeutet. ein compiler muss sich nicht darum kuemmert dass flushes gemacht werden oder am cache vorbei geschrieben wird.

    deiner definition nach wuerde kein compiler der welt je standard conform werden koennen, weil keiner zur laufzeit des schlussendlichen binaries anhand der addresse wissen koennte wie er sich plattformspecifisch korrekt verhalten sollte.

    mit synchronisation hat es zwar direkt nichts zu tun, aber 'volatile' sorgt zumindest dafür, dass änderungen des speichers für mehrere tasks bzw. mehrere prozessoren zeitnah sichtbar sind.
    siehe auch: http://www.open-std.org/JTC1/SC22/wg14/www/docs/n897.pdf
    🙂

    nein, macht volatile nicht. lies dir dein pdf selbst genauer durch, volatile sorgt lediglich dafuer dass der compiler die variable nicht im register behaelt, sondern explizit immer zurueckschreibt bzw ausliest.

    im falle von

    static volatile int32_t test=1;
    .
    .
    .
    void funktion()
    {
    while(test)
    {
    }
    

    wird dir ein compiler ohne volatile einfach eine endlosschleife generieren, das selbe gilt fuers schreiben, beim schreiben innerhalb der schleife, ohne test zu nutzen, wuerde er test nach der schleife erst aus dem register schreiben. mit volatile weist er den wert immer zu.
    fuer ein pipeline flush auf simplen powerpc musst du dann noch __asm__ volatile("sync"); explizit angeben, danach eventuell das OS anweisen den cache zu flushen. auf mips musst du am cache vorbei schreiben indem du das oberste bit der addresse setzt. ... du bist ebenfalls dafuer verantwortlich, dass alles in der richtigen reihenfolge geschrieben wird, hast du mehrere variablen die in den speicher sollten, darfst du dich nicht drauf verlassen dass der compiler bzw die architektur sie in der im c source angegebenen reihenfolge machen, du musst das explizit sicherstellen, falls die reihenfolge wichtig ist, z.b. wenn du einen commandbuffer befuellst und dann den cmdb-pointer weiterschiebst.



  • rapso schrieb:

    nein, das ist nicht was es bedeutet. ein compiler muss sich nicht darum kuemmert dass flushes gemacht werden oder am cache vorbei geschrieben wird.

    deiner definition nach wuerde kein compiler der welt je standard conform werden koennen, weil keiner zur laufzeit des schlussendlichen binaries anhand der addresse wissen koennte wie er sich plattformspecifisch korrekt verhalten sollte.

    Häh? Wozu brauche ich dann einen Compiler und 'volatile'? Der Compiler muß sich merken, ab wo es volatile ist und zur Runtime (falls Parameter weitergereicht werden) den Ursprung konsistent auflösen.

    rapso schrieb:

    im falle von

    static volatile int32_t test=1;
    .
    .
    .
    void funktion()
    {
    while(test)
    {
    }
    

    wird dir ein compiler ohne volatile einfach eine endlosschleife generieren, das selbe gilt fuers schreiben, beim schreiben innerhalb der schleife, ohne test zu nutzen, wuerde er test nach der schleife erst aus dem register schreiben. mit volatile weist er den wert immer zu.

    Ich suche noch die Pointe ... ohne volatile persistentes static, Endlosschleife, ja klar. Ab

    if (test) test = 0;
    

    in dem while()- Block muß er test holen, auswerten und zurückschreiben. Nix Endlosschleife. 🤡
    Aber eigentlich ist static volatile für Hardware gedacht, so daß sich test irgendwann von Außen ändern wird:

    A static volatile object is an appropriate model for a memory-mapped I/O register. Implementors of C translators should take into account relevant hardware details on the target systems when implementing accesses to volatile objects.

    ^^^^^^^^^^^^ (s.o. PDF)

    rapso schrieb:

    fuer ein pipeline flush auf simplen powerpc musst du dann noch __asm__ volatile("sync"); explizit angeben,

    Nöh. Das müssen meine Compiler brav machen, sonst nicht Standard und auf OS zurechtkastriert und ab in die Mülltonne. 👎

    rapso schrieb:

    danach eventuell das OS anweisen den cache zu flushen.

    Hat mit volatile nicht soviel zu tun, wenn das OS aus Geschwindigkeitsgründen Inkonsistenzen riskiert. -> API

    rapso schrieb:

    auf mips musst du am cache vorbei schreiben indem du das oberste bit der addresse setzt. ... du bist ebenfalls dafuer verantwortlich, dass alles in der richtigen reihenfolge geschrieben wird, hast du mehrere variablen die in den speicher sollten, darfst du dich nicht drauf verlassen dass der compiler bzw die architektur sie in der im c source angegebenen reihenfolge machen, du musst das explizit sicherstellen, falls die reihenfolge wichtig ist, z.b. wenn du einen commandbuffer befuellst und dann den cmdb-pointer weiterschiebst.

    Wenn ich einen nackigen Prozessor und 'nen C- Compiler habe, verlasse ich mich primär darauf, daß das so sequentiell wie in der Source abläuft mit entsprechender Vorsicht bei Hardwarezugriffen, die durch static volatile impliziert sind (jaja, die Precautions, die langen, die kruden).
    Sonst könnte man ja gar nichts Deterministisches bauen. 😉



  • Hallo,

    also erst einmal danke für euren geballten Eifer 🙂

    Ich muss das ganz doch etwas präzisieren: Die Sprache ist C, es handelt sich um ein Einprozessorsystem und Variablen, die entweder global sind oder auch nur einmal existieren, aber über Zeiger weitergereicht werden.

    Meine Aussage bzgl. Atomarität war dann zu pauschal, was ich gemeint hatte war folgendes: Von dem verwendeten Compiler wird Assemblercode erzeugt, der neue Werte in einem Befehl schreibt (bzw. liest). Also kann höchstens ein veralteter Wert verwendet werden, in keinem Fall eine Mischung aus altem/neuen Low/High Nibble oder so.

    Von daher denke ich, dass auf der beschriebenen Plattform keine Synchronisation nötig ist. Stimmt das so?

    Zur zweiten Frage nochmal: Weiß jemand, ob bzw. wie Regler mit unterschiedlichen Abtastperioden normalerweise synchronisiert werden - man müsste ja für eine Synchronisierung im schnelleren Regler immer mitzählen, wieviele Perioden schon vergangen sind. Oder evtl. nen zählenden Semaphor verwenden.



  • rapso schrieb:

    ein compiler muss sich nicht darum kuemmert dass flushes gemacht werden oder am cache vorbei geschrieben wird.

    richtig, er muss sich nur darum kümmern, dass echte physikalische schreib/lese-zugriffe auf ein objekt stattfinden, bevor der code hinter dem nächsten sequenzpunkt ausgeführt wird, wenn das objekt als 'volatile' deklariert wurde. wie er das macht, bleibt ihm überlassen.

    rapso schrieb:

    deiner definition nach wuerde kein compiler der welt je standard conform werden koennen, weil keiner zur laufzeit des schlussendlichen binaries anhand der addresse wissen koennte wie er sich plattformspecifisch korrekt verhalten sollte.

    das ist nicht meine definition, sondern die der c-standard-schreiberlinge. und wenn eine rechnerarchitektur das nicht zulässt, dann kann es für diesen rechner leider keinen compiler geben, der 'volatile' standardkonform unterstützt.

    synchronisator schrieb:

    Meine Aussage bzgl. Atomarität war dann zu pauschal, was ich gemeint hatte war folgendes: Von dem verwendeten Compiler wird Assemblercode erzeugt, der neue Werte in einem Befehl schreibt (bzw. liest). Also kann höchstens ein veralteter Wert verwendet werden, in keinem Fall eine Mischung aus altem/neuen Low/High Nibble oder so.
    Von daher denke ich, dass auf der beschriebenen Plattform keine Synchronisation nötig ist. Stimmt das so?

    das kommt drauf an. in der regel tickert in solchen multitasking-systemen ein timer-interrupt, der's dem scheduler erlaubt, zwischen den tasks periodisch hin- und herzuschalten. ein interrupt kann normalerweise keinen angefangenen maschinenbefehl unterbrechen, sondern schlägt immer vorher oder hinterher zu. d.h. wenn bei dir z.b. 16-bit zugriffe in einem rutsch passieren und ein solcher mit einem maschinenbefehl gemacht wird, dann geht es. was aber sein kann ist z.b. dass der compiler einen vermeintlich 16-bittigen zugriff doch in 2 8-bit zugrffe zerlegt, weil die variable an einer ungeraden adresse beginnt und die cpu keine 16-bit zugriffe auf ungerade adressen machen kann. da musste mal in die doku deines systems und compilers gucken, um mehr rauszufinden.
    🙂



  • ~fricky schrieb:

    rapso schrieb:

    ein compiler muss sich nicht darum kuemmert dass flushes gemacht werden oder am cache vorbei geschrieben wird.

    richtig, er muss sich nur darum kümmern, dass echte physikalische schreib/lese-zugriffe auf ein objekt stattfinden, bevor der code hinter dem nächsten sequenzpunkt ausgeführt wird, wenn das objekt als 'volatile' deklariert wurde. wie er das macht, bleibt ihm überlassen.

    'echte' ist eine schwammige behauptung von dir.
    genau gesagt, ich wiederhole mich gerne, darf er den wert nicht nur im register behalten. ob dieser schreibvorgang 'echt' im physikalischem speicher ankommt ist nicht mehr sache des compilers.

    rapso schrieb:

    deiner definition nach wuerde kein compiler der welt je standard conform werden koennen, weil keiner zur laufzeit des schlussendlichen binaries anhand der addresse wissen koennte wie er sich plattformspecifisch korrekt verhalten sollte.

    das ist nicht meine definition, sondern die der c-standard-schreiberlinge. und wenn eine rechnerarchitektur das nicht zulässt, dann kann es für diesen rechner leider keinen compiler geben, der 'volatile' standardkonform unterstützt.

    das ist deine interpretation davon, wenn das so waere, braeuchte man keine fences (und die gibt es selbst auf x86.



  • Auch ein interessanter Einwand. Laut Datenblatt der CPU werden solche Zugriffe durch die Speicherschnittstelle entsprechend verschoben. Von daher passt wohl alles 🙂



  • rapso schrieb:

    das ist deine interpretation davon, wenn das so waere, braeuchte man keine fences (und die gibt es selbst auf x86.

    aber 'fences' beissen sich doch nicht mit dem, was ich hier erzähle. 'volatile' sorgt dafür, dass zugriffe auch tatsächlich stattfinden und der zugriff bei beginn der nächsten anweisung (in derselben task) vollständig abgeschlossen ist. in welcher zeitlichen reihenfolge andere tasks diese zugriffe wahrnehmen (weil z.b. die hardware buszugriffe optimiert), wird nicht durch volatile geregelt. darüber steht nichts im C-standard (und im entsprechenden dokument von C's fettleibiger schwester sicherlich auch nicht). was aber volatile (als logische konsequenz) sicherstellt ist, dass änderungen eines volatile-objekts in anderen tasks sichtbar sind, weil beide auf dieses zielobjekt auch wirklich zugreifen müssen.
    🙂



  • synchronisator schrieb:

    Ich muss das ganz doch etwas präzisieren: Die Sprache ist C, es handelt sich um ein Einprozessorsystem und Variablen, die entweder global sind oder auch nur einmal existieren, aber über Zeiger weitergereicht werden.

    Das ist ungünstig, weil Du nur zur Compiletime "volatile" aufgelöst bekommst.

    synchronisator schrieb:

    Also kann höchstens ein veralteter Wert verwendet werden, in keinem Fall eine Mischung aus altem/neuen Low/High Nibble oder so.

    Die Frage ist, wie portabel das werden soll. Mach 'ne Note rein oder eine ordentliche Doppelabfrage.

    synchronisator schrieb:

    Von daher denke ich, dass auf der beschriebenen Plattform keine Synchronisation nötig ist. Stimmt das so?

    Der geschilderte 2- Thread Mechanismus klingt sauber soweit.

    synchronisator schrieb:

    Zur zweiten Frage nochmal: Weiß jemand, ob bzw. wie Regler mit unterschiedlichen Abtastperioden normalerweise synchronisiert werden - man müsste ja für eine Synchronisierung im schnelleren Regler immer mitzählen, wieviele Perioden schon vergangen sind. Oder evtl. nen zählenden Semaphor verwenden.

    Ei, ei, ei, richtig, isochrone Algorithmen und Multitasking vertragen sich eigentlich nicht, ich denke, Du solltest mehr in medias res gehen - also, was genau brauchst Du?



  • ~fricky, du misverstehst da eine sache, und zwar gröber: volatile kümmert sich (in C und C++) nur um dinge die der compiler selbst macht. volatile sorgt also dafür dass der compiler nicht selbst einen wert in einem register behält oder verspätet zurückschreibt, weil er weiss dass der wert kurz später sowieso nochmal geschrieben wird.

    volatile ist also auch NICHT ohne auswirkungen auf systemen die keinen cache haben. überflüssig ist es sowieso zu 100%, aber das ist wieder eine andere geschichte.

    was caches, reordering in der CPU etc. angeht: darüber sagt der C++ standard nichts aus, und es gibt auch kaum compiler die dahingehend irgendwas machen wenn man "volatile" verwendet. MSVC ab version 8 oder 9 gibt da gewisse garantien, aber das ist so ziemlich der einzige C++ compiler. GCC macht genau nichts in der richtung.

    volatile stellt also auch NICHT sicher dass eine änderung von thread A irgendwann von thread B gesehen wird. warum? eben weil es CPUs gibt die ihre caches nicht selbst synchronisieren, und volatile auf dieser ebene nichts bewirkt.

    ----

    wenn du volatile mit der bedeutung möchtest die du hier unterstellst, dann musst du entweder java programmieren, oder auf den neuen C++ standard warten, in dem das hoffentlich endlich geregelt wird.



  • hustbaer schrieb:

    überflüssig ist es sowieso zu 100%, aber das ist wieder eine andere geschichte.

    nun mach's nicht so spannend. wieso ist 'volatile', nach deiner meinung, zu 100% überflüssig?
    🙂



  • das würd mich auch mal interessieren lol



  • pointercrash() schrieb:

    rapso schrieb:

    nein, das ist nicht was es bedeutet. ein compiler muss sich nicht darum kuemmert dass flushes gemacht werden oder am cache vorbei geschrieben wird.

    deiner definition nach wuerde kein compiler der welt je standard conform werden koennen, weil keiner zur laufzeit des schlussendlichen binaries anhand der addresse wissen koennte wie er sich plattformspecifisch korrekt verhalten sollte.

    Häh? Wozu brauche ich dann einen Compiler und 'volatile'?

    damit er es nicht im register haellt.

    Der Compiler muß sich merken, ab wo es volatile ist und zur Runtime (falls Parameter weitergereicht werden) den Ursprung konsistent auflösen.

    ein compiler kann nicht ueber alle systeme bescheid wissen, wenn deine graphikkarte ne variable liest und dein compiler sie einfach nur in den speicher schreibt, wird die graka diese variable nie sehen weil sie nur im cache bleibt (falls die architektur den wert nicht ab und zu rausschreibt). passiert z.b. auf gamecube und anderen powerpc architekturen.

    egal ob mit oder ohne volatile.

    rapso schrieb:

    im falle von

    static volatile int32_t test=1;
    .
    .
    .
    void funktion()
    {
    while(test)
    {
    }
    

    wird dir ein compiler ohne volatile einfach eine endlosschleife generieren, das selbe gilt fuers schreiben, beim schreiben innerhalb der schleife, ohne test zu nutzen, wuerde er test nach der schleife erst aus dem register schreiben. mit volatile weist er den wert immer zu.

    Ich suche noch die Pointe ... ohne volatile persistentes static, Endlosschleife, ja klar. Ab

    if (test) test = 0;
    

    in dem while()- Block muß er test holen, auswerten und zurückschreiben. Nix Endlosschleife. 🤡

    dafuer andere schweinereien, was wenn thread 1 sich test holt und an if vorbei kommt und bevor er test auf 0 setzt, kommt thread 2 dran und kommt auch an test vorbei, dann hast du ploetzlich zwei threads die da lang laufen. je nachdem was danach gemacht wird, kann ziemlicher misst rauskommen.

    Aber eigentlich ist static volatile für Hardware gedacht, so daß sich test irgendwann von Außen ändern wird:

    A static volatile object is an appropriate model for a memory-mapped I/O register. Implementors of C translators should take into account relevant hardware details on the target systems when implementing accesses to volatile objects.

    ^^^^^^^^^^^^ (s.o. PDF)

    das heisst lediglich, dass ein IO register bei dir IO gemappt wird. wenn du einen cmdbuffer implementierst der mit einer anderen hardware zusammenarbeitet und du weist dieser hardware diesen speicherbereich dafuer zu, kann der compiler nichts machen.
    ebenfalls wenn du einfach nur daten in gecachten bereich schreiben willst der auf einer anderen hardware liegt. es liegt durchaus im interesse des users dass nicht bei jedem byte zugriff die cacheline rausgeschrieben wird. etc etc.

    rapso schrieb:

    fuer ein pipeline flush auf simplen powerpc musst du dann noch __asm__ volatile("sync"); explizit angeben,

    Nöh. Das müssen meine Compiler brav machen, sonst nicht Standard und auf OS zurechtkastriert und ab in die Mülltonne. 👎

    da das ein compiler nicht wissen kann, macht er das nicht. ich weiss nicht woher du diese unsinnige behauptung hast. ich muss das bei VC++, gcc 4.1 und IBM selbst machen, der compiler hat 0 ahnung wann er das machen sollte. natuerlich koennte er das immer machen, aber da so ein sync 20 bis 600 cycle kosten kann, waere es nicht im sinne des erfinders.

    rapso schrieb:

    danach eventuell das OS anweisen den cache zu flushen.

    Hat mit volatile nicht soviel zu tun,

    eigentlich schon, ohne volatile waere ein flush eventuell sinnlos da der compiler den wert im register behalten koennte. deswegen, erst volatile zuweisen, dann sync bzw flush.

    rapso schrieb:

    auf mips musst du am cache vorbei schreiben indem du das oberste bit der addresse setzt. ... du bist ebenfalls dafuer verantwortlich, dass alles in der richtigen reihenfolge geschrieben wird, hast du mehrere variablen die in den speicher sollten, darfst du dich nicht drauf verlassen dass der compiler bzw die architektur sie in der im c source angegebenen reihenfolge machen, du musst das explizit sicherstellen, falls die reihenfolge wichtig ist, z.b. wenn du einen commandbuffer befuellst und dann den cmdb-pointer weiterschiebst.

    Wenn ich einen nackigen Prozessor und 'nen C- Compiler habe, verlasse ich mich primär darauf, daß das so sequentiell wie in der Source abläuft mit entsprechender Vorsicht bei Hardwarezugriffen, die durch static volatile impliziert sind (jaja, die Precautions, die langen, die kruden).
    Sonst könnte man ja gar nichts Deterministisches bauen. 😉

    ja, wenn man sich wenig auskennt verlaesst man sich drauf. ich hab damit taeglich zu tun und ich weiss, dass jede hardware ihre eigenen spezialitaeten hat. leute die sich nicht auskennen vertrauen einfach darauf das es immer so laufen wird wie bei 10tests und checken das ein. andere knallen an jeder moeglichen stelle nen mutex rein vor panik und haben unmengen synchronisationsaufwand der mehr kostet als threads bringen.
    ich weiss dass compiler dir keine grossartigen syncs einbauen, aus diesem grund gibt es explizit befehle und addressmasken um dennoch zum ziel zu kommen. hast du code wie

    struct
    {
    volatile int foo;
    volatile int bar;
    };
    ..
    volatile int array[...];
    

    und greifst du darauf in der folge zu

    foo=1;
    array[x]=..;
    bar=1;
    

    kann dir die hardware diese schreibzugriffe umordnen in

    foor=1;
    bar=1;
    array[x]=....;
    

    weil es von den cachelines her guenstiger ist. willst du das verhindern, brauchst du einen sync/fence/data barrier.

    sorry fuer die spaete antwort 😉



  • rapso schrieb:

    ein compiler kann nicht ueber alle systeme bescheid wissen, wenn deine graphikkarte ne variable liest...

    über angeschlossene zusatzhardware weiss er natürlich nix, aber sonst weiss er sehr wohl eine menge über die hardware, für die er entwickelt wurde. z.b. der IAR für ARM-basierte controller kennt ca 300 verschiedene target-devices. wieso wohl? deiner ansicht nach würde es doch reichen, wenn er bloss ARM7...ARM11 code generieren könnte, ohne jegliches wissen über die komponenten und internen bussysteme der controller.

    rapso schrieb:

    rapso schrieb:

    fuer ein pipeline flush auf simplen powerpc musst du dann noch __asm__ volatile("sync"); explizit angeben,

    Nöh. Das müssen meine Compiler brav machen, sonst nicht Standard und auf OS zurechtkastriert und ab in die Mülltonne. 👎

    da das ein compiler nicht wissen kann, macht er das nicht.

    ein guter compiler sollte mehr mehr wissen, als nur wie maschinencode für den cpu-core erzeugt wird. sonst kannst du auch gleich gcc nehmen.
    🙂



  • ~fricky schrieb:

    rapso schrieb:

    ein compiler kann nicht ueber alle systeme bescheid wissen, wenn deine graphikkarte ne variable liest...

    über angeschlossene zusatzhardware weiss er natürlich nix, aber sonst weiss er sehr wohl eine menge über die hardware, für die er entwickelt wurde. z.b. der IAR für ARM-basierte controller kennt ca 300 verschiedene target-devices. wieso wohl?

    weil es unglaublich viele inkarnationen von ARM cores gibt. jeder hersteller mit lizens kann sich seine persoenliche cpu zusammenstecken. die sind nicht zwingend untereinander kompatibel und haben andere performanceeigenschaften beim optimieren. (also der selbe grund weshalb intel und MS verschiede cpu profile trotz compatibilitaet unterstuetzen).

    deiner ansicht nach würde es doch reichen, wenn er bloss ARM7...ARM11 code generieren könnte, ohne jegliches wissen über die komponenten und internen bussysteme der controller.

    rapso schrieb:

    rapso schrieb:

    fuer ein pipeline flush auf simplen powerpc musst du dann noch __asm__ volatile("sync"); explizit angeben,

    Nöh. Das müssen meine Compiler brav machen, sonst nicht Standard und auf OS zurechtkastriert und ab in die Mülltonne. 👎

    da das ein compiler nicht wissen kann, macht er das nicht.

    ein guter compiler sollte mehr mehr wissen, als nur wie maschinencode für den cpu-core erzeugt wird. sonst kannst du auch gleich gcc nehmen.
    🙂

    und nochmals, das hat nichts mit der guete des compilers zu tun. es reicht schon der unterschied zwischen single socket systemen und multisocket und schon verhaelt sich die synchronisation anders, der compiler kann unmoeglich wissen welche hardware du alles nutzt und wird keine systemcalls implizit einbauen um fuer dich zu synchronisieren.
    ansonsten sag mir bitte einfach wie ein compiler wissen kann, ob ein schreibzugriff auf einen speicherbereich einen flush des caches braucht, oder soll jeder volatile write mehrere kiloCycles kosten?



  • rapso schrieb:

    ~fricky schrieb:

    rapso schrieb:

    ein compiler kann nicht ueber alle systeme bescheid wissen, wenn deine graphikkarte ne variable liest...

    über angeschlossene zusatzhardware weiss er natürlich nix, aber sonst weiss er sehr wohl eine menge über die hardware, für die er entwickelt wurde. z.b. der IAR für ARM-basierte controller kennt ca 300 verschiedene target-devices. wieso wohl?

    weil es unglaublich viele inkarnationen von ARM cores gibt. jeder hersteller mit lizens kann sich seine persoenliche cpu zusammenstecken. die sind nicht zwingend untereinander kompatibel und haben andere performanceeigenschaften beim optimieren.

    es geht dabei aber nicht nur um die CPU, sondern um das gesamtsystem. von ARM gibts sozusagen eine baukastensystem für chipdesigner, wie AHB-bus (der z.b. auch 'ne pipeline hat, burst-transfers machen, transaktionen aufsplitten kann), dma-controller usw. und dementsprechend gibt es compiler, die die bekanntesten konfigurationen kennen und dafür angepassten code erzeugen können. wenn man einen ARM-basierten chip hat, der nicht direkt vom compiler unterstützt wird, dann hat man immerhin noch die möglichkeit, nur den cpu-core auszuwählen (z.b. arm7tdmi) muss dann aber einiges manuell machen, vergleichbar mit deinem __asm__(volatile) u.ä. beim alten arm-ADS war's z.b. so. das wurde aber eingestellt, wahrscheinlich auch, weil's einfach zu unbequem und fehleranfällig war, alles 'händisch' zu machen.
    🙂



  • ~fricky schrieb:

    ein guter compiler sollte mehr mehr wissen, als nur wie maschinencode für den cpu-core erzeugt wird. sonst kannst du auch gleich gcc nehmen. 🙂

    Also, jetzt muß ich für gcc mal 'ne Lanze brechen, nicht alle Ports sind mies: 😉

    rapso schrieb:

    fuer ein pipeline flush auf simplen powerpc musst du dann noch __asm__ volatile("sync"); explizit angeben, ...
    da das ein compiler nicht wissen kann, macht er das nicht.

    Der von KPIT gepflegte GCC- Port für die SH2A (5-stufige Pipe) berücksichtigt z.B. sowas und fügt sogar ohne mein Zutun einen Pipeflush ein, sobald er Zugriffe auf Hardwareregister (sind über einen switch generell als volatile deklariert) durchführt.
    Da bin ich auch reichlich dankbar dafür, denn wenn ich z.B. Timerregister anspreche, darf er mir die Schreibzugriffe nicht einfach umsortieren. Es macht ja sehr wohl einen Unterschied, ob ich den Timer konfiguriere, rücksetze und starte oder rücksetze, starte und dann konfiguriere (lt. Datasheet wäre das Verhalten dann undefiniert 😞 ).
    Mal auf C bezogen, sorgen meine Compiler über volatile dafür, daß wirklich die Zieladresse mit der Datensequenz in genau der Reihenfolge versorgt wird, wie sie in der Source steht.

    rapso schrieb:

    dafuer andere schweinereien, was wenn thread 1 sich test holt und an if vorbei kommt und bevor er test auf 0 setzt, kommt thread 2 dran und kommt auch an test vorbei, dann hast du ploetzlich zwei threads die da lang laufen. je nachdem was danach gemacht wird, kann ziemlicher misst rauskommen.

    Mist? In dem Fall würde test einfach zweimal auf 0 gesetzt, vorausgesetzt, thread 2 wird fertig. Wenn das stört, wird man unter "semaphore locking" bei google fündig.

    rapso schrieb:

    ansonsten sag mir bitte einfach wie ein compiler wissen kann, ob ein schreibzugriff auf einen speicherbereich einen flush des caches braucht, oder soll jeder volatile write mehrere kiloCycles kosten?

    Ich habe nicht behauptet, daß es performant ist, sondern daß volatile die Semmeln frisch vom Bäcker holen muß ;). Ein Timerwert, der seit 'ner Millisekunde im Cache vergammelt, ist soviel wert wie eine schimmelige Schrippe. :p
    Mag sein, daß es der Standard nicht zwingend vorschreibt, aber in der Praxis ist gecachet genauso wenig wert, wie registered 👎 - meine Compiler haben mir in der Hinsicht allerdings noch nie Kopfzerbrechen bereitet, deswegen wundert mich das, daß Du ASM- Zeugs nachschieben mußt. Ich kenn's einfach nicht anders. 😃
    Natürlich ist Vorsicht geboten, wenn man die Zieladresse der Hardware in einen Pointer lädt und den dereferenziert, da hat der Compiler keine Chance, einzugreifen, das ist allerdings wahr.



  • synchronisator schrieb:
    Ich muss das ganz doch etwas präzisieren: Die Sprache ist C, es handelt sich um ein Einprozessorsystem und Variablen, die entweder global sind oder auch nur einmal existieren, aber über Zeiger weitergereicht werden.

    Das ist ungünstig, weil Du nur zur Compiletime "volatile" aufgelöst bekommst.

    Wie meinst Du das?

    synchronisator schrieb:
    Also kann höchstens ein veralteter Wert verwendet werden, in keinem Fall eine Mischung aus altem/neuen Low/High Nibble oder so.

    Die Frage ist, wie portabel das werden soll. Mach 'ne Note rein oder eine ordentliche Doppelabfrage.

    Mit Note meinst Du wohl einen Kommentar im Quellcode?

    synchronisator schrieb:
    Von daher denke ich, dass auf der beschriebenen Plattform keine Synchronisation nötig ist. Stimmt das so?

    Der geschilderte 2- Thread Mechanismus klingt sauber soweit.

    Super! 🙂

    synchronisator schrieb:
    Zur zweiten Frage nochmal: Weiß jemand, ob bzw. wie Regler mit unterschiedlichen Abtastperioden normalerweise synchronisiert werden - man müsste ja für eine Synchronisierung im schnelleren Regler immer mitzählen, wieviele Perioden schon vergangen sind. Oder evtl. nen zählenden Semaphor verwenden.

    Ei, ei, ei, richtig, isochrone Algorithmen und Multitasking vertragen sich eigentlich nicht, ich denke, Du solltest mehr in medias res gehen - also, was genau brauchst Du?

    Also, es geht darum, dass ich einen Strom- und einen übergeordneten Drehzahlregler hab. Der Stromregeler läuft häufiger als der Drehzahlregler, allerdings war das bisher so implementiert, dass der Stromregler "wichtiger" ist und deshalb zuerst ausgeführt wurde (beide waren in einer einzigen Timer-ISR verbaut). Das heißt, wenn der Drehzahlregler auch auszuführen ist, wurde bisher trotzdem zuerst der Stromregler (mit alten Stromvorgaben) ausgeführt, bevor der Drehzahlregler neue Stromvorgaben machen konnte. Von daher denke ich, dass ich argumentieren kann "das muss so sein" und bisher hats ja auch funktioniert.

    Meine Frage war: Wenn man das ganze "sauberer" implementieren möchte, welche Möglichkeiten gibt es da? 😕



  • synchronisator schrieb:

    Wie meinst Du das?

    pointercrash() schrieb:

    Natürlich ist Vorsicht geboten, wenn man die Zieladresse der Hardware in einen Pointer lädt und den dereferenziert, da hat der Compiler keine Chance, einzugreifen ...

    synchronisator schrieb:

    Mit Note meinst Du wohl einen Kommentar im Quellcode?

    Wenn das Projekt, Compiler und Hardware für immer und ewig verheiratet sind, stört's keinen. Ich muß ab und an downsizen, also z.B. vom 16- Bitter runter auf 8 Bit, da sind mir solche Hints die Tipperei wert.

    synchronisator schrieb:

    Also, es geht darum, dass ich einen Strom- und einen übergeordneten Drehzahlregler hab. Der Stromregeler läuft häufiger als der Drehzahlregler, allerdings war das bisher so implementiert, dass der Stromregler "wichtiger" ist und deshalb zuerst ausgeführt wurde (beide waren in einer einzigen Timer-ISR verbaut). Das heißt, wenn der Drehzahlregler auch auszuführen ist, wurde bisher trotzdem zuerst der Stromregler (mit alten Stromvorgaben) ausgeführt, bevor der Drehzahlregler neue Stromvorgaben machen konnte. Von daher denke ich, dass ich argumentieren kann "das muss so sein" und bisher hats ja auch funktioniert.

    Meine Frage war: Wenn man das ganze "sauberer" implementieren möchte, welche Möglichkeiten gibt es da? 😕

    Das Problem bei Threads ist, daß Du nicht genau weißt, wann die Auswertung wirklich "dran" ist. Also Thread 1 beschreibt Dir timergesteuert eine Variable und Thread 2 kommt mit so 5- fachem Oversampling zur Auswertung. In der Folge jittert die Reaktion des Reglers je nach Systemlast unterschiedlich. Bei einem Zweipunktregler ziemlich egal, aber ein PID mit hohem D- Anteil kann sowas übel nehmen.
    Alles über eine timergesteuerte ISR zu fahren, ist prinzipiell die sauberste Lösung, Du kannst sie ja "Aufbrechen". Gilt aber nur, wenn Du die Meßwertgewinnung entsprechend synchronisieren kannst. Bei festem 5:1- Verhältnis gibt der Stromregler seinen Output zwar regelmäßig ab, aber erst im 5. Durchlauf auch an den Drehzahlregler weiter, der nach seinem Output wieder vier Durchläufe lang "schlafen" kann. Gibt ein sauberes Timing ab.

    Wenn der Drehzahlregler eher unkritisch ist, kannst Du den natürlich auch in einen Thread auslagern, aber über die grundlegende Abfolge mußt Du Dir keine größeren Sorgen machen. Zum Zeitpunkt t bekommt der Drehzahlregler die zu t gültigen Werte, er weiß ja nicht, daß es ein t-1, t-2 ... t-4 gab, weil die unterhalb seiner Erfassungszeitscheibe liegen, er kriegt nur t, t-5, t-10 usw. zu sehen. Das ist aber völlig in Ordnung für einen isochronen Regler. Der Versuch, ihm "irgendwie" ein t-4 in die Auswertung zu drücken, würde das Ergebnis verschlimmbessern. Er kann halt als Folge nur auf jedes t5 reagieren.

    Komplizierter wird es natürlich, wenn die Meßwerte zeitlich völlig unzusammenhängend reinkommen, aber ich glaube, das war nicht Dein Problem 😉


Anmelden zum Antworten