Multitasking for real time clock?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
dream21
Member
Member
Posts: 25
Joined: Thu Aug 18, 2016 12:54 pm

Multitasking for real time clock?

Post by dream21 »

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?
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Multitasking for real time clock?

Post by iansjack »

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.
User avatar
Ch4ozz
Member
Member
Posts: 170
Joined: Mon Jul 18, 2016 2:46 pm
Libera.chat IRC: esi

Re: Multitasking for real time clock?

Post by Ch4ozz »

Make an idle process which simply calls hlt as suggested by iansjack
dream21
Member
Member
Posts: 25
Joined: Thu Aug 18, 2016 12:54 pm

Re: Multitasking for real time clock?

Post by dream21 »

Ch4ozz wrote:Make an idle process which simply calls hlt as suggested by iansjack
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.
I know that timer fires 18.222 per second, can I add something so that it delays for 400ms?

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);
}
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Multitasking for real time clock?

Post by Brendan »

Hi,
dream21 wrote:I know that timer fires 18.222 per second, can I add something so that it delays for 400ms?
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).

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.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: Multitasking for real time clock?

Post by BenLunt »

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?
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.

However, you will need to implement a timer of some kind sooner or later (pun intended), so take the suggestions made here.
Ben
dream21
Member
Member
Posts: 25
Joined: Thu Aug 18, 2016 12:54 pm

Re: Multitasking for real time clock?

Post by dream21 »

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".
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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Multitasking for real time clock?

Post by Brendan »

Hi,
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.
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").


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.
dream21
Member
Member
Posts: 25
Joined: Thu Aug 18, 2016 12:54 pm

Re: Multitasking for real time clock?

Post by dream21 »

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").
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);
}

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);
}
The problem is when I try to print the hour and minutes, it gives me incorrect results. Also I have to give a certain amount of delay between rtc_install and printing the values. What could be the reason for it?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Multitasking for real time clock?

Post by Brendan »

Hi,
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.
This code:

Code: Select all

u8 read_register(int reg)
{
	outb(reg, cmos_address);
	return inb(cmos_data);
}
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:

Code: Select all

int get_update_in_progress_flag() 
{
	outb(0x0A, cmos_address);
	return (inb(cmos_data) & 0x80);
}
should use the "read_register()" function above instead for reading the register itself.


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;
}
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:

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);
}
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).
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?
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").


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.
Post Reply