Hallo,
ich möchte hier mal ein Konzept für periodische Events vorstellen. Dieses habe ich selber schon mal implementiert und war damit recht zufrieden. Falls jemand bessere Vorschläge oder Fragen hat dann sind die hier natürlich gern gesehen.
Für periodisch auszulösende Events benötigt man natürlich einen passenden Timer der genau und monoton läuft. Da der klassische PIT nur mit einer festgelegten Frequenz IRQs generieren kann ohne von der CPU ständig neu konfiguriert zu werden fällt dieser als alleinige Basis aus. Man kann diesen aber auf z.B. 1kHz programmieren und den eigentlichen Zähler und die zugehörigen Comparators in SW implementieren, man verliert damit nicht an Langzeitgenauigkeit sondern bekommt nur einen höheren Jitter und eine kleinere zeitliche Auflösung (z.B. der HPET arbeitet normalerweise mit einigen MHz).
Der Einfachheit halber bleibe ich erst mal bei der PIT-Variante:
Man programmiert ihn eben auf 1kHz und lässt sich so jede ms einen IRQ generieren. Im IRQ-Handler zählt man einen simplen Counter nach oben, dafür sollte man gleich einen 64Bit-Wert nehmen um nicht zu schnell mit Überläufen hantieren zu müssen (welche ein ordentlicher Programmierer natürlich trotzdem berücksichtigt, auch wenn ein 64Bit-Zähler bei 1kHz erst in etwa 600 Millionen Jahren überläuft und der verantwortliche Programmierer bis da hin bestimmt was besseres macht), und schaut nach ob irgendwas gemacht werden muss also ob der aktuelle Zählerwert dem nächsten Event-Zeitpunkt entspricht.
Den Zählerwert verarbeitet man im OS-Kernel am besten so wie er ist als allgemeine absolute Zeitbasis. Wenn nun ein Programm einen periodischen Event beantragt, ich gehe jetzt mal von 64 Hz aus, dann schaut der SYSCALL-Handler nach dem aktuellem Zählerstand und vermerkt diesen als Zeit-Basis, als nächstes wird die nächste absolute Event-Zeit des ersten Events errechnet, in diesem Fall also von X+15.625s, was gerundet dann X+16ms ergibt. Beim Zählerwert X+16 generiert dann der IRQ-Handler einen Event (beim HPET wäre das dann erst ein IRQ, das zählen und vergleichen wird dort in Hardware erledigt) und errechnet vorher noch den nächsten absoluten Zählerwert für den nächsten Event welcher bei X+31.25ms liegt und zu X+32ms gerundet wird. Das Spiel geht dann immer so weiter und man bekommt einen periodischen Event welcher mit exakt 64Hz aber bis zu 1ms Jitter getriggert wird.
Das es wirklich exakt 64 Hz (mit einer durchschnittlichen Periodenlänge von exakt 15.625ms) sind ergibt sich aus folgender Liste:
Die errechneten absoluten Event-Zeitpunkte trägt man in eine geordnete Liste ein welche dann vom IRQ-Handler für die Vergleiche benutzt wird. Damit lassen sich auch beliebig viele solcher periodischen Events (mit unabhängigen Frequenzen) verwalten und auch die einmaligen Events kann man dort problemlos eintragen. Damit das einsortieren möglichst schnell geht habe ich dafür einen Baum benutzt, das Element unten rechts war der nächste Timer der abläuft. Die absoluten Zeiten muss man natürlich in Relation zum aktuellen Timer-Zählerwert als quasi Vorzeichenbehaftet betrachten. Hinter der ganzen Mathematick stecken an einigen Stellen ein paar Tricks aber da ist nichts wovor ein geübter Programmierer Angst haben muss.
Wenn man dazu den HPET verwendet, was auf jeden Fall zu empfehlen ist, dann bekommt man nicht ständig IRQs sondern nur dann wenn wieder ein Event dran ist, dazu trägt der IRQ-Handler immer die nächste Event-Zeit in das/die Comparator-Register des HPET ein. Hier muss man beachten das man beim Eintragen prüfen sollte ob der gewünschte Zeitpunkt für den Counter noch in der Zukunft liegt und nicht zwischenzeitlich schon überzählt wurde (der Comparator im HPET prüft nur auf Gleichheit, in SW kann man das etwas besser lösen), sonst wartet man bis der Counter im HPET ein mal komplett umgelaufen ist (was bei einem 64Bit Counter selbst mit extrem hoher Frequenz sehr lange dauern kann). Der HPET-IRQ-Handler muss also möglicherweise mehrere Event auslösen (wenn deren Zeitpunkte dicht bei einander liegen).
So, ich hoffe ich hab nichts wichtiges vergessen.
Falls jemand Fragen hat dann nur zu.
Grüße
Erik
ich möchte hier mal ein Konzept für periodische Events vorstellen. Dieses habe ich selber schon mal implementiert und war damit recht zufrieden. Falls jemand bessere Vorschläge oder Fragen hat dann sind die hier natürlich gern gesehen.
Für periodisch auszulösende Events benötigt man natürlich einen passenden Timer der genau und monoton läuft. Da der klassische PIT nur mit einer festgelegten Frequenz IRQs generieren kann ohne von der CPU ständig neu konfiguriert zu werden fällt dieser als alleinige Basis aus. Man kann diesen aber auf z.B. 1kHz programmieren und den eigentlichen Zähler und die zugehörigen Comparators in SW implementieren, man verliert damit nicht an Langzeitgenauigkeit sondern bekommt nur einen höheren Jitter und eine kleinere zeitliche Auflösung (z.B. der HPET arbeitet normalerweise mit einigen MHz).
Der Einfachheit halber bleibe ich erst mal bei der PIT-Variante:
Man programmiert ihn eben auf 1kHz und lässt sich so jede ms einen IRQ generieren. Im IRQ-Handler zählt man einen simplen Counter nach oben, dafür sollte man gleich einen 64Bit-Wert nehmen um nicht zu schnell mit Überläufen hantieren zu müssen (welche ein ordentlicher Programmierer natürlich trotzdem berücksichtigt, auch wenn ein 64Bit-Zähler bei 1kHz erst in etwa 600 Millionen Jahren überläuft und der verantwortliche Programmierer bis da hin bestimmt was besseres macht), und schaut nach ob irgendwas gemacht werden muss also ob der aktuelle Zählerwert dem nächsten Event-Zeitpunkt entspricht.
Den Zählerwert verarbeitet man im OS-Kernel am besten so wie er ist als allgemeine absolute Zeitbasis. Wenn nun ein Programm einen periodischen Event beantragt, ich gehe jetzt mal von 64 Hz aus, dann schaut der SYSCALL-Handler nach dem aktuellem Zählerstand und vermerkt diesen als Zeit-Basis, als nächstes wird die nächste absolute Event-Zeit des ersten Events errechnet, in diesem Fall also von X+15.625s, was gerundet dann X+16ms ergibt. Beim Zählerwert X+16 generiert dann der IRQ-Handler einen Event (beim HPET wäre das dann erst ein IRQ, das zählen und vergleichen wird dort in Hardware erledigt) und errechnet vorher noch den nächsten absoluten Zählerwert für den nächsten Event welcher bei X+31.25ms liegt und zu X+32ms gerundet wird. Das Spiel geht dann immer so weiter und man bekommt einen periodischen Event welcher mit exakt 64Hz aber bis zu 1ms Jitter getriggert wird.
Das es wirklich exakt 64 Hz (mit einer durchschnittlichen Periodenlänge von exakt 15.625ms) sind ergibt sich aus folgender Liste:
- 1. Event: X+15.625ms wird zu X+16ms : also Periode gegenüber letztem Event : 16ms (okay, das zählt nicht weil es bei der Erstellung keinen Event gab)
- 2. Event: X+31.25ms wird zu X+32ms : also Periode gegenüber letztem Event : 16ms
- 3. Event: X+46.875ms wird zu X+47ms : also Periode gegenüber letztem Event : 15ms
- 4. Event: X+62.5ms wird zu X+63ms : also Periode gegenüber letztem Event : 16ms
- 5. Event: X+78.175ms wird zu X+79ms : also Periode gegenüber letztem Event : 16ms
- 6. Event: X+93.75ms wird zu X+94ms : also Periode gegenüber letztem Event : 15ms
- 7. Event: X+109.375ms wird zu X+110ms : also Periode gegenüber letztem Event : 16ms
- 8. Event: X+125.0ms wird zu X+125ms : also Periode gegenüber letztem Event : 15ms
- 9. Event: X+140.625ms wird zu X+141ms : also Periode gegenüber letztem Event : 16ms
Die errechneten absoluten Event-Zeitpunkte trägt man in eine geordnete Liste ein welche dann vom IRQ-Handler für die Vergleiche benutzt wird. Damit lassen sich auch beliebig viele solcher periodischen Events (mit unabhängigen Frequenzen) verwalten und auch die einmaligen Events kann man dort problemlos eintragen. Damit das einsortieren möglichst schnell geht habe ich dafür einen Baum benutzt, das Element unten rechts war der nächste Timer der abläuft. Die absoluten Zeiten muss man natürlich in Relation zum aktuellen Timer-Zählerwert als quasi Vorzeichenbehaftet betrachten. Hinter der ganzen Mathematick stecken an einigen Stellen ein paar Tricks aber da ist nichts wovor ein geübter Programmierer Angst haben muss.
Wenn man dazu den HPET verwendet, was auf jeden Fall zu empfehlen ist, dann bekommt man nicht ständig IRQs sondern nur dann wenn wieder ein Event dran ist, dazu trägt der IRQ-Handler immer die nächste Event-Zeit in das/die Comparator-Register des HPET ein. Hier muss man beachten das man beim Eintragen prüfen sollte ob der gewünschte Zeitpunkt für den Counter noch in der Zukunft liegt und nicht zwischenzeitlich schon überzählt wurde (der Comparator im HPET prüft nur auf Gleichheit, in SW kann man das etwas besser lösen), sonst wartet man bis der Counter im HPET ein mal komplett umgelaufen ist (was bei einem 64Bit Counter selbst mit extrem hoher Frequenz sehr lange dauern kann). Der HPET-IRQ-Handler muss also möglicherweise mehrere Event auslösen (wenn deren Zeitpunkte dicht bei einander liegen).
So, ich hoffe ich hab nichts wichtiges vergessen.
Falls jemand Fragen hat dann nur zu.
Grüße
Erik

Übersicht
Wiki
Hilfe
Suche
Einloggen
Registrieren
Drucken

Gespeichert


Es gibt viele Ereignisse, die länger benötigen (sei es die halbsekünde Anzeige der CPU-Auslastung), und das Teil ist dann immernoch genauer als manche RTC. Kann man eigentlich One-Shot und kontinuierlichen Modus kombinieren? Dann könntest du kürzere Intervalle besser realisieren.