Timer Programming in Linux 경희대학교 컴퓨터공학과 조 진 성
주요내용 주요내용 타이머 일반 리눅스 시스템에서의 타이머 처리 PXA255기반 시스템에서의 타이머 처리 타이머 프로그래밍 예제
타이밍 측정의 중요성 운영체제를 관리하는 대부분의 커널 함수가 시간의 흐름에 따라 동작 대부분의 커널 함수가 이벤트 기반(event-driven)이 아닌 시간 기반(time-driven) 조절 방식 더 정확히 말하면 time-driven 방식은 시간을 이벤트로 하는 event-driven 방식이라고도 할 수 있음 타이밍 측정은 타이머 인터럽트를 통하여 해결하고 타이머 인터럽트 핸들러에서 모든 커널 동작시간을 조율
하드웨어 클럭 RTC(Real Time Clock) 리눅스에서의 RTC RTC는 시스템 시간을 저장하는 비휘발성 장치 시스템 전원이 꺼져 있을 때도 배터리로 시간유지 CPU를 비롯한 다른 모든 칩과 무관하게 동작하는 하드웨어 장치 부팅이 될 때 커널 초기화시 RTC에서 시간을 읽어 현재시간을 관리하는 xtime 구조체 변수에 저장 리눅스에서의 RTC 날짜와 시간을 알아내는 용도 /dev/rtc 장치파일을 통해 프로세스가 RTC를 프로그래밍 할 수 있도록 허용
PIT(Programmable Interrupt Timer) 시스템 타이머 타이밍 측정 장치(타이밍의 토대가 되는 하드웨어 장치) PIT를 이용하여 커널이 현재 날짜와 시간을 유지 커널은 PIT 장치를 프로그램하여 미리 정해진 고정 주파수로 인터럽트를 계속 발생(프로그램 가능한 하드웨어 장치) 틱(tick) 타이머 인터럽트 발생 간격 틱을 통해 커널이 현재 시각과 시스템 가동시간을 유지 시스템과 관련하여 모든 동작 시간을 조율 커널이 시간 간격을 추적하는 방법 tick의 값을 tick 변수에 저장 100Hz(10ms)마다 한번씩 IRQ0에 타이머 인터럽트 발생(예)
타이머 인터럽트의 진동수(Tick Rate) HZ 시스템 타이머의 진동수는 시스템의 부팅 시에 미리 정의 된 preprocessor인 HZ에 의해 정해짐 <arm/param.h>에 정의 Architecture에 따라 정의되어 있는 값이 다름 현재 대부분의 플랫폼에서 100HZ 로 정의됨 커널의 시간개념은 전적으로 시스템 타이머의 주기성에 달려있음 (적절한 시스템 타이머의 주기 값이 중요) // arm 아키텍처인 경우 #define HZ 100
아키텍처별 진동수 아키텍처 진동수(HZ) alpha arm cris h8300 i386 ia64 m68k m68knommu mips mips64 parisc ppc ppc64 s390 sh sparc sparc64 um v850 x86-64 1024 100 1000 32 or 1024 50,100, or 1000 100 or 1000 24, 100, or 122
HZ 값에 따른 장/단점 HZ 값이 높을 경우 장점(짧은 tick) HZ 값이 높을 경우 단점 타이머 인터럽트의 해상도가 높아지고 결과적으로 모든 시간 관련 이벤트가 높은 해상도를 가짐 시간 관련 이벤트의 정확도가 높아짐 poll(), select() 같이 타임아웃 값을 받아들일 수 있는 시스템 콜을 더 높은 정밀도로 실행 자원 사용 현황이나 시스템 가동시간과 같은 측정값이 더 높은 해상도로 기록 프로세스 선점이 보다 정확하게 처리 됨 HZ 값이 높을 경우 단점 진동수가 높으면 인터럽트가 자주 발생하여 프로세서가 타이머 인터럽트 핸들러를 실행하는데 더 많은 시간을 소비해야 하므로 많은 부하가 발생 다른 작업에 할당되는 프로세스 시간이 감소
jiffies 부팅될 때 커널은 jiffies값을 0으로 초기화 타이머 인터럽트 발생할 때마다 jiffies 값 증가 <linux/jiffies.h>에 정의 되어있음 extern unsigned long volatile jiffies Jiffies/HZ seconds = system uptime 1 jiffies = 1/HZ 초 초를 jiffies로 변환하는 공식 (second * HZ) jiffies를 초로 변환하는 공식 (jiffies / HZ)
Jiffies overflow 32비트 jiffies 64비트 jiffies 100 HZ일 경우 497일 후에 overflow 64비트를 사용하는 ALPHA는 5 억년 동안 사용가능 리눅스 커널 2.6 에서 변수 jiffies 자료형은 unsigned long 에서 u64로 변경 extern u64 jiffies_64; 몇 가지 특별한 링커 스크립트(linker scripts)를 사용하여 기존 jiffies 변수를 새로운 jiffies_64 변수에 overlay하여 호환성 유지
Jiffies wraparound 틱(tick) 카운트는 최대값에서 1만큼 증가되면 다시 0으로 되돌아가기 때문에 틱의 되돌림을 정확히 고려할 수 있는 4 가지 매크로를 사용 <linux/jiffies.h>에 정의되어 있음 #define time_after(unknown, known) ((long)(known) – (long)(unknown) < 0) #define time_before(unknown, known) ((long)(unknown) – (long)(known) < 0) #define time_after_eq(unknown, known) ((long)(unknown) – (long)(known) >= 0) #define time_before_eq(unknown, known) ((long)(known) – (long)(unknown) >= 0) // 0.5초 후에 타임아웃 unsigned long timeout = jiffies + HZ/2; If (timeout < jiffies) { /* not time out*/ } else { /*time out*/ } // 0.5초 후에 타임아웃 unsigned long timeout = jiffies + HZ/2; If ( time_after(jiffies, timeout) ) { /* not time out*/ } else { /*time out*/ } 잠재적인 overflow 문제 소지 잠재적인 overflow 문제 해결
타이머 인터럽트 핸들러 타이머 인터럽트 핸들러는 아키텍처 종속적인 부분과 독립적인 부분으로 나누어짐 아키텍처 종속적인 부분은 시스템 타이머의 인터럽트로 등록되며 타이머 인터럽트가 발생할 때 실행 정확한 작업내역은 아키텍처에 따라 다르지만 대부분의 핸들러는 다음과 같은 일 수행 xtime_lock 록 얻음 Jiffies_64에 대한 접근과 현재 시각 값인 xtime을 보호 필요에 따라 시스템 타이머를 승인하거나 초기화 주기적으로 현재 시각을 갱신하여 실시간 클럭에 저장 아키텍처 독립적인 타이머 루틴 do_timer()를 호출 xtime_lock 록 해제
타이머 인터럽트 핸들러(2) 아키텍처 독립적인 부분인 do_timer()의 역할 Jiffies_64 값을 1만큼 증가 값을 사용하기 전에 xtime_lock 록을 잠그기 때문에 32비트 아키텍처에서도 안전 현재 실행 중인 프로세스에 대해 시스템 및 유저 시간 소비량과 같은 자원 사용 현황을 갱신 만료된 동적 타이머가 있다면 다시 실행 scheduler_tick()을 실행 xtime에 저장된 현재 시각을 갱신 악명 높은 부하 평균(load average) 계산
타이머 인터럽트 핸들러(3) user_mode() 매크로는 프로세서 레지스터인 regs의 상태를 검사 /* 실제 함수는 다음과 같이 간단함 */ /* 대부분의 작업이 다른 함수에서 처리됨 */ void do_timer(struct pt_regs* regs) { jiffies_64++; update_process_times(user_mode(regs)); /* 현재시각을 갱신 */ update_times(); } user_mode() 매크로는 프로세서 레지스터인 regs의 상태를 검사 유저 공간에서 타이머 인터럽트가 발생했으면 1을, 커널 모드에서 인터럽트가 발생했으면 0을 return update_process_times()로 하여금 이전의 타이머 틱이 유저와 커널 모드 중 어디에서 일어났는지 알 수 있도록 하는 역할
현재 시각 xtime 변수를 사용하려면 xtime_lock 록을 얻어야 하는데 이것은 일반적인 스핀록이 아닌 seq 록으로 구현되어 있음 xtime을 갱신하는 방법 xtime을 읽기 위한 방법 write_seqlock(&xtime_lock); /* xtime 갱신 */ write_sequnlock(&xtime_lock); do { unsigned long lost; seq = read_seqbegin (&xtime_lock); usec = timer->get_offset(); lost = jiffies – wall_jiffies; if (lost) usec += lost * (1000000 / HZ); sec = xtime.tv_sec; usec += (xtime.tv_nsec / 1000); } while (read_seqretry(&xtime_lock, seq)); 이 loop는 writer가 없는 상태에서 reader가 데이터를 읽을 때까지 반복 만약 loop가 진행되는 동안 타이머 인터럽트가 발생하여 xtime이 갱신된다면, return 되는 시퀀스 번호가 유효하지 않게 되므로 loop는 계속 반복된다.
현재 시각(2) 현재시각(wall time)은 <kernel/time.c>에 정의 되어 있음 struct timespec xtime; timespec 자료구조의 정의 <linux/time.h> xtime.tv_sec 값에는 1970년 1월 1일(UTC)부터 지금까지의 시간이 초 단위로 저장 - 에포크(epoch : 신기원)라 부름 대부분의 유닉스 시스템에서의 현재 시각은 바로 이 에포크를 기준으로 한 상대적인 날짜를 의미 xtime.v_nsec 값은 마지막 초 이후에 경과된 나노 초 값 struct timespec { time_t tv_sec; // 초 long tv_nsec; // 나노초 };
현재 시각(3) 유저공간에서 현재 시각을 알아내기 위한 인터페이스는 gettimeofday() 이며 이는 sys_gettimeofday()로구현되어 있음 커널은 time() 시스템 콜도 제공 time()보다 gettimeofday()가 훨씬 나은 기능을 제공 C 라이브러리에는 ftime(), ctime() 과 같은 시간관련 함수도 제공 asmlinkage long sys_gettimeofday(struct timeval* tv, struct timezone* tz) { if (likely(tv != NULL)) { struct timeval ktv;` do_gettimeofday(&ktv); if (copy_to_user(tz, &sys_tz, sizeof(ktv))) return -EFAULT } if (unlikely(tz != NULL)) { if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) return 0; tv에 NULL이 아닌 값이 주어질 경우에는 아키텍처 종속적인 _do_gettimeofday()를 호출하는데 이 함수는 xtime 읽기 루프를 수행. tz에 NULL이 아닌 값이 주어지면 유저에게 시스템 타임 존(zone)을 return 만약 현재 시각을 복사하거나 time zone을 유저에게 return하는 과정에서 오류 발생시 –EFAULT를 return하고 성공하면 0을 return.
동적 타이머(커널 타이머) 장래의 특정 시간에 함수(타이머 핸들러)의 실행 처리시 사용 어떤 특정한 시간 만큼 작업을 지연시킬 수 있는 방법 타이머 사용법 초기값 설정 만료 시간 지정 만료 시 실행할 함수를 지정 타이머를 활성화 지정한 함수는 타이머가 만료될 때 실행 타이머는 만료가 되면 한번 그것을 호출하고 정지하게 됨 타이머는 주기적이 아님(동적 타이머가 필요한 이유) 동적 타이머는 끊임없이 생성되고 소실되며, 타이머의 수에는 제한이 없음 타이머는 커널에서 전체적으로 널리 사용 됨
타이머 사용 커널 타이머의 구조는 struct timer_list 자료형으로 표현 <linux/timer.h>에 정의 되어 있음 타이머의 타임아웃은 jiffies가 timer->expires보다 크거나 같을 때 timer->function이 동작할 필요가 있다는 점에서 “jiffy” 값 참조 타임아웃은 절대값 current value + the amount of the desired delay 현재시간에 상대적이지 않고 갱신될 필요가 없음 struct timer_list { struct list_head entry; // 타이머는 연결 리스트의 일부 unsigned long expires; // 만료시간, jiffy 단위 spinlock_t lock; // 이 타이머를 보호하는 록 void (*function)(unsigned long); // 타이머 핸들러 함수 unsignde long data; // 핸들러의 인수 struct tvec_t_base_s* base; // 내부적으로 사용되는 필드 }
타이머 사용(2) <linux/timer.h> 에 타이머 관리를 위한 인터페이스 포함 실제 구현은 kernel/timer.c에 정의 // 타이머의 내부 값을 초기화 void init_timer(struct timer_list *timer); // 활동중인 타이머의 전역 리스트에 타이머를 삽입 void add_timer(struct timer_list *timer); // 타이머 만료시간 변경 int mod_timer(struct timer_list *timer, unsigned long expires); // 타이머가 만료되기 전에 리스트에서 제거(비활성화) // 타이머 만료가 되면 자동으로 리스트에서 삭제 int del_timer(struct timer_list *timer); // 멀티 프로세싱 시스템의 경우 경쟁상태를 회피하기 위해 사용 int del_timer_sync(struct timer_list *timer);
타이머 경쟁 상태 타이머는 현재 실행중인 코드에 대해 비동기적으로 실행되므로 잠재적인 경쟁 상태 위험을 가짐 mod_timer() 대신 다음과 같이 처리하는 것은 피해야 하며 멀티프로세싱 시스템에서 안전하지 않음 SMP 경우 다른 CPU가 타이머 핸들러를 실행하고 있을 수 있음 SMP에서 안전한 타이머 함수 del_timer_sync() timerlist_lock spin lock 타이머 핸들러 함수에서 사용하는 공유 데이터 보호 필수 함수가 다른 코드에 대해 비동기적으로 실행되기 때문 … del_timer(&mytimer); x_Release_Resources(); … del_timer(my_timer); my_timer->expires = jiffies + new_delay add_timer(my_timer);
효율적인 타이머 관리 동적 타이머 리스트 타이머는 연결리스트의 형태로 저장 커널은 만료시간을 기준으로 타이머를 5개의 그룹으로 분할하고 만료시간이 가까워진 타이머일수록 하위 그룹으로 이동시켜 관리 struct timer_vec_root { int index; struct list_head vec[256]; } tv1; struct timer_vec { struct list_head vec[64]; } tv2, tv3, tv4, tv5; 동적 타이머 리스트 tvecs tv1 tv2 tv3 tv4 tv5 (0-255) (<214-1) (<220-1) (<226-1) (<232-1)
실행 지연 커널코드는 타이머나 Bottom-Half 메커니즘을 사용하지 않고서도 실행을 일정 시간 동안 지연할 수 있는 방법 필요 특히 드라이버의 경우 Lock 이 걸려있거나 인터럽트가 비활성화 될 동안에는 실행 지연을 하면 안됨 busy 루프 짧은 지연 <linux/delay.h> unsigned long delay = jiffies + 2*HZ; /* two seconds */ while (time_before(jiffies, delay)) ; cond_resched( ); void udelay(unsigned long usecs); /* microsecond delay */ void mdelay(unsigned long msecs); /* millisecond delay */
실행지연의 최적화 방법 Schedule_timeout() 지정된 시간이 경과할 때까지 태스크를 휴면 시킴 단순히 커널 타이머를 응용한 것 schedule_timeout(timeout) { struct timer_list timer; expire = timeout + jiffies; init_timer(&timer); timer.expires = expire; timer.data = (unsigned long)current; timer.function = process_timeout; add_timer(&timer); schedule(); del_timer_sync(&timer); timeout = expire - jiffies; return (timeout < 0 ? 0 : timeout); } …. timeout = 2*HZ; set_current_state(TAST_INTERRUPTIBLE); timeout = schedule_timeout(timeout); void process_timeout(unsigned long data){ wake_up_process((struct task_struct *)data); }
타이밍 측정과 관련한 시스템 콜 time(), ftime(), gettimeofday() 시스템 time() ftime() 1970년 1월 1일 자정부터 시작하여 경과한 시간을 초 단위로 반환 ftime() timeb 자료 구조 반환 gettimeofday() ftime()과 같은 정보를 timeval과 timezone 자료구조로 반환
PXA255에서의 타이머 처리(In uP) 모든 시간관련 활동은 PIT에 의해서 동작 time_init()으로 인터럽트 게이트 IRQ0을 설정 타이머 서비스 개략적인 흐름 timer_interrupt() -> do_timer_interrupt() -> do_timer() BHs 활성화 TIMER_BH TQUEUE_BH(tq_timer 태스크 큐가 비지 않았을 경우) ARM(PXA255 CPU)의 경우 setup_timer() -> pxa_timer_interrupt() -> do_timer() -> update_process_times() -> timer_bh() -> update_times() -> run_timer_list() static struct irq0 = { timer_interrupt, SA_INTERRUPT, 0, “time”, NULL, NULL }
setup_timer (void) extern inline void setup_timer (void) { gettimeoffset = pxa_gettimeoffset; set_rtc = pxa_set_rtc; xtime.tv_sec = pxa_get_rtc_time(); timer_irq.handler = pxa_timer_interrupt; OSMR0 = 0; /* set initial match at 0 */ OSSR = 0xf; /* clear status on all timers */ setup_arm_irq(IRQ_OST0, &timer_irq); OIER |= OIER_E0; /* enable match on timer 0 to cause interrupts */ OSCR = 0; /* initialize free-running timer, force first match */ }
주요소스 코드 내용(구조) void do_timer(struct pt_regs *regs) { (*(unsigned long *)&jiffies)++; #ifndef CONFIG_SMP /* SMP process accounting uses the local APIC timer */ update_process_times(user_mode(regs)); #endif mark_bh(TIMER_BH); if (TQ_ACTIVE(tq_timer)) mark_bh(TQUEUE_BH); } static void pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { long flags; int next_match; do_profile(regs); do { do_leds(); do_set_rtc(); save_flags_cli( flags ); do_timer(regs); OSSR = OSSR_M0; /* Clear match on timer 0 */ next_match = (OSMR0 += LATCH); restore_flags( flags ); } while( (signed long)(next_match - OSCR) <= 0 ); } void timer_bh(void) { update_times(); run_timer_list(); }
pxa_timer_interrupt() static void pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { long flags; int next_match; do_profile(regs); /* Loop until we get ahead of the free running timer. * This ensures an exact clock tick count and time acuracy. * IRQs are disabled inside the loop to ensure coherence between * lost_ticks (updated in do_timer()) and the match reg value, so we * can use do_gettimeofday() from interrupt handlers. */ do { do_leds(); do_set_rtc(); save_flags_cli( flags ); do_timer(regs); OSSR = OSSR_M0; /* Clear match on timer 0 */ next_match = (OSMR0 += LATCH); restore_flags( flags ); } while( (signed long)(next_match - OSCR) <= 0 ); }
do_timer(struct pt_regs *regs) void do_timer(struct pt_regs *regs) { (*(unsigned long *)&jiffies)++; #ifndef CONFIG_SMP /* SMP process accounting uses the local APIC timer */ update_process_times(user_mode(regs)); #endif mark_bh(TIMER_BH); if (TQ_ACTIVE(tq_timer)) mark_bh(TQUEUE_BH); }
update_process_times() void update_process_times(int user_tick) { struct task_struct *p = current; int cpu = smp_processor_id(), system = user_tick ^ 1; update_one_process(p, user_tick, system, cpu); if (p->pid) { if (--p->counter <= 0) { p->counter = 0; p->need_resched = 1; } if (p->nice > 0) kstat.per_cpu_nice[cpu] += user_tick; else kstat.per_cpu_user[cpu] += user_tick; kstat.per_cpu_system[cpu] += system; } else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
timer_bh(void) void timer_bh(void) { update_times(); run_timer_list(); }
void update_times(void) static inline void update_times(void) { unsigned long ticks; /* * update_times() is run from the raw timer_bh handler so we * just know that the irqs are locally enabled and so we don't * need to save/restore the flags of the local CPU here. -arca */ write_lock_irq(&xtime_lock); ticks = jiffies - wall_jiffies; if (ticks) { wall_jiffies += ticks; update_wall_time(ticks); } write_unlock_irq(&xtime_lock); calc_load(ticks);
update_wall_time(unsigned long ticks) static void update_wall_time(unsigned long ticks) { do { ticks--; update_wall_time_one_tick(); } while (ticks); if (xtime.tv_usec >= 1000000) { xtime.tv_usec -= 1000000; xtime.tv_sec++; second_overflow(); }
run_timer_list(void) (1) static inline void run_timer_list(void) { spin_lock_irq(&timerlist_lock); while ((long)(jiffies - timer_jiffies) >= 0) { LIST_HEAD(queued); struct list_head *head, *curr; if (!tv1.index) { int n = 1; do { cascade_timers(tvecs[n]); } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS); } run_timer_list_running = &queued;
run_timer_list(void) (2) repeat: head = tv1.vec + tv1.index; curr = head->next; if (curr != head) { struct timer_list *timer; void (*fn)(unsigned long); unsigned long data; timer = list_entry(curr, struct timer_list, list); fn = timer->function; data= timer->data; detach_timer(timer); timer->list.next = timer->list.prev = NULL; timer_enter(timer); spin_unlock_irq(&timerlist_lock); fn(data); spin_lock_irq(&timerlist_lock); timer_exit(); goto repeat; }
run_timer_list(void) (3) run_timer_list_running = NULL; ++timer_jiffies; tv1.index = (tv1.index + 1) & TVR_MASK; curr = queued.next; while (curr != &queued) { struct timer_list *timer; timer = list_entry(curr, struct timer_list, list); curr = curr->next; internal_add_timer(timer); } spin_unlock_irq(&timerlist_lock);
타이머 프로그램 예제(1) Timer 프로그램 소스 예제 (mytimer.c) /* Timer Handling */ #include <linux/kernel.h> #include <linux/module.h> #ifdef CONFIG_MODVERSIONS #define MODVERSIONS #include <linux/modversions.h> #endif #include <linux/sched.h> #include <linux/timer.h> /* type definitions */ struct mytimer_data { int m_times; int m_endtimes; };
타이머 프로그램 예제(2) Timer 등록 /* global variables */ static struct timer_list s_mytimer; static struct mytimer_data s_mytimer_data; /* function prototypes */ static void mytimer_proc(unsigned long ptr); static void add_mytimer(void); void del_mytimer(void); /* Module startup/cleanup */ int init_module(void){ printk("Loading Timer\n"); s_mytimer_data.m_times = 0; s_mytimer_data.m_endtimes = 5; add_mytimer(); return 0; } Timer 등록
타이머 프로그램 예제(3) Timer 제거 Timer 재등록 void cleanup_module(void) { del_mytimer(); printk("Unloading Timer\n"); } void mytimer_proc(unsigned long ptr) struct mytimer_data *pdata = (struct mytimer_data *) ptr; ++pdata->m_times; printk("Timer called %d/%d\n", pdata->m_times, pdata->m_endtimes); if (pdata->m_times < pdata->m_endtimes) add_mytimer(); Timer 제거 Timer 재등록
타이머 프로그램 예제(4) Timer 생성 Timer 추가 void add_mytimer(void) { init_timer(&s_mytimer); s_mytimer.function = mytimer_proc; s_mytimer.data = (unsigned long) &s_mytimer_data; s_mytimer.expires = jiffies + HZ * 5; /* 5 seconds */ add_timer(&s_mytimer); } void del_mytimer(void) del_timer(&s_mytimer); Timer 생성 Timer 추가
타이머 모듈 실행 예 커널 타이머 인터페이스를 이용
PCFR (Power Manager General Configuration Register) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0000 0000 0000 Reserved FP FS OPDE [0x 40F0 001C] OPDE [0] 3.6864MHz oscillator power-down enable. FP [1] Float PCMCIA controls during Sleep Mode. FS [2] Float Static Chip Selects during Sleep Mode. Address : 0x40F0001C Intel Developer’s Manual Page 3-24. OPDE 0 – Sleep Mode 동안에 oscillator 가 정지되지 않는다. 1 – Sleep Mode 동안에 3.6864Mhz oscillator 가 정지된다. FP 0 – Sleep Mode 동안에 PCMCIA 핀들은 동작되지 않는다. 1 – Sleep Mode 동안에 PCMCIA 신호(nPOE,nPWE,nPIOW등)가 동작된다. FS 0 – Sleep Mode 동안에 Static Chip Select 핀들은 동작되지 않는다. 1 – Sleep Mode 동안에 Static Chip Select 신호(CS5..1, nWE,nOE등)가 동작된다.
PXA255 Processor Clock 1 0 1 CCCR PXbus RETAINS POWER IN SLEEP RTC PWR_MGR 3.6864 PWM SSP GPIO OST CPU CORE MEM Controller LCD USB 47.923 FICP 12C 31.949 MMC 19.169 UARTs 14.746 AC97 12.288 12S 5.672 32.768 KHz OSC MHz /1 /112 100-400 PLL* 147.46 PLL 95.846 DMA / Bridge /M /2 /4 /N RETAINS POWER IN SLEEP PXbus L 1 0 1 OSCC, OON CCLKCFG cp14 c6.1 : turbo CCCR CCCR - Core Clock Configuration Register ( Intel Developer’s Manual Page 3-35 ) OSCC - Oscillator Configuration Register ( Intel Developer’s Manual Page 3-38 ) CCLKCFG - Core Clock Configuration Register ( Intel Developer’s Manual Page 3-39 )
Core PLL Output Frequencies M Turbo Mode Frequency (MHz) for Values “N” and Core Clock Configuration Register (CCCR[15:0]) Programming for Values of “N”: PXbus Frequency MEM, LCD Frequency (MHz) SDRAM max Freq 1.00 (Run) 1.50 2.00 3.00 99.5 @1.0 V ㅡ 199.1 @1.0 V 298.6 @1.1 v 50 99.5 99.5 27 1 36 1 132.7 @1.0 V ㅡ ㅡ ㅡ 66 132.7 66 199.1 @1.0 V 2 298.6 @1.1 v 398.1 @1.3 V ㅡ 99.5 99.5 99.5 27 265.4 @1.1 V ㅡ 2 ㅡ ㅡ 132.7 132.7 66 36 331.8 @1.3 V ㅡ ㅡ 45 ㅡ 2 165.9 165.9 83 27 4 398.1 @1.3 V ㅡ ㅡ 196 99.5 99.5
Core Clock Configuration Register(CCCR) (1) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0001 0010 0001 Reserved N M L N[9:7] Run Mode Frequency 에서 Turbo Mode Frequency 로 변환하기 위해서 곱하는 수 Turbo Mode Freq. = Run Mode Frequency * N 000 , 001 , 101 , 111 – Reserved 001(Multiplier) = 1 011(Multiplier) = 1.5 100 (Multiplier) = 2 110 (Multiplier) = 3 ☞ Hardware Reset , Watchdog Reset 이 되면 010 값이 default가 된다. ☞ Turbo Mode Freq(398.1MHz) = Run Mode Freq(199.1MHz) * N(2) Address : 0x4130/0004 CCCR - Intel PXA255 Developer’s Manual Page 3-35
Core Clock Configuration Register(CCCR) (2) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0001 0010 0001 Reserved N M L M [6:5] Memory Frequency에서 Run Mode Frequency로 변환하기 위해서 곱하는 수 Memory Freq = Crystal Frequency * L 00 , 11 – Reserved 01(Multiplier) = 1(Multiplier가 1이면 Run Mode Freq와 Memory Frequency가 같다.) 10(Multiplier) = 2(Multiplier가 2이면 Run Mode Freq가 Memory Frequency의 2배가 된다.) ☞ Hardware Reset , Watchdog Reset 이 되면 10 값이 default가 된다. ☞ Memory Freq(99.5MHz) = Crystal Frequency(3.6864MHz) * L(27) CCCR - Intel PXA255 Developer’s Manual Page 3-35
Core Clock Configuration Register(CCCR) (3) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0001 0010 0001 Reserved N M L L[4:0] Crystal Frequency에서 Memory Frequency로 변환하기 위해서 곱하는 수 (3.6864MHz Crystal을 사용) 00000 , 00110 to 11111 – Reserved 00001(Multiplier) = 27 (Memory Freq는 99.53MHz가 된다.) 00010(Multiplier) = 32 (Memory Freq는 117.96MHz가 된다.) 00011(Multiplier) = 36 (Memory Freq는 132.71MHz가 된다.) 00100(Multiplier) = 40 (Memory Freq는 147.46MHz가 된다.) 00101(Multiplier) = 45 (Memory Freq는 165.89MHz가 된다.) ☞ Hardware Reset , Watchdog Reset 이 되면 00001 값이 default가 된다. CCCR - Intel PXA255 Developer’s Manual Page 3-35
Clock Enable Register(CKEN)(1) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0001 0111 1101 1110 1111 Reserved CKEN13 CKEN11 CKEN8 CKEN6 Reserved CKEN2 CKEN0 Reserved CKEN16 CKEN14 CKEN12 Reserved CKEN7 CKEN5 CKEN3 CKEN1 CKEN16[16] LCD Unit Clock Enable 0 = Clock이 LCD Unit으로 전달되지 않는다. 1 = Clock이 LCD Unit으로 전달된다. Hardware Reset , Watchdog Reset시 1로 set 된다. CKEN14[14] I2C Unit Clock Enable 0 = Clock이 I2C Unit으로 전달되지 않는다. 1 = Clock이 I2C Unit으로 전달된다. CKEN13[13] FICP Unit Clock Enable 0 = Clock이 FICP Unit으로 전달되지 않는다. 1 = Clock이 FICP Unit으로 전달된다. CKEN12[12] MMU Unit Clock Enable 0 = Clock이 MMU Unit으로 전달되지 않는다. 1 = Clock이 MMU Unit으로 전달된다. Address : 0x4130/0004
Clock Enable Register(CKEN)(2) CKEN11[11] USB Unit Clock Enable 0 = Clock이 USB Unit으로 전달되지 않는다. 1 = Clock이 USB Unit으로 전달된다. Hardware Reset , Watchdog Reset시 1로 set 된다. GP7 Alternate Function 1 에서 48MHz 의 Clock이 출력 되려면 1로 set CKEN8[8] I2S Unit Clock Enable 0 = Clock이 I2S Unit으로 전달되지 않는다. 1 = Clock이 I2S Unit으로 전달된다. CKEN7[7] BTUART Unit Clock Enable 0 = Clock이 BTUART Unit으로 전달되지 않는다. 1 = Clock이 BTUART Unit으로 전달된다. CKEN6[6] FFUART Unit Clock Enable 0 = Clock이 FFUART Unit으로 전달되지 않는다. 1 = Clock이 FFUART Unit으로 전달된다. CKEN5[5] STUART Unit Clock Enable 0 = Clock이 STUART Unit으로 전달되지 않는다. 1 = Clock이 STUART Unit으로 전달된다.
Clock Enable Register(CKEN)(3) CKEN3<3> SSP Unit Clock Enable 0 = Clock이 SSP Unit으로 전달되지 않는다. 1 = Clock이 SSP Unit으로 전달된다. Hardware Reset , Watchdog Reset시 1로 set 된다. GP7 Alternate Function 1 에서 48MHz 의 Clock이 출력 되려면 1로 set CKEN2<2> AC97 Unit Clock Enable 0 = Clock이 AC97 Unit으로 전달되지 않는다. 1 = Clock이 AC97 Unit으로 전달된다. CKEN1<1> PWM1 Unit Clock Enable 0 = Clock이 PWM1 Unit으로 전달되지 않는다. 1 = Clock이 PWM1 Unit으로 전달된다. CKEN0<0> PWM0 Unit Clock Enable 0 = Clock이 PWM0 Unit으로 전달되지 않는다. 1 = Clock이 PWM0 Unit으로 전달된다.
Oscillator Configuration Register(OSCC) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0000 0000 0000 Reserved OON OOK OSCC는 32.768MHz Oscillator의 Configuration을 Control하는 Register이다. Oscillator가 동작되었을 때 안정화가 되는 데는 10초가 걸린다. Oscillator가 안정화 되면 OOK bit가 1로 set된다. OON[1] Write-once only bit 0 = 32.768MHz Oscillator 사용 불가능 이때 RTC 와 Power Manager 의 Clock은3.6863MHz Oscillator의 값이 공급된다.(112로 나누어진 값) ☞ Page 3-3 의 Figure 3-1 Clocks Manager Block Diagram 참고 1 = 32.768KHz Oscillator 사용가능. 이 비트는 Hardware Reset으로만 Clear가 된다. OOK[0] Read-only bit 0 = OON bit가 0 이거나 Oscillator가 안정화 되지 않았을 경우 1 = OON bit가 1 로 set 되고 Oscillator가 안정화 되었을 경우 RTC 와 Power Manager의 Clock은 32.768KHz Oscillator의 Clock을 사용 Address : 0x4130/0008
Timers and Watchdog One free running timer Runs from 3.68 MHz clock Three general purpose timer registers User programs a specific value in the registers When timer reaches the preprogrammed value, interrupts may be generated One timer register general purpose or watchdog Operation determined by state of bit 0 in OWER Value loaded at reset If enabled, cannot be disabled until reset occurs OS should occasionally check timer and add to this value if necessary 리눅스에서는 이 모드를 통해 인터럽트 핸들러 구현 fiq안씀 => 수퍼바이저모드로 바꿈..
RTC Alarm Register(RTAR) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0000 0000 0000 RTMV RTAR과 RCNR의 값이 같아지고 RTSR[ALE] bit가 1로 set 되어있으면 RTSR[AL] bit가 1로 set 된다. RTMV<31:0> RTC Target Match Value 이 값은 RTC counter Register(RCNR)의 값과 비교를 한다. Physical Address 0x40900004
RTC Counter Register(RCNR) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0000 0000 0000 RCV RCNR Register의 값은 1Hz(Default)의 rising edge에서 1씩 증가한다. RCV<31:0> RTC Count Value RTC Counter Register의 값이다. ☞ Ex) RTAR = RCNR + 1; 위와 같이 하면 RTC Interrupt는 1초마다 발생한다. Physical Address 0x40900000
RTC Status Register(RTSR) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???? ???? ???? ???? ???? ???? ???? 0000 ALE Reserved HZE HZ AL <3> HZE : HZ 인터럽트 가능 0 = HZ 인터럽트 불가능 1 = HZ 인터럽트 가능 <2> ALE : RTC alarm 인터럽트 가능 0 = RTC alarm 인터럽트 불가능 1 = RTC alarm 인터럽트 가능 <1> HZ : HZ rising-edge 발견 유/무 0 = rising-edge가 발견되지 않았다. 1 = rising-edge가 발견되었다. 그리고 현재 HZE bit가 1이다. <0> AL : RTC alarm 발견 유/무 0 = RTC alarm이 발견되지 않았다. 1 = RTC alarm이 발견되었다.(RCNR 과 RTAR이 같아졌을 때 1로set) 그리고 현재 ALE bit가 1이다. Physical Address 0x40900008 ☞ RTC alarm 인터럽트를 발생시키려면 ALE bit를 1로 set 시켜주어야 한다.
RTC Status Register(RTSR) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0??? ??00 0000 0000 0111 1111 1111 1111 LCK Reserved DEL CK_DIV <31> LCK 0 = RTTR value 값의 변경 가능 1 = RTTR value 값의 변경 불가능 <25:16> DEL Trim delete count This value represents the number of 32KHz clocks to delete when clock trimming begins <15:0> CK_DIV Clock divider count 32KHz 클럭을 CK_DIV값으로 나눈다. Default로 0x7fff 이렇게 되면 RTC 클럭이 1Hz 가 된다. 32KHz의 클럭을 사용하려면 이 부분을 모두 0으로 바꿔줘야 한다. ☞ Ex1) RTAR = RTCR + 1; CK_DIV 값을 설정하지 않고 위와 같이 설정을 하게 되면 1초마다 RTC Interrupt 가 발생하게 된다. Physical Address 0x4090000C
OS Timer Match Register 0-3 (OSMR0,OSMR1,OSMR2,OSMR3) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0000 0000 0000 OSMV OSMV<31:0> : OS Timer Match Value 이 Register의 값은 OSCR Register의 값과 비교를 한다. 이때 Register의 값이 OSCR Register의 값과 같으면 Interrupt를 발생시킨다. (4개의 OSMR Register 중 비교하고자 하는 OSMR Register에 해당하는 OIER Bit가 1로 Set 되어있어야 한다.) ☞ Ex) OSMR1 = OSCR + 1000 (OSMR1 = 0 OSCR = 0 이라고 가정) 위와 같이 하면 OSMR1 = 1000 이 되고 OSCR 의 값은 0 이다. 이때 OSCR 이 counting을 하면서 OSMR1 Register의 값과 비교를 한다. OSCR의 값이 OSMR1의 값과 같아지면 Interrupt가 발생한다. OSMR IRQ No. OSMR0 : 26 OSMR1 : 27 OSMR2 : 28 OSMR3 : 29 (ICPR Register 참고) Physical Address : 0x40A00000, 0x40A00004, 0x40A00008, 0x40A0000C
OS Timer Counter Register(OSCR) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 0000 0000 0000 0000 0000 0000 0000 0000 OSCV OS Timer Counter Register - 32bit counter 로서 3.6863MHz의 rising edge시에 1씩 OSCV<31:0> : OS Timer Counter Value 이 Register의 값은 OSMR Register의 값과 비교 Physical Address 0x40A00010
OS Timer Interrupt Enable Register(OIER) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???? ???? ???? ???? ???? ???? ???? 0000 Reserved E3 E2 E1 E0 이 Register는 OSMR[0-3] 의 값과 OSCR의 값이 같아졌을 때 해당 OSMR에 대한 Interrupt를 발생시킬지 여부를 결정하는 Register이다. E[3-0] : 0 = OSMR[3-0] 의 값이 OSCR의 값과 같아도 Interrupt를 발생시키지 않는다. 1 = OSMR[3-0] 의 값이 OSCR의 값과 같으면 Interrupt를 발생시킨다. Physical Address 0x40A0001C
OS Timer Status Register(OSSR) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???? ???? ???? ???? ???? ???? ???? 0000 Reserved M3 M2 M1 M0 4개의 OSMR Register중 하나와 OSCR과 match가 됐는지 여부를 알려줌 OSMR Register중 하나와 그에 상응하는 OIER bit 가 1로 set 되어있고 OSMR과 OSCR이 match가 되면 OSSR[3-0]의 bit가 1로 set 된다. M[3-0] : 0 = OSMR[3-0]과 OSCR의 값이 match가 되지 않았다. 1 = OSMR[3-0]과 OSCR의 값이 match가 되었다. Physical Address 0x40A00014