PIT appears to run a lot slower at higher frequencies
Posted: Sat Jul 13, 2024 12:45 pm
I have configured IRQ0 to increase a global timer every time it is called with PIT mode 2. I have tried to make it such that, irrespective of what frequency I've chosen, the timer should remain accurate. I have done this by calculating the interval between IRQ calls and incrementing the timer by that interval every calll, similar to the code provided in the wiki page. When I choose very low frequencies, it seems to work just fine, but at higher frequencies, it takes several seconds for the global timer to increase by one second. The inaccuracy seems to increase with the frequency. I've tried to search for this issue online, but I have not yet found someone with a problem similar to mine. If any further information needs to be provided, I will gladly amend it to the post.
The PIT code is as follows:
The main code is as follows:
The PIT code is as follows:
Code: Select all
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
/*
* CLOSEST(x, a, b) determines whether x is closer to a or b.
* If x is closer to b then CLOSEST(x, a, b) > 0.
* If x is closer to a then CLOSEST(x, a, b) < 0.
* If x is equally close to both a and b then CLOSEST(x, a, b) = 0.
*/
#define CLOSEST(x, low, high) \
((x) - (low) > (high) - (x) ? 1 : (x) - (low) < (high) - (x) ? -1 : 0)
#define RELOAD(frequency) (PIT_MAX_FREQUENCY / (frequency))
#define FREQ(reload) (PIT_MAX_FREQUENCY / (reload))
static struct timespec irq_interval = {0};
static struct timespec pit_timer = {0};
static uint32_t pit_frequency;
static uint16_t pit_reload;
static void irq0_handler(unsigned irq_line);
void pit_init(void) { irq_set_handler(0, irq0_handler); }
void pit_set_frequency(uint32_t frequency) {
frequency = MIN(MAX(frequency, PIT_MIN_FREQUENCY), PIT_MAX_FREQUENCY);
pit_reload = MIN(RELOAD(frequency), UINT16_MAX);
if (CLOSEST(frequency, FREQ(pit_reload), FREQ(pit_reload + 1)) > 0)
++pit_reload;
pit_frequency = FREQ(pit_reload);
irq_interval.tv_nsec = 1000000000 / pit_frequency;
__asm__("cli");
port_out8(PORT_PIT_CMD, pit_cmd(0, PIT_ACCESS_FULL, 2));
port_out8(PORT_PIT_CH0_DATA, (pit_reload >> 0) & 0xFF);
port_out8(PORT_PIT_CH0_DATA, (pit_reload >> 8) & 0xFF);
__asm__("sti");
}
const struct timespec *pit_get_global_timer(void) { return &pit_timer; }
static void irq0_handler(unsigned irq_line) {
bext_timespec_add(&pit_timer, &irq_interval);
pic_send_eoi(irq_line);
}
Code: Select all
void kernel_main(void) {
putchar('\f');
gdt_init();
idt_init();
pic_init();
pit_init();
pit_set_frequency(10000);
for (;;) {
volatile const struct timespec *timer = pit_get_global_timer();
bext_printf_basic(
"%us %uns\r", (unsigned)timer->tv_sec, (unsigned)timer->tv_nsec
);
}
}