1 / 41

Real Time Operating System RTLinux – time module

Real Time Operating System RTLinux – time module. u9 01631 陳碩璜. Real Time Operating System. able to execute all of its tasks without violating specific timing constraints . RTLinux version2 : 15us in worst case Linux : unpredictable ( level of ms )

ollie
Download Presentation

Real Time Operating System RTLinux – time module

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Real Time Operating System RTLinux – time module u901631 陳碩璜

  2. Real Time Operating System • able to execute all of its tasks without violating specific timing constraints . RTLinux version2 : 15us in worst case Linux : unpredictable ( level of ms ) • Time when the task will execute can be predicted deterministically on the basis of knowledge about the system`s hardwre and software.

  3. Why is Linux unsuitable • Delay is unpredictable ex: file system . page fault . disable interrupt … • Resolution of time is not good • generate timer interrupt every 1~10ms • delay function , But …

  4. Con. • Dynamic timer : for kernel • Interval timer : for user processes • Linux has • Timer handler executed by deferrable function • Critical • Noncritical • Noncritical deferrable

  5. Timing in RTLinux • Programmable interval timer inhibit regular timer interrupts • RTLinux still requires low resolution to do normal periodic timer , so the interrupt emulator simulates this by generating periodical 1~10ms interrupt for Linux .

  6. Time Facility • RTLinux provides several timer objects of different clocks as the low level interface for the scheduler . obtaining timestamps … • Here is the general timing APIs • int clock_gettime(clockid_t clock_id, struct timespec *tp) • hrtime_t clock_gethrtime (clockid_t clock_id , ) • for POSIX standard • hrtime_t t = clock_id->gethrtime(clock_id); • struct timespec{ time_t tv_sec ; //second • long tv_nsec ; // nano second} • typedef long long hrtime_t;

  7. Supported Clocks (Version 3.2) • Programmable Interval Timer (PIC) • Time Stamp Counter (TSC) • Local APIC Timer

  8. Programmable Interval Timer • 8254 CMOS chip using 0x40~0x43 I/O ports • 16 bits down counter • Support several modes • Command register : 0x43 • 3 16-bits counters : 0x40~0x42 • Driven by 1193182 HZ oscillator • One shot • periodical • …

  9. From Intel document

  10. ex : outb(0xb6 , 0x43) 0xb6 = 1011 0110 From Intel document

  11. Time Stamp Counter • 64-bits counter driven by CPU clocks • 1 nsec resolution for 1GHZ CPU • High Resolution • Drawbacks • Frequency varies • Power management changes CPU clock

  12. Local APIC Timer • 32-bit down-counter driven by bus clock signal • Send the interrupt to its processor only • (PIC generates the global interrupt) • Three registers From Intel document

  13. Trace RTLinux Source Code rtl_time.c

  14. struct rtl_clock { int (*init) (struct rtl_clock *); void (*uninit) (struct rtl_clock *); hrtime_t (*gethrtime)(struct rtl_clock *); int (*sethrtime)(struct rtl_clock *, hrtime_t t); int (*settimer)(struct rtl_clock *, hrtime_t interval); int (*settimermode)(struct rtl_clock *, int mode); clock_irq_handler_t handler; int mode; hrtime_t resolution; hrtime_t value; hrtime_t delta; pthread_spinlock_t lock; struct rtl_clock_arch arch; }; time from initialization of rtl_clock data about Linux

  15. int init_module (void) {rtl_spin_lock_init (&lock8254); rtl_spin_lock_init (&lock_linuxtime); init_hrtime(); rtl_create_clock_8254(); #ifdef CONFIG_X86_LOCAL_APIC { int i; for (i = 0; i < rtl_num_cpus(); i++) { int cpu = cpu_logical_map (i); rtl_create_clock_apic(cpu); } }

  16. #ifdef CONFIG_RTL_CLOCK_GPOS if (smp_found_config) { rtl_irqstate_t flags; rtl_no_interrupts(flags); _8254_init(&_i8254_clock); _i8254_clock. settimermode (&_i8254_clock, RTL_CLOCK_MODE_PERIODIC); _i8254_clock.settimer (&_i8254_clock, LATCH_NS); rtl_restore_interrupts(flags);} #endif #endif rtl_init_standard_clocks(); #ifdef CONFIG_RTL_CLOCK_GPOS rtl_clock_gpos_init(); #endif return 0; } initialize struct rtl_clock clock_ust

  17. For what unsigned long scaler_8254_to_hrtime; unsigned long scaler_hrtime_to_8254; static unsigned long scaler_pentium_to_hrtime; static unsigned long scaler_hrtime_to_apic; static hrtime_t (*rtl_do_get_time)(void); From TSC or from PIC 8254 TSC local APIC nano sec nextreturn

  18. CLOCK_TICK_RATE=1193182 HRTICKS_PER_SEC=1000000000 muldiv(a,mul,div)=(a*mul)/div static void init_hrtime (void) { int flags; #if HRTICKS_PER_SEC != CLOCK_TICK_RATE scaler_8254_to_hrtime = muldiv (HRTICKS_PER_SEC, 1 << 22, CLOCK_TICK_RATE); scaler_hrtime_to_8254 = muldiv (CLOCK_TICK_RATE, 1 << 31, HRTICKS_PER_SEC / 2); LATCH_NS = muldiv (LATCH, HRTICKS_PER_SEC, CLOCK_TICK_RATE); #else LATCH_NS = LATCH; #endif ns / LATCH ==CLOCK_TICK_RATE / HZ HZ=100 nextreturn

  19. MAX_LATCH_ONESHOT = LATCH_NS * 3 / 4; rtl_no_interrupts(flags); if (cpu_has_tsc && !notsc) { can_change_latch2 = 1; do_calibration(1); rtl_do_get_time = pent_gettime; hrtime_resolution = 32;} == (TSC register) * scaler_pentium_to_hrtime nextreturn

  20. } else { // if no TSC do_calibration(0); can_change_latch2 = 0; outb(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */ outb(LATCH2 & 0xff, 0x42); outb((LATCH2 >> 8) & 0xff, 0x42); outb((inb(0x61) & 0xfd) | 1, 0x61); /* shut up the speaker and enable counting */ LATCH_CNT2(); READ_CNT2(last_c2); offset_time = 0; base_time = 0; rtl_do_get_time = global_8254_gettime; hrtime_resolution = HRTICKS_PER_SEC / CLOCK_TICK_RATE; } // end of init_hrtime return ==32*1024 used to modify base_time used to keep time from now

  21. static void do_calibration(int do_tsc) { #ifdef CONFIG_X86_LOCAL_APIC long temp = 0 , a1 = 0 , a2 = 0 , save_apic = 0 , result_apic; #endif long long t1 = 0 , t2 = 0 ; long pps; int j; long result = 0; rtl_irqstate_t flags; rtl_no_interrupts(flags); #ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { save_apic = apic_read(APIC_TMICT); apic_write(APIC_TMICT, 1000000000/APIC_DIVISOR); } #endif next unsigned long Read initial count register of local APIC timer ==16 write to initial count register of local APIC timer

  22. outb((inb(0x61) & ~0x02) | 0x01, 0x61); outb(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */ outb(CLATCH & 0xff, 0x42); outb(CLATCH >> 8, 0x42); wait_cycle(); if (do_tsc) rdtscll(t1); #ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { a1 = apic_read(APIC_TMCCT);} #endif next apic_read(APIC_TMCCP): read the current count register of local APIC rdtscll(x): read TSC register ==1024*32 ==50 for (j = 0; j < NLOOPS; j++) { wait_cycle();} if (do_tsc) rdtscll(t2); #ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { a2 = apic_read(APIC_TMCCT); } #define wait_value(x) do {; } while ((inb(0x61) & 0x20) != (x)) #define wait_cycle() do { wait_value(0); wait_value(0x20); } while (0)

  23. result_apic = a1 - a2; <= duration of local APIC timer #endif if (do_tsc) result = t2 - t1; <= duration of TSC #ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { restore the original value temp = apic_read(APIC_TMICT); apic_write(APIC_TMICT, save_apic);} #endif rtl_restore_interrupts(flags); if (do_tsc) { ==count of TSC during one sec pps = muldiv (result, CLOCK_TICK_RATE, CLATCH * NLOOPS); #if HRTICKS_PER_SEC == NSECS_PER_SEC Pentium TSC scaler_pentium_to_hrtime = muldiv (1 << 27, HRTICKS_PER_SEC, pps); #else scaler_pentium_to_hrtime = muldiv (1 << 31, HRTICKS_PER_SEC * 2, pps); #endif next

  24. } else { scaler_pentium_to_hrtime = 0; } #ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { temp = ==count of local APIC during one sec muldiv (result_apic, CLOCK_TICK_RATE, CLATCH * NLOOPS); #if HRTICKS_PER_SEC == NSECS_PER_SEC scaler_hrtime_to_apic = muldiv (temp, 1 << 31, HRTICKS_PER_SEC / 2); #else scaler_hrtime_to_apic = muldiv (temp, 1 << (31 - 10), HRTICKS_PER_SEC / 2); #endif apic_ticks_per_sec = temp;} #endif } // end of do_calibration return

  25. Read-Back Command of 8254 #define LATCH_CNT2() outb(0xd8, 0x43); 0xd8 == 1101 1000 #define READ_CNT2(var) \ do { var = inb(0x42); var |= (inb(0x42) << 8); } while (0) return

  26. hrtime_t global_8254_gettime (void) { register unsigned int c2; int flags; long t; rtl_spin_lock_irqsave (&lock8254, flags); LATCH_CNT2(); READ_CNT2(c2); offset_time += ((c2 < last_c2) ? (last_c2 - c2) / 2 : (last_c2 - c2 + LATCH2) / 2); last_c2 = c2; if (offset_time >= CLOCK_TICK_RATE) { offset_time -= CLOCK_TICK_RATE; base_time += HRTICKS_PER_SEC; } nextreturn

  27. #if HRTICKS_PER_SEC != CLOCK_TICK_RATE __asm__("shl $10, %%eax\n\t" "mul %%ebx\n\t" :"=d" (t) : "b" (scaler_8254_to_hrtime), "a" (offset_time)); #else t = offset_time; #endif last_8254_time = base_time + t; rtl_spin_unlock_irqrestore (&lock8254, flags); return last_8254_time; } //end of global_8254_gettime return

  28. int rtl_create_clock_8254(void) { _i8254_clock = RTL_CLOCK_DEFAULTS; _i8254_clock.init = _8254_init; _i8254_clock.uninit = _8254_uninit; _i8254_clock.settimermode = _8254_settimermode; return 0; } return

  29. Default clock constructor #define RTL_CLOCK_INITIALIZER { \ definit, \ defuninit, \ defgethrtime, \ defsethrtime, \ defsettimer, \ defsettimermode, \ default_handler, \ RTL_CLOCK_MODE_UNINITIALIZED, \ 0, \ 0, \ 0, \ PTHREAD_SPINLOCK_INITIALIZER, \ RTL_CLOCK_ARCH_INITIALIZER}; struct rtl_clock RTL_CLOCK_DEFAULTS = RTL_CLOCK_INITIALIZER; return

  30. static int _8254_init (clockid_t clock) { int flags; rtl_no_interrupts (flags); save_do_gettimeoffset = do_gettimeoffset; do_gettimeoffset = do_rt_gettimeoffset; save_use_tsc = use_tsc; use_tsc = 0; rtl_save_jiffies = jiffies; ==return rtl_do_get_time(); _i8254_clock.arch.linux_time = gethrtime() + LATCH_NS; register real time 8254 timer interrupt handler “_8254_irq” rtl_request_global_irq(0, _8254_irq); _8254_settimermode (clock, RTL_CLOCK_MODE_ONESHOT); _i8254_clock.settimer (clock, HRTIME_INFINITY); rtl_restore_interrupts (flags); return 0; } return

  31. int _8254_settimermode (struct rtl_clock *c, int mode) { if (mode == _i8254_clock.mode) { return 0;} if (mode == RTL_CLOCK_MODE_PERIODIC) { outb(0x34, 0x43); 0x34=0011 0100 /* binary, mode 2, LSB/MSB, ch 0 */ _i8254_clock.mode = mode; _i8254_clock.gethrtime = periodic_gethrtime; _i8254_clock.settimer = _8254_setperiodic; _i8254_clock.arch.count_irqs = 0; periodic static hrtime_t periodic_gethrtime (struct rtl_clock *c) { return c->value; } Updated by _8254_irq nextreturn

  32. } else if (mode == RTL_CLOCK_MODE_ONESHOT) { outb(0x30, 0x43); 0x30 == 0011 0000 /* 8254, channel 0, mode 0, lsb+msb */ _i8254_clock.mode = mode; _i8254_clock.gethrtime = oneshot_gethrtime; _i8254_clock.settimer = _8254_setoneshot; _i8254_clock.resolution = HRTICKS_PER_SEC / CLOCK_TICK_RATE; } else { return -EINVAL;} return 0; } One shot return static hrtime_t oneshot_gethrtime (struct rtl_clock *c) { return gethrtime(); }

  33. static int _8254_setperiodic (clockid_t c, hrtime_t interval) { int flags; long t; rtl_spin_lock_irqsave (&lock8254, flags); t = RTIME_to_8254_ticks (interval) + 1; if (t < 10) { t = LATCH; rtl_printf("RTLinux 8254 periodic settimer set too low!\n");} if (t > LATCH) { t = LATCH; rtl_printf("RTLinux 8254 periodic settimer set too high!\n");} WRITE_COUNTER_ZERO16 (t); _i8254_clock.value = gethrtime(); _i8254_clock.resolution = interval; _i8254_clock.arch.istimerset = 1; rtl_spin_unlock_irqrestore(&lock8254, flags); return 0; } return

  34. static inline long RTIME_to_8254_ticks(long t) { #if HRTICKS_PER_SEC != CLOCK_TICK_RATE int dummy; __asm__("mull %2" :"=a" (dummy), "=d" (t) :"g" (scaler_hrtime_to_8254), "0" (t) ); #endif return (t); } #define WRITE_COUNTER_ZERO16(x) do { \ outb(x&0xff,0x40); outb((x>>8)&0xff,0x40);\ clock_counter =x; \ } while (0) return

  35. static int _8254_setoneshot (clockid_t c, hrtime_t interval) { rtl_irqstate_t flags; long t; rtl_spin_lock_irqsave (&lock8254, flags); if (interval > MAX_LATCH_ONESHOT) { interval = MAX_LATCH_ONESHOT;} t = RTIME_to_8254_ticks (interval); /* - _8254_latency); */ if (t < 1) {t = 1;} == WRITE_COUNTER_ZERO16(x) WRITE_COUNTER_ZERO_ONESHOT(t); _i8254_clock.arch.istimerset = 1; rtl_spin_unlock_irqrestore(&lock8254, flags); return 0; } return

  36. unsigned long muldiv(unsigned long a, unsigned long mul, unsigned long div) { int temp; __asm__("mull %2 ; divl %3" :"=a" (a), "=d" (temp) :"1" (mul), "c" (div), "0" (a) ); return a; } return

  37. Example #if HRTICKS_PER_SEC != CLOCK_TICK_RATE __asm__("shl $10, %%eax\n\t" "mul %%ebx\n\t" :"=d" (t) : "b" (scaler_8254_to_hrtime), "a" (offset_time)); scaler_8254_to_hrtime= (HRTICKS_PER_SEC * 2^ 22) / CLOCK_TICK_RATE offset_time * 2^10 * scaler_8254_to_hrtime = offset_time * 2^10 * ((HRTICKS_PER_SEC * 2^ 22) / CLOCK_TICK_RATE) = (offset_time * HRTICKS_PER_SEC / CLOCK_TICK_RATE) * (2^32) ==t return

  38. return

  39. static void rtl_clock_gpos_init(void) { rtl_clock_gpos_irq = rtl_get_soft_irq (clock_irq_handler, "RTLinux CLOCK_GPOS"); clock_gpos = RTL_CLOCK_DEFAULTS; clock_gpos.gethrtime = &gpos_gethrtime; clock_gpos.resolution = gethrtimeres(); clock_gpos.delta = gpos_get_delta(); } global time – execution time of system retur static hrtime_t gpos_gethrtime(struct rtl_clock *c) { hrtime_t t; pthread_spin_lock(&c->lock); t = gethrtime() + c->delta; pthread_spin_unlock(&c->lock); return t; } from start of computer

  40. static unsigned int _8254_irq(unsigned int irq, struct pt_regs *regs) { int flags; rtl_spin_lock_irqsave (&lock8254, flags); if (_i8254_clock.mode == RTL_CLOCK_MODE_PERIODIC) { if (test_and_set_bit(0, &_i8254_clock.arch.count_irqs)) { _i8254_clock.value += _i8254_clock.resolution;}} else {_i8254_clock.arch.istimerset = 0;} rtl_hard_enable_irq(0); rtl_spin_unlock_irqrestore (&lock8254, flags); _i8254_clock.handler(regs); if (rtl_rt_system_is_idle()) { rtl_allow_interrupts(); //rtl_hard_sti()} _8254_checklinuxirq(); rtl_stop_interrupts(); //rtl_hard_cli() return 0;} return

  41. void _8254_checklinuxirq (void) { rtl_irqstate_t flags; hrtime_t t; rtl_no_interrupts(flags); rtl_spin_lock(&lock_linuxtime); t = gethrtime(); if (t > _i8254_clock.arch.linux_time && !rtl_global_ispending_irq(0)) { _i8254_clock.arch.linux_time += LATCH_NS; rtl_global_pend_irq (0); do {rtl_save_jiffies = jiffies; } while (rtl_save_jiffies != jiffies); rtl_clock_gpos_update(); } rtl_spin_unlock(&lock_linuxtime); if (!_i8254_clock.arch.istimerset) { _i8254_clock.settimer (&_i8254_clock, MAX_LATCH_ONESHOT);} rtl_restore_interrupts(flags);} return

More Related