Hello,
Zhaoming Luo, le sam. 22 mars 2025 15:24:50 +0800, a ecrit:
> The HPET counter is read once in every clock interrupt. When any of the
> functions used for getting time is used, the HPET counter is read again and
> the difference between these two read is multiplied by the HPET period
> and added to the read value from time or uptime to get a more accurate
> time read.
>
> It seems not bad, we can have the accuracy of 10ns:
> ```
> demo@debian:~$ ./main
> Seconds: 1405, Nanoseconds: 688178790
> demo@debian:~$ ./main
> Seconds: 1406, Nanoseconds: 216391110
> ```
Nice!
> BTW, I found that CLOCK_REALTIME is still using host_get_time() instead
> of host_get_time64() [0].
Indeed, contribution welcome ;)
> diff --git a/i386/i386/apic.h b/i386/i386/apic.h
> index 92fb900a..30aa8521 100644
> --- a/i386/i386/apic.h
> +++ b/i386/i386/apic.h
> @@ -268,6 +268,8 @@ void ioapic_configure(void);
> void hpet_init(void);
> void hpet_udelay(uint32_t us);
> void hpet_mdelay(uint32_t ms);
> +uint32_t read_hpet_counter(void);
> +uint32_t get_hpet_period_nsec(void);
>
> extern int timer_pin;
> extern void intnull(int unit);
Please rather put them into ./kern/mach_clock.h, and avoid
using "hpet" in the name, so that interface is general and not
specific to i386. You can call them e.g. clock_read_counter() and
clock_get_counter_period_nsec()
> diff --git a/i386/i386/model_dep.h b/i386/i386/model_dep.h
> index 5369e288..6a46cbbf 100644
> --- a/i386/i386/model_dep.h
> +++ b/i386/i386/model_dep.h
> @@ -65,4 +65,14 @@ extern void machine_relax (void);
> */
> extern void c_boot_entry(vm_offset_t bi);
>
> +/*
> + * Return the tick value of hpet
> + */
> +extern uint32_t read_hpet_counter (void);
> +
> +/*
> + * Return the period of HPET timer in nanoseconds
> + */
> +extern uint32_t get_hpet_period_nsec (void);
> +
> #endif /* _I386AT_MODEL_DEP_H_ */
No need to declare them several times :)
> diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
> index 30449c37..aa8451ac 100644
> --- a/i386/i386at/model_dep.c
> +++ b/i386/i386at/model_dep.c
> @@ -229,6 +229,11 @@ void machine_init(void)
> */
> hpet_init();
> #endif
> +
> + /*
> + * Initialize the HPET
> + */
> + hpet_init();
> }
>
> /* Conserve power on processor CPU. */
Rather move #endif above the hpet_init() call. And put a #ifdef APIC
around it.
You should also cope with the case where hpet_addr is NULL.
> diff --git a/kern/mach_clock.c b/kern/mach_clock.c
> index 5501b7b8..63a6988f 100644
> --- a/kern/mach_clock.c
> +++ b/kern/mach_clock.c
> @@ -83,6 +83,15 @@ unsigned tickadj = 500 / HZ; /* can adjust 100 usecs
> per second */
> unsigned bigadj = 1000000; /* adjust 10*tickadj if adjustment
> > bigadj */
>
> +/* HPET is taken into account to increase the accuracy of the functions
> + * used for getting time (e.g. host_get_time64()). The HPET counter is
> + * read once in every clock interrupt. When any of the functions used
> + * for getting time is used, the HPET counter is read again and the
> + * difference between these two read is multiplied by the HPET period
> + * and added to the read value from time or uptime to get a more accurate
> + * time read. */
Here as well, avoid using the HPET name, just call it "high-precision
hardware clock".
> +uint32_t last_hpet_read = 0;
> +
> /*
> * This update protocol, with a check value, allows
> * do {
> @@ -128,7 +137,8 @@ MACRO_BEGIN
> \
> __sync_synchronize(); \
> (time)->nanoseconds = mtime->time_value.nanoseconds; \
> __sync_synchronize(); \
> - } while ((time)->seconds != mtime->check_seconds64); \
> + } while ((time)->seconds != mtime->check_seconds64); \
> + time_value64_add_hpet(time); \
> MACRO_END
>
> #define read_mapped_uptime(uptime) \
> @@ -139,6 +149,7 @@ MACRO_BEGIN
> \
> (uptime)->nanoseconds = mtime->uptime_value.nanoseconds;\
> __sync_synchronize(); \
> } while ((uptime)->seconds != mtime->check_upseconds64); \
> + time_value64_add_hpet(uptime); \
> MACRO_END
>
> def_simple_lock_irq_data(static, timer_lock) /* lock for ... */
> @@ -292,6 +303,7 @@ void clock_interrupt(
> }
> }
> }
> + last_hpet_read = read_hpet_counter();
> }
>
> /*
> @@ -426,6 +438,19 @@ clock_boottime_update(const struct time_value64
> *new_time)
> time_value64_add(&clock_boottime_offset, &delta);
> }
>
> +/*
> + * Add the time value since last clock interrupt in nanosecond.
> + */
> +static void
> +time_value64_add_hpet(time_value64_t *value)
> +{
> + uint32_t now = read_hpet_counter();
> + /* Time since last clock interrupt in nanosecond. */
> + int ns = (now - last_hpet_read) * get_hpet_period_nsec();
Please cap that value to the period of a clock interrupt. Otherwise, if
for any reason the computation is not that precise and we end up having
ns greater than the clock interrupt period, time would go *back*, which
we really don't want :) Better see time stuck at the very end of the
clock interrupt period in case of such computation imprecision.
> + time_value64_add_nanos(value, ns);
> +}
Samuel