Handle insufficient DOS timer precision by measuring a 100 ms delay instead of a 100 us delay. This should reduce the likelihood of flashrom timer complaints from 100% to 50%. If you want less complaints, use 1000 ms instead. This is mostly a proof of concept and I'd appreciate tests.
Once I know which #defines to look for on MinGW and Cygwin, I can modify the #if to cover them as well.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-measure_delay_precision_dos/udelay.c =================================================================== --- flashrom-measure_delay_precision_dos/udelay.c (Revision 1008) +++ flashrom-measure_delay_precision_dos/udelay.c (Arbeitskopie) @@ -93,7 +93,19 @@ * a scheduler delay or something similar. */ for (i = 0; i < 4; i++) { - if (measure_delay(100) < 90) { +#if !defined(__DJGPP__) + timeusec = measure_delay(100); +#else + /* This zero delay workaround should be active for + * DOS and Windows and maybe libpayload. The criterion + * here is insufficient (worse than 100 us) OS timer + * resolution which will result in measure_delay(100)=0 + * whereas a longer delay (100 ms) will be sufficient + * to get a nonzero time measurement. + */ + timeusec = measure_delay(100000) / 1000; +#endif + if (timeusec < 90) { msg_pdbg("delay more than 10%% too short, " "recalculating... "); goto recalibrate;
Totally new patch.
Do not trust the OS at all and measure timer precision before calibrating the delay loop and use that measurement to get reasonable precision for our own delay code.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-measure_delay_precision_dos/udelay.c =================================================================== --- flashrom-measure_delay_precision_dos/udelay.c (Revision 1023) +++ flashrom-measure_delay_precision_dos/udelay.c (Arbeitskopie) @@ -37,6 +37,30 @@ } }
+unsigned long measure_os_delay_resolution(void) +{ + unsigned long timeusec; + struct timeval start, end; + unsigned long counter = 0; + + gettimeofday(&start, 0); + timeusec = 0; + + while (!timeusec && (++counter < 1000000000)) { + gettimeofday(&end, 0); + timeusec = 1000000 * (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec); + /* Protect against time going forward too much. */ + if ((end.tv_sec > start.tv_sec) && + ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1)) + timeusec = 0; + /* Protect against time going backwards during leap seconds. */ + if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX)) + timeusec = 0; + } + return timeusec; +} + unsigned long measure_delay(int usecs) { unsigned long timeusec; @@ -61,10 +85,16 @@ void myusec_calibrate_delay(void) { unsigned long count = 1000; - unsigned long timeusec; + unsigned long timeusec, resolution; int i, tries = 0;
msg_pinfo("Calibrating delay loop... "); + resolution = measure_os_delay_resolution(); + if (resolution) { + msg_pdbg("OS timer resolution is %u usecs, ", resolution); + } else { + msg_pinfo("OS timer resolution is unusable. "); + }
recalibrate: count = 1000; @@ -94,7 +124,22 @@ * a scheduler delay or something similar. */ for (i = 0; i < 4; i++) { - if (measure_delay(100) < 90) { + if (resolution && (resolution < 10)) { + timeusec = measure_delay(100); + } else if (resolution && (resolution < ULONG_MAX / 200)) { + timeusec = (measure_delay(resolution * 10) * 100) / (resolution * 10); + } else { + /* This workaround should be active for broken + * OS and maybe libpayload. The criterion + * here is horrible or non-measurable OS timer + * resolution which will result in + * measure_delay(100)=0 whereas a longer delay + * (1000 ms) may be sufficient + * to get a nonzero time measurement. + */ + timeusec = measure_delay(1000000) / 10000; + } + if (timeusec < 90) { msg_pdbg("delay more than 10%% too short, " "recalculating... "); goto recalibrate;
On 02.06.2010 15:21, Carl-Daniel Hailfinger wrote:
Do not trust the OS at all and measure timer precision before calibrating the delay loop and use that measurement to get reasonable precision for our own delay code.
Changes in this patch: Print a measurement for a delay of 4x the OS timer resolution. Be precise about how bad the deviation was if we had to recalculate.
Tests on DOS and Windows appreciated. I tested on Linux.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-measure_delay_precision_dos/udelay.c =================================================================== --- flashrom-measure_delay_precision_dos/udelay.c (Revision 1026) +++ flashrom-measure_delay_precision_dos/udelay.c (Arbeitskopie) @@ -37,6 +37,30 @@ } }
+unsigned long measure_os_delay_resolution(void) +{ + unsigned long timeusec; + struct timeval start, end; + unsigned long counter = 0; + + gettimeofday(&start, 0); + timeusec = 0; + + while (!timeusec && (++counter < 1000000000)) { + gettimeofday(&end, 0); + timeusec = 1000000 * (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec); + /* Protect against time going forward too much. */ + if ((end.tv_sec > start.tv_sec) && + ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1)) + timeusec = 0; + /* Protect against time going backwards during leap seconds. */ + if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX)) + timeusec = 0; + } + return timeusec; +} + unsigned long measure_delay(int usecs) { unsigned long timeusec; @@ -61,10 +85,16 @@ void myusec_calibrate_delay(void) { unsigned long count = 1000; - unsigned long timeusec; + unsigned long timeusec, resolution; int i, tries = 0;
msg_pinfo("Calibrating delay loop... "); + resolution = measure_os_delay_resolution(); + if (resolution) { + msg_pdbg("OS timer resolution is %u usecs, ", resolution); + } else { + msg_pinfo("OS timer resolution is unusable. "); + }
recalibrate: count = 1000; @@ -94,9 +124,25 @@ * a scheduler delay or something similar. */ for (i = 0; i < 4; i++) { - if (measure_delay(100) < 90) { - msg_pdbg("delay more than 10%% too short, " - "recalculating... "); + if (resolution && (resolution < 10)) { + timeusec = measure_delay(100); + } else if (resolution && (resolution < ULONG_MAX / 200)) { + timeusec = (measure_delay(resolution * 10) * 100) / (resolution * 10); + } else { + /* This workaround should be active for broken + * OS and maybe libpayload. The criterion + * here is horrible or non-measurable OS timer + * resolution which will result in + * measure_delay(100)=0 whereas a longer delay + * (1000 ms) may be sufficient + * to get a nonzero time measurement. + */ + timeusec = measure_delay(1000000) / 10000; + } + if (timeusec < 90) { + msg_pdbg("delay more than 10%% too short (got " + "%lu%% of expected delay), " + "recalculating... ", timeusec); goto recalibrate; } } @@ -113,6 +159,8 @@ msg_pdbg("1000 myus = %ld us, ", timeusec); timeusec = measure_delay(10000); msg_pdbg("10000 myus = %ld us, ", timeusec); + timeusec = measure_delay(resolution * 4); + msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
msg_pinfo("OK.\n"); }
On Thu, Jun 03, 2010 at 03:44:14PM +0200, Carl-Daniel Hailfinger wrote:
Tests on DOS and Windows appreciated. I tested on Linux.
Tested on Windows XP, 32 bit, built using MinGW, output:
flashrom v0.9.2-runknown on unknown machine, built with GCC 3.4.5 (mingw-vista special r3), little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 15625 usecs, 467M loops per second, 10 myus = 0 us, 100 myus = 0 us, 1000 myus = 0 us, 10000 myus = 0 us, 62500 myus = 62500 us, OK.
(the "-runknown" is OK, I don't have cmdline svn tools in my $PATH)
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Acked-by: Uwe Hermann uwe@hermann-uwe.de
timeusec = (measure_delay(resolution * 10) * 100) / (resolution * 10);
Minor nitpick: please make this one fit in 80 chars / line.
Uwe.
2010/6/3 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Tests on DOS and Windows appreciated. I tested on Linux.
Tested on FreeDOS v1.0 Final:
flashrom v0.9.2-r1026 on FreeDOS 7 (i786), built with libpci 3.1.5, GCC 4.3.2, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 160000 usecs, 1873M loops per second, 10 myus = 0 us, 100 myus = 0 us, 1000 myus = 0 us, 10000 myus = 0 us, 640000 myus = 600000 us, OK.
And tested on FreeBSD:
flashrom v0.9.2-r1026 on FreeBSD 8.0-RELEASE-p3 (i386), built with libpci 3.1.7, GCC 4.2.1 20070719 [FreeBSD], little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 17 usecs, 1883M loops per second, 10 myus = 13 us, 100 myus = 107 us, 1000 myus = 969 us, 10000 myus = 11579 us, 68 myus = 125 us, OK.
Acked-by: Idwer Vollering <vidwer+lists.flashrom@gmail.comvidwer%2Blists.flashrom@gmail.com
On 03.06.2010 21:17, Idwer Vollering wrote:
Tested on FreeDOS v1.0 Final: [...] And tested on FreeBSD: [...]
Acked-by: Idwer Vollering <vidwer+lists.flashrom@gmail.comvidwer%2Blists.flashrom@gmail.com
Thanks, committed in r1028 with the formatting changes requested by Uwe.
Regards, Carl-Daniel