Hallo,
Ich habe ein Problem das Timingverhalten in einem selbstgeschriebenen
Windows-Treiber zu verstehen.
Vielleicht kann mir jemand helfen ... oder sagen wonach ich suchen muss.
Unter MinGW habe ich auf einem XP-Rechner einen kleinen Treiber
programmiert.
Später soll über eine Dauer von 2 sec alle 2,5 microsec der Parallelport
ausgelesen werden (tickstep=22 entspricht 2,5 microsecs).
Da Windows so große Zeitscheiben nicht sicher zur Verfügung stellen
kann, sperre ich alle Interrupts und akzeptiere für die Messzeit einen
"eingefrorenen" Windows-Rechner.
Die Funktion tick256th() gibt das Ergebnis vom Assemblerbefehl "rdtsc"
zurück. Allerdings werden von den ursprünglich 64 Bit nur Bit 39-8
zurückgegeben, da es vollkommen ausreicht und ich auf long
long-Berechnungen verzichten kann. Damit ist der zurückgegebene
Prozessortakt durch 256 geteilt.
Die vorerst auskommentierte Funktion InPort() führt den Assemblerbefehl
"inb" aus.
Hier eine kurze Sequenz aus dem Treiber:
1 | unsigned int i, j; // counters
|
2 | unsigned int tickendwait, tickdiff; // all in ticks256th
|
3 | unsigned int tickstep=22; // this is 2,5 micro secs
|
4 | unsigned int maxnumber=800000; // 800000*2,5 => 2sec
|
5 |
|
6 | tickendwait=tick256th()+tickstep; // when reached tickendwait, a new action is required
|
7 | asm ("cli"); // disable interrupts, no system reaction for a specific time
|
8 | for(j=0; j<maxnumber; j++) { // main loop to collect data
|
9 | for (i=0;;i++) { // wait for next action
|
10 | tickdiff=tick256th()-tickendwait; // if tickdiff is negative: continue waiting
|
11 | // if tickdiff is zero: reached end time exactly now
|
12 | if (tickdiff<0x80000000) { // if tickdiff is positive: end time is in the past, break, action now
|
13 | tickendwait+=tickstep; // calculate next tickendwait
|
14 | break;
|
15 | }
|
16 | }
|
17 | /* ACTION */
|
18 | // pio_field->i_port[j]=(InPort(0x379)&0xF8)^0x80; // read port and fill in (bit7-3 without inversion)
|
19 | pio_field->i_port[j]=i;
|
20 | pio_field->tickoffset[j]=tickdiff+tickstep; // fill in tickoffset in tick/256
|
21 | }
|
22 | asm("sti"); // enable interrupts
|
Grundsätzlich funktioniert das Programm, doch folgende Dinge fallen mir
auf:
1.) Mit i kann man zählen, wie oft die Warteschleife durchlaufen wurde,
bis die nächsten 2,5µm verstrichen sind. Es werden bei meinem Prozessor
(2,3MHz) ca. 309-310 Durchläufe verbraucht (96%). Doch in 4% der Fälle
zeigt der Schleifenzähler kleinere Werte (235-308)! WARUM? Wenn
Interrupts gesperrt sind, wer kann denn noch Prozessorticks verbrauchen?
Dieses Verhalten ist reprodizierbar.
2.) Wenn die Zeile zum Einlesen des Ports (mit InPort()) wieder
einkommentiert wird, ist der Effekt noch viel größer. Erwartet hätte ich
eine (konstante) Verringerung der Durchläufe, aber stattdessen kann in
26 Fällen von 800000 nicht einmal das Timing von 2,5µm eingehalten
werden! Der Durchlaufzähler bleibt bei 0(!!!) und tickdiff hat einen
Wert von bis zu 15, was einer Verspätung von 1,7µm entspricht!!!
Wer oder was klaut Prozessorticks? Kann/muss noch etwas abgeschaltet
werden?
Vielen Dank schon im voraus für eine hilfreiche Antwort