Timers

279 Views

January 20, 19

スライド概要

Brief introduction to PC timers: PIT, RTC, HPET, ACPI PM Timer, Local APIC Timer, TSC. This slides also describes how to use ACPI PM Timer and Local APIC Timer.

profile-image

サイボウズ・ラボ株式会社で教育向けのOSやCPU、コンパイラなどの研究開発をしています。

シェア

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

タイマー @uchan_nos 第14回 自作OSもくもく会 2019/01/20

2.

時間計測の必要性 • 時計表示 • タスク切り替え • sleep関数の実装 • 時間計測はOSを構成する基礎技術の1つ

3.

パソコン用タイマーの種類 • PIT • RTC • HPET • ACPI PM Timer • Local APIC Timer • TSC

4.

PIT • PIT: Programmable Interval Timer • Programmable = 周期などを設定できる • Interval = 間隔 • 「30日でできる!OS自作入門」でおなじみ • 一定周期ごとに割り込みを発生させる • ビープ音を鳴らす • レガシーデバイス

5.

RTC • RTC: Real Time Clock • 日時(年月日,時分秒)を扱う時計 • バッテリバックアップされている唯一のタイマー • レガシーデバイス

6.

HPET • HPET: High Precision Event Timer • High Precision = 高精度 • Event = ?(特に意味はないと思う) • 分解能が100nsより優れていることが保証されている • = 周波数が10MHz以上 • 割り込みを発生させられる • レガシーデバイス(後述)

7.

ACPI PM Timer • PM: Power Management • Power Management = 電源管理 • ACPI規格で定められているタイマー • もともとはスリープ状態の時間を測るため,らしい • でも,汎用的に使える

8.

Local APIC Timer • Local APIC規格で定められたタイマー • Local = CPUコア内 • APIC = Advanced Programmable Interrupt Controller • 各CPUコア内に存在する → 使用時オーバーヘッドが小さい • 割り込みを発生させられる

9.

TSC • TSC: Time Stamp Counter • 各CPUコア内に存在する • CPUの動作クロックを数えるカウンタ • CPUが省電力状態になっても周波数が変わらないもの → Invariant TSC

10.

タイマーまとめ 名前 周波数 割り込み レガシー PIT 1.193182MHz 可 レガシー RTC 32.768KHz 可 レガシー HPET 機種依存,レジスタから取得可 可 レガシー ACPI PM Timer 3.579545MHz 不可 Local APIC Timer 機種依存,取得不可 可 TSC 機種依存,取得不可 不可 Invariant TSC 機種依存,レジスタから取得可 不可

11.

レガシーデバイス • Legacy = 遺産 • 現代におけるレガシーデバイスの定義(筆者独自の定義) • 存在をシステマチックに確かめる術がないもの • レジスタにアクセスしてみないとデバイスの有無が分からない • 電源管理できないもの • 省電力モードでも常に動き続ける • この定義で行けばHPETはレガシーではないが…

12.

HPETはレガシーデバイスか • “N-series Intel Pentium Processors and Intel Celeron Processors Datasheet - Volume 1 of 3”, Feb. 2016 • によれば,NシリーズのPentiumおよびCeleronでは HPETはPCU-iLB内に存在 • PCU = Platform Controller Unit • iLB = The Intel Legacy Block • iLBは8259 PIC, I/O-APIC, 8254 PIT, HPET, RTCを含む • → HPETはレガシーデバイスの仲間!

13.

XSDTを確認してみる • XSDT = ACPIのルートテーブル • NシリーズCerelon搭載パソコン • CPU: Celeron N3050 • PC名: Shuttle XS36V5 • 確かにHPETがXSDTに無い! • ちなみにQEMUでは FACP, APIC, HPET, BGRT が見える

14.

筆者おすすめの組み合わせ • 初期計時:ACPI PM Timer • 普段使い:Local APIC Timer • ACPI PM Timerは規格ではオプショナルだが事実上標準ぽい • https://japan.zdnet.com/article/20365868/ • 「ACPI対応のマザーボードであれば、このタイマーが提供される。」 • Local APIC TimerはIntel SDMに載っている標準デバイス

15.

初期計時のためのタイマー選定 1/2 • 最初から動作周波数が分かっているタイマーを使って Local APIC Timerの周波数を測りたい • 動作周波数が既知のタイマー • • • • • PIT RTC HPET ACPI PM Timer Invariant TSC • なるべく分解能が優れ,レガシーでないタイマーを選びたい

16.

初期計時のためのタイマー選定 2/2 • レガシーでない,周波数が既知のタイマー • ACPI PM Timer • Invariant TSC • Invariant TSCはレジスタから周波数を取得できるが, BIOSの設定によってはズレることがある • Bus Clockを変更するとズレる • CPUによってはTSCがInvariantではない • ということで,ACPI PM Timerを選ぶのをおすすめする

17.

普段使いのためのタイマー選定 • 割り込みに対応したタイマーの中で, 動作オーバーヘッドが小さいものを使いたい • 割り込みに対応したタイマー • • • • PIT RTC HPET Local APIC Timer • この中でレガシーでないのはLocal APIC Timerのみ • 動作オーバーヘッドが最も小さいのもLocal APIC Timer

18.

タイマーの接続図 • Local APIC Timer, TSC • CPUコア内に実装されている CPUコア TSC Local APIC Timer • PIT, RTC, HPET, ACPI PM Timer • CPU外に実装されている • アーキテクチャ依存の場所 • Intel 5シリーズ以降はPCH内 バス Platform Controller Hub PIT RTC HPET ACPI PM Timer

19.

ACPI PM Timerの使い方 UEFIで起動してからタイマーで時間を測るまでの道のり

20.

大まかな流れ • ACPIのXSDTを得る • XSDTからFADTを探す • FADTのPM_TMR_BLKレジスタを読む • TMR_VALレジスタを読む

21.

XSDTを得る • ACPIのXSDTを取得する • UEFI環境での方法は大神さんの同人誌が参考になる • 『フルスクラッチで作る!x86_64自作OS パート2』 http://yuma.ohgami.jp/x86_64-Jisaku-OS-2/01_acpi.html • • • • • ざっくり言うと EFI_SYSTEM_TABLE → EFI_CONFIGURATION_TABLE → RSDP → XSDT

22.

XSDTからFADTを探す • FADTのシグネチャは”FACP” • テーブル名とシグネチャが異なるので注意! • XSDTは64ビットアドレスの配列 • 順にアドレスが指す先を読んでFADTを探す

23.

Sig = “FACP”である ディスクリプタを探す ACPI Specification, Version 6.2 Errata Aより

24.

FADTのPM_TMR_BLKレジスタを読む • FADTの位置が分かればPM_TMR_BLKレジスタを読める • PM_TMR_BLKはFADT先頭から76バイト目

25.

ACPI Specification, Version 6.2 Errata Aより

26.

TMR_VALレジスタを読む • PM_TMR_BLKはACPI PM Timer関連の レジスタブロックを指すアドレス • PM_TMR_BLKの説明に“System port address”とある • メモリアドレスではなくI/Oポートのアドレスである • movではなく,in命令で読む • uint32_t pm_tmr_blk = *(uint32_t*)(fadt_addr + 76); • uint32_t tmr_val = io_in32(pm_tmr_blk);

27.

TMR_VALレジスタ • 3.579545MHzでカウントアップし続ける • 24ビットで1周 = 約4.69秒で1周 • 時間を測るにはこんな風にする(1秒測る例) • uint32_t initial_val = io_in32(pm_tmr_blk); • uint32_t target_val = initial_val + 3579545; • While (io_in32(pm_tmr_blk) < target_val); target_valが 24ビット以上に ならないように 注意

28.

Local APIC Timerの使い方

29.

LVT Timerレジスタを設定 • LVT Timerレジスタ = FEE0 0320H • ビット18:17でモードを設定 • 00b: One-shotモード • 01b: Periodicモード • 10b: TSC-deadlineモード • 他のビットは割り込み関係の設定. • ビット16を1にしておくと割り込みが発生しない.

30.

Initial Countレジスタを設定 • Initial Countレジスタ = FEE0 0380H • 0より大きな値を書くとタイマーの動作開始 • One-shotモード • 値がCurrent Countレジスタにコピーされ, Current Countレジスタがカウントダウンされる • Periodicモード • Current Countレジスタが0になると割り込み発生後, Initial Countレジスタから値が再設定される • 詳しくはIntel SDM Vol.3, “10.5.4 APIC Timer”

31.

Periodicモード Current Countレジスタの値 N 0 時刻 Initial Countレジスタ に値Nを書き込む 割り込み 割り込み

32.

補足: レジスタアドレスの謎 • Local APIC Timer関連のレジスタはアドレス固定 • Initial Countレジスタ = FEE0 0380H • LVT Timerレジスタ = FEE0 0320H • しかし,Local APIC Timerは各コア毎に存在するはず • 同じアドレスなのに,なぜ区別できるのだろうか

33.

CPUコア1 EBX 0003 0000 EAX FEE0 0320 mov [eax], ebx メモリバス アドレスデコーダ LVT Timer Initial Count FEE0 0000H - FEE0 0400H DDR SDRAM CPUコアN Local APICレジスタへのアクセスは CPUコア内部で完結する →同じアドレスでも競合しない