Multitasking for real time clock?
Multitasking for real time clock?
Hello, all. Right now I am trying to write IDE driver for a minimal filesystem implementation in my OS. Since IDE configuration requires a sleep() function for a delay of 400ms, all the implementation that I have seen till now has Multitasking support and are stopping the currently running processes. It is correct to think as there should be some other process running for it to sleep. Can I implement sleep without multitasking or do I have to add multitasking support first?
Re: Multitasking for real time clock?
I'd implement multitasking first. But you can set a one-shot timer and a "hlt" instruction where you want to sleep. This will stop the processor until the interrupt from the timer.
Re: Multitasking for real time clock?
Make an idle process which simply calls hlt as suggested by iansjack
Re: Multitasking for real time clock?
Ch4ozz wrote:Make an idle process which simply calls hlt as suggested by iansjack
I know that timer fires 18.222 per second, can I add something so that it delays for 400ms?I'd implement multitasking first. But you can set a one-shot timer and a "hlt" instruction where you want to sleep. This will stop the processor until the interrupt from the timer.
Code: Select all
static void timer_callback(registers_t regs)
{
tick++;
if (tick % 18 == 0)
{
if (pit_handler_nest() != 0)
{
kprintf("PIT handler error");
}
}
}
Code: Select all
void sleep(u32 ticks)
{
u32 end = tick + ticks;
while(tick < end);
}
Re: Multitasking for real time clock?
Hi,
Note that I'd also recommend abstracting the time source, so that it's trivial to add support for other time sources (HPET, local APIC timer, etc) later. With this in mind I'd keep track of time using something like "nanoseconds since boot" that is always the same regardless of time source (and wouldn't use something like "ticks since boot").
I'd also consider going one step further and using something similar for tracking real time (e.g. 64-bit nanoseconds since the year 2000) so you can do "current_time = nanoseconds_since_boot + time_at_boot".
Cheers,
Brendan
The PIT chip is left as 18.22 Hz by the BIOS (which is the slowest frequency the PIT chip supports); but it can and probably should be reprogrammed. By reprogramming the PIT chip you can set it to any frequency from 18.22 Hz to about 10 KHz, but (instead of having a fixed frequency) you can also set it to "one shot mode" where you set the delay to what you actually want. This gives you far better precision (e.g. "+/- 838 nanoseconds of precision" rather than the "+/- 1 millisecond" you'd get if you set the PIT to a fixed frequency of 1 KHz) and avoids IRQs you don't want (e.g. one IRQ after 400 ms rather than 400 IRQs with an IRQ every 1 ms).dream21 wrote:I know that timer fires 18.222 per second, can I add something so that it delays for 400ms?
Note that I'd also recommend abstracting the time source, so that it's trivial to add support for other time sources (HPET, local APIC timer, etc) later. With this in mind I'd keep track of time using something like "nanoseconds since boot" that is always the same regardless of time source (and wouldn't use something like "ticks since boot").
I'd also consider going one step further and using something similar for tracking real time (e.g. 64-bit nanoseconds since the year 2000) so you can do "current_time = nanoseconds_since_boot + time_at_boot".
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Multitasking for real time clock?
May I just comment that you might want to look at the specifications once more. That 400ms is probably 400ns (nano). A read from the Alt Status Register takes longer than this, so is a good delay.dream21 wrote:Hello, all. Right now I am trying to write IDE driver for a minimal filesystem implementation in my OS. Since IDE configuration requires a sleep() function for a delay of 400ms, all the implementation that I have seen till now has Multitasking support and are stopping the currently running processes. It is correct to think as there should be some other process running for it to sleep. Can I implement sleep without multitasking or do I have to add multitasking support first?
However, you will need to implement a timer of some kind sooner or later (pun intended), so take the suggestions made here.
Ben
Re: Multitasking for real time clock?
Thank you for your suggestions sir but right now I am in more need of examples or refrences as I am new here. Surely your suggestions will help me make a more reliable RTC in the future.Brendan wrote: Note that I'd also recommend abstracting the time source, so that it's trivial to add support for other time sources (HPET, local APIC timer, etc) later. With this in mind I'd keep track of time using something like "nanoseconds since boot" that is always the same regardless of time source (and wouldn't use something like "ticks since boot").
I'd also consider going one step further and using something similar for tracking real time (e.g. 64-bit nanoseconds since the year 2000) so you can do "current_time = nanoseconds_since_boot + time_at_boot".
Re: Multitasking for real time clock?
Hi,
Cheers,
Brendan
There's example code for the PIT in the wiki page for the PIT, and example code for the RTC in the wiki page for the RTC ("Real Time Clock").dream21 wrote:Thank you for your suggestions sir but right now I am in more need of examples or refrences as I am new here. Surely your suggestions will help me make a more reliable RTC in the future.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Multitasking for real time clock?
I looked at the RTC and CMOS tutorial and made a working implementation. My code is just a copy and goes like this.Brendan wrote: There's example code for the PIT in the wiki page for the PIT, and example code for the RTC in the wiki page for the RTC ("Real Time Clock").
Code: Select all
u8 read_register(int reg)
{
outb(reg, cmos_address);
return inb(cmos_data);
}
int get_update_in_progress_flag()
{
outb(0x0A, cmos_address);
return (inb(cmos_data) & 0x80);
}
time_t rtc_handler()
{
bool ready = read_register(0x0C) & 0x10;
if (ready)
{
{
current_time.second = read_register(0x00);
current_time.minute = read_register(0x02);
current_time.hour = read_register(0x04);
current_time.month = read_register(0x08);
current_time.year = read_register(0x09);
current_time.day_of_week = read_register(0x06);
current_time.day_of_month = read_register(0x07);
current_time.century = read_register(0x32);
}
}
return current_time;
}
void rtc_install(void)
{
unsigned char status;
status = read_register(0x0B);
status |= 0x02; // 24 hour clock
status |= 0x10; // update ended interrupts
status &= ~0x20; // no alarm interrupts
status &= ~ 0x40; // no periodic interrupts
bcd = !(status & 0x04); // check if data type is BCD
write_register(0x0B, status);
read_register(0x0C);
//register_interrupt_handler(40, &rtc_handler);
}
Re: Multitasking for real time clock?
Hi,
needs some kind of protection around it, in case an IRQ or task switch happens causing something else to change "cmos_address" after it's been set but before "cmos_data" has been read.
This code:
should use the "read_register()" function above instead for reading the register itself.
This code:
is extremely slow (every "read_register()" costs several thousand cycles of CPU time due to slow IO ports) and shouldn't be used anyway.
Typically an OS gets the current time from the RTC once during boot and convert it into some kind of "time since epoch" format (e.g. "seconds since 1970 in UTC", or "nanoseconds since 2000 in UTC" or ...), and then only does a "time_since_epoch += time_between_ticks" (e.g. when the update IRQ occurs) after that.
This code:
is broken. You can't change the way the RTC's time/date registers are configured (e.g. change them from 12-hour to 24-hour) without breaking the BIOS and ruining everything. Unlike most hardware, the RTC is battery backed and remembers how its time/date registers are configured even when the computer is turned off, which means that if you change it then turn the computer off, the next time the computer is turned on the BIOS sees all kinds of unexpected data in the time/date registers because the RTC was left in a state that the BIOS doesn't expect and doesn't support.
Instead; you need to leave the time/date register configuration "as is", and read the pre-existing configuration to figure out how to decode the time/date register values (e.g. convert from BCD to binary if it was using BCD, and convert from 12-hour to 24-hour if it was using 12-hour; so that you end up with "binary 24-hour" regardless of how the time/date registers are configured).
Note: If you do change the way the RTC's time/date registers are configured; then you also have to write corrected values into those registers. For example, if the RTC was configured as "12-hour BCD" and the "hour" register contains the value 0x82 (meaning 2 pm) and you change it to "24-hour BCD", then the "hour" register will still contain the value 0x82 (meaning 82 o'clock, which is pure nonsense).
Cheers,
Brendan
This code:dream21 wrote:I looked at the RTC and CMOS tutorial and made a working implementation. My code is just a copy and goes like this.
Code: Select all
u8 read_register(int reg)
{
outb(reg, cmos_address);
return inb(cmos_data);
}
This code:
Code: Select all
int get_update_in_progress_flag()
{
outb(0x0A, cmos_address);
return (inb(cmos_data) & 0x80);
}
This code:
Code: Select all
time_t rtc_handler()
{
bool ready = read_register(0x0C) & 0x10;
if (ready)
{
{
current_time.second = read_register(0x00);
current_time.minute = read_register(0x02);
current_time.hour = read_register(0x04);
current_time.month = read_register(0x08);
current_time.year = read_register(0x09);
current_time.day_of_week = read_register(0x06);
current_time.day_of_month = read_register(0x07);
current_time.century = read_register(0x32);
}
}
return current_time;
}
Typically an OS gets the current time from the RTC once during boot and convert it into some kind of "time since epoch" format (e.g. "seconds since 1970 in UTC", or "nanoseconds since 2000 in UTC" or ...), and then only does a "time_since_epoch += time_between_ticks" (e.g. when the update IRQ occurs) after that.
This code:
Code: Select all
void rtc_install(void)
{
unsigned char status;
status = read_register(0x0B);
status |= 0x02; // 24 hour clock
status |= 0x10; // update ended interrupts
status &= ~0x20; // no alarm interrupts
status &= ~ 0x40; // no periodic interrupts
bcd = !(status & 0x04); // check if data type is BCD
write_register(0x0B, status);
read_register(0x0C);
//register_interrupt_handler(40, &rtc_handler);
}
Instead; you need to leave the time/date register configuration "as is", and read the pre-existing configuration to figure out how to decode the time/date register values (e.g. convert from BCD to binary if it was using BCD, and convert from 12-hour to 24-hour if it was using 12-hour; so that you end up with "binary 24-hour" regardless of how the time/date registers are configured).
Note: If you do change the way the RTC's time/date registers are configured; then you also have to write corrected values into those registers. For example, if the RTC was configured as "12-hour BCD" and the "hour" register contains the value 0x82 (meaning 2 pm) and you change it to "24-hour BCD", then the "hour" register will still contain the value 0x82 (meaning 82 o'clock, which is pure nonsense).
I have no idea - there's no code to print any of the values. Note that it should take up to 1 second before you get your first "update interrupt" (and if you're using an emulator, in some cases "1 virtual second" could take "10 actual seconds").dream21 wrote:Also I have to give a certain amount of delay between rtc_install and printing the values. What could be the reason for it?
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.