Použití OS Linux pro měřicí aplikace

Použití OS Linux pro měřicí aplikace
ČVUT - Fakulta Elektrotechnická
Bakalářská práce
Použití OS Linux pro měřicí aplikace
Jakub Kocourek
2009
Zde je prostor pro zadání.
1
Čestné prohlášení
Prohlašuji, že jsem svou bakalářskou práci vypracoval samostatně a použil jsem pouze podklady (literaturu, projekty, SW atd.) uvedené v přiloženém seznamu.
Nemám závažný důvod proti užití tohoto školního díla ve smyslu § 60 Zákona č.121/2000 Sb., o právu
autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon).
Datum:
Podpis:
2
Děkuji Doc. Roztočilovi za pomoc při tvorbě této práce a cenné připomínky. Special thanks to Ian
Abbotti, the author of Amplicon driver, for comprehensive information about DIO card and its driver.
3
Použití OS Linux pro měřicí aplikace
Tato práce sa zabývá generováním přesného času v operačním systému Linux, s použitím běžného počítače PC a univerzální DAQ měřicí karty. Popsána a vyzkoušena jsou rozšíření RT_PATCH a Xenomai,
aplikovaná na standardní jádro GNU/Linux, včetně měření latence systému a přerušení.
Building Measurement Applications under Linux
This thesis is focused on generation of time in Linux operating system, using standard personal computer
and DAQ card. There are theoretical information abou RT_PATCH and Xenomai extensions in the text.
Both were tested for time generation and reviewed. System and interrupt latency were tested too.
4
Obsah
1 Real-Time
7
1.1
Parametry RT
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
1.2
Real-time v měřicích aplikacích . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2 Linux
9
2.1
Rozšíření pro RT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
2.2
RT patch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
2.3
RTAI (Xenomai) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
2.4
Comedi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12
3 Časovače v PC
14
3.1
PIT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
3.2
RTC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
3.3
APIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
3.4
TSC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
3.5
HPET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
3.6
Problémy časování . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
4 Měřicí karty
16
4.1
NI PCI 6221
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
4.2
Amplicon PCI 236 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
5 Generování přesného času
18
5.1
Latence systému . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
5.2
Sekundový generátor s HPET časovačem a RT_PATCH . . . . . . . . . . . . . . . . . . .
18
5.3
Sekundový generátor s TSC časovačem a Xenomai . . . . . . . . . . . . . . . . . . . . . .
19
5.4
Zpětnovazební sekundový generátor
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
5.5
Latence přerušení . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
6 Výsledky
21
6.1
Postup měření
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
6.2
Výpočty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
7 Zhodnocení
25
8 Použitá literatura
26
5
OBSAH
A Zdrojový kód - test latence systému
27
B Zdrojový kód - sekundový generátor s HPET časovačem
29
C Zdrojový kód - sekundový generátor s TSC časovačem
31
D Zdrojový kód - latence přerušení
34
6
KAPITOLA 1
Real-Time
V literatuře lze najít nejrůznější definice Real-time (dále jen RT) systémů, často ale ne příliš přesné.
Obecně lze za RT označit libovolný systém, který splňuje námi zadané časové podmínky, ať už chceme
asynchronně spouštět různé úlohy s určitou garantovanou prodlevou nebo žádáme spouštění periodické.
1.1
Parametry RT
V obou případech definujeme tzv. Release time, neboli minimální prodlevu, se kterou se úloha spustí
(čas na přípravu úlohy) a Deadline, neboli maximální dobu dokončení. U periodických úloh navíc žádáme
dodržení periody běhu.
Systémy se ještě často dělí na Soft a Hard Real-time. Platí, že pokud žádáme přesné dodržení mezí
Release time a Deadline, jedná se o Hard Real-time. Když chceme jen udržení určitých kvalitativních
vlastností (např. maximální počet nedodržení Deadline za časový úsek), pak užíváme označení Soft Realtime. Takové chování si ale můžeme dovolit jen v málo RT aplikacích.
Mezi další charakteristické parametry Real-time systému (např. z pohledu odezvy přerušovacího systému) patří latence a jitter. Latence obecně značí dobu mezi zadáním požadavku a jeho provedením.
V našem případě se může jednat např. o čas mezi příchodem přerušovacího signálu a jeho obsluhou.
Rozkmit latence od střední hodnoty se nazývá jitter.
Naprostá většina průmyslových aplikací vyžaduje Hard Real-time. Jako příklad uveďme embeded
systémy pro automatické řízení vlaků [9]. RT systém musí na asynchronní události (změna semaforu)
reagovat v garantovaném čase, aby mohl bezpečně provést odpovídající akci. Takovýto systém nepřipouští
pozdní zpracování.
Uvedeným příkladem se dostáváme k problému garance latence. Je relativně snadné garantovat odezvu jednoduchých specializovaných systémů, např. snímání a regulace otáček motoru jednoúčelovým uP.
V takovém případě známe počet běžících úloh a dobu potřebnou k jejich vykonání. Zde je tedy možné předem sestavit tabulku plánovače (statické plánování) a případné sporadické úlohy umisťovat do časových
mezer.
Ve chvíli, kdy je třeba reagovat na asynchronní události a dodržet jejich striktní Deadline, začínají
problémy. Je nutné rozhodnout o prioritě úloh, zajistit preemptivitu a plánovat spouštění tak, aby byly
dodrženy požadované parametry. Zde nastupují algoritmy dynamického rozvrhování. Např. algoritmus
EDF (Earliest Deadline First) umožňuje přeskládávat úlohy podle jejich termínu Deadline.
7
KAPITOLA 1. REAL-TIME
1.2
Real-time v měřicích aplikacích
K nasazení v měřicí a regulační technice je třeba vybrat správný systém. Pro safety critical aplikace,
jako je řízení velkých strojů, nemocničních zařízení, atd., se obvykle preferuje nasazení embeded systémů,
které jsou na RT připraveny a případně se spojují do většího celku, který řídí klasické PC s RT systémem.
V těchto případech se ještě užívají speciální zařízení, jako např. Watchdog timer, pro ošetření případného
selhání, redundantní čidla i celé systémy, atd.
Pro běžné měření a regulaci pomocí DAQ karet postačují PC s RT operačním systémem. Je však
třeba počítat s omezeními danými operačním systémem a hardwarem PC. Systémová volání a obsluhy
přerušení mohou předbíhat jiné úlohy, maximální rozlišení času je dáno systémovým časovačem (obvykle
HPET nebo TSC). Funkce jako SMI (viz dále) mohou poškodit časování. SMI, DMA a další hardwarové
záležitosti mohou způsobit i nedodržení nejhorších stanovených hodnot Release time a Deadline.
Je však pravdou, že i přes uváděné problémy s nasazením PC v safety critical aplikacích, již existuje
např. systém PikeOS (výrobce Sysgo), postavený na takovémto hardwaru s využitím OS Linux, který
má certifikace DO-178B, IEC 61508 a EN 50128. Jedná se v podstatě o upravený GNU/Linux, s mikro
kernelem a výborným vrstvením systému. PikeOS také nabízí široké spektrum podporovaných vývojových
API a platforem, vzdálené ladění a monitoring po síti LAN, update systému bez nutnosti výpadku, atd.
Dále viz. [2].
8
KAPITOLA 2
Linux
GNU/Linux je primárně určen k serverovým a desktopovým aplikacím. Nicméně díky podpoře mhoha
různých platforem vznikly i nejrůznější úpravy jádra pro speciální nasazení. Knihovny Comedi, které
používám pro řízení DAQ karty, jsou kompatibilní s RT patchem Ingo Molnára a RTAI.
2.1
Rozšíření pro RT
Ve standardním jádru se nachází mnoho kritických sekcí, jejichž souběhu (při přerušení nebo preempci)
brání spin locky – zámky paměti s aktivním čekáním. Také vykonání přerušení může úlohy blokovat
na velmi dlouho. Patch mění povahu obslužných rutin přerušení na běžné úlohy, kterým je možné přiřadit příslušnou prioritu (nižší, než má RT úloha). Prodlevy při souběhu vláken jsou místo spin locku
řešeny mutexy, které umožňují uzamknout jednotlivé nezávislé kritické sekce různým úlohám (CPU se
nevyhradí pro jednu úlohu, která vyvolala spin lock pro libovolnou sekci). Patch mění část jádra, ale pro
programátora uživatelských aplikací je transparentní .
RTAI sází na jinou koncepci. Místo úpravy jádra pouze přidává nový modul1 , který funguje jako
vrstva HAL mezi operačním systémem a skutečným hardwarem, která plánuje přerušení a programuje
systémový časovač. RTAI nabízí dva módy činnosti. Primární mód garantuje zpoždění aplikace do 50 us
(nejhorší případ) a k plánování využívá nepřerušitelný co-scheduler. Pokud se mají vykonávat standardní
systémová volání, úloha přejde do sekundárního módu, ve kterém již nemá takovou prioritu. Tento mód
zaručuje pozdržení přerušení, do doby než doběhnou RT úlohy, aby nevznikaly nežádoucí prodlevy. Vzniká
zde tedy skutečně něco mezi vrstvou HAL a virtualizací OS.
Při přípravě jádra Linux jsem zkoušel aplikovat oba dva patche. Úprava Ingo Molnára je dnes již
ve stabilním stavu a nezaznamenal jsem žádný problém. Naopak RTAI nefunguje tak jak má, neobsahuje
univerzální patche Adeos/I-Pipe, ale své modifikace, nekompatibilní s mnoha verzemi jádra. Instalační
proces je pro systémy Unixového typu značně nestandardní a poskytované API není příliš „programátorsky přívětivé”. Z těchto důvodů jsem RTAI nahradil patchem Xenomai. Jedná se o mladší větev RTAI,
oddělenou od původního vývoje.
Další informace o RT modifikacích a jejich srovnání lze najít v [10].
1 Dnešní
implementace Xenomai jádro upravuje, viz [12], myšlenka HAL je využita nadále.
9
KAPITOLA 2. LINUX
2.2
RT patch
Pro použití RT patche je běžně potřeba překompilovat jádro. Po několika testech latence jsem však zjistil,
že RT jádro, které je součástí distribuce Ubuntu, vykazuje nejlepší parametry. I přes použití tohoto zde
uvedu některá nastavení, nutná při kompilaci [6]:
• pro správnou funkci je zásadní hardwarová podpora přesných časovačů a její zapnutí v jádře (HPET
či TSC)
• dále je nutné deaktivovat APM, nechat aktivní ACPI (pro funkci HPET), ale její části vypnout –
nadbytečné moduly správy napájení zvyšují latenci a vyvolávají nežádoucí přerušení
• aktivovat volby „CONFIG_PREEMPT=y“, „CONFIG_PREEMPT_RT=y„ a také vysoce přesné
časovače „CONFIG_HIGH_RES_TIMERS=y“
• jakékoliv ladicí volby (debug) prodlužují latenci jádra
• neaktivujte podporu swapu – výpadky stránek (page fault) vedou k velké latenci
• podpora škálování CPU (změna napájení/frekvence za běhu) nesmí být povolena (může mít vliv
nejen na výkon, ale i funkci některých časovačů)
I při správném nastavení jádra je potřeba dát pozor na některé hardwarové problémy. Největším z nich je
SMI mód procesoru. Ten může být kdykoliv vyvolán hardwarem a má větší prioritu než nemaskovatelná
přerušení. Když procesor přejde do SMI, vykoná obslužný kód z pevně dané adresy (obvykle BIOS
ROM) a systém nejen nemůže rutinu zdržet, ale ani není schopen přechod do SMI detekovat a reagovat
na něj. Tento problém působí latenci až v řádu stovek mikro sekund a projevuje se na x86 platformě i
ve specializovaných RT operačních systémech.
Další systémově nezávislou latenci v řádu mikro sekund může působit přidělování DMA (s tímto
problémem jsem se nesetkal). V lepších případech je možné latenci ovlivnit nastavením ovladače.
VGA konzole působí velmi vysokou latenci. Zápis na ni může zcela znehodnotit běh RT aplikace. Je
doporučeno využít sériovou konzoli, SSH, grafické prostředí nebo framebuffer.
Při psaní RT programů je potřeba dodržovat některé zásady. Předně je nutné vyvarovat se výpadku
stránek. Nelze v RT sekcích pracovat se soubory, které nejsou načtené v operační paměti a provádět další
systémová volání, která mohou způsobit výpadek stránky. Vlákna s RT prioritou se vytváří na začátku
programu – ne dynamicky. Stejně tak je vhodné pro pozdější dynamickou alokaci paměti vyhradit místo již
před spuštěním RT sekcí, pomocí funkce malloc(). Co nejdříve se v sekci Main() zapíše volání mlockall(),
které zajistí uzamčení všech zdrojů (soubory mapované do paměti, použité knihovny...) v operační paměti.
Pokud je potřeba přistupovat ke sdíleným zdrojům, nesmí se použít semafory, ale speciální mutex „PTHREAD_PRIO_INHERIT“. Ten umožňuje navýšení priority vlákna vlastnícího zdroj, pokud
ve frontě čeká úloha s vysokou prioritou, čímž se dosáhne co nejrychlejšího dokončení práce s uzamčeným
zdrojem a jeho předání RT úloze. Některé specializované systémy ve svém API umožňují zapnutí Priority
Inheritance při vytváření semaforu.
U složitějších aplikací je nejlepším řešením předávání dat použitím více vláken, která si předávají
data pomocí soketů. Hlavní vlákno běží s vysokou prioritou a ostatní (pro zpracování souborů, výpisy
výsledků, atd.) mají prioritu mnohem nižší. Více informací uvádí literatura [3].
10
KAPITOLA 2. LINUX
2.3
RTAI (Xenomai)
Veškeré informace uvedené v této části se budou týkat Xenomai a nemusí zcela platit pro původní RTAI.
Xenomai vznikl jako nová větev, která si klade za cíl vylepšit původní implementaci a také později
dosáhnout kompatibility s RT_PATCH. Sám mohu potvrdit, že instalace Xenomai je jednodušší, rozšíření
stabilní a obsahuje dokonalejší API.
Po stažení balíku Xenomai a patche Adeos/iPipe dojde nejprve ke spuštění úvodního nastavení, při
kterém Xenomai aplikuje patch. Narozdíl od RTAI je zde stažen oficiální patch z projektu Adeos, což
umožňuje instalovat téměř na libovolné jádro (RTAI poskytuje jen velmi omezenou podporu jader) a
využít tak i bezpečnostní patche hlavní větve jádra.
V další části již lze spustit standardní konfiguraci a kompilaci jádra GNU/Linux. V konfiguračním
menu přibudou volby pro aktivaci Xenomai. Patch dokonce nepovolí aktivaci Xenomai pokud je např.
aktivováno ACPI nebo jiná nekompatibilní součást. Obecně platí při konfiguraci podobné zásady jako
u výše zmíněného RT_PATCH. Po restartu do nového jádra je nutno kompilovat samotné Xenomai. Pro
podrobné nastavení doporučuji prostudovat [4].
Xenomai redukuje problémy s SMI. V krajním případě je možné se pokusit jej zcela vypnout - to
ale nelze doporučit, protože tím zastavíte i některé bezpečnostní mechanismy, např. Thermal Throtling,
který se stará o zastavení CPU při přehřátí.
Pro psaní programů platí stejné zásady jako ty uvedené v části o RT_PATCH. Dále je nutno zmínit,
že Xenomai ještě není zcela kompatibilní s knihovnami Comedi. Nicméně žádný problém jsem nezaznamenal a s Xenomai jsem nakonec dosáhl výborných výsledků i při nasazení na velmi vytíženém systému.
Pro psaní programů je neocenitelná výborná dokumantace [1].
Rozšíření se snaží o snadný přechod z jiných RT systémů. Proto API definuje RT úlohy jako speciální
objekty „task” a nabízí metody pro manipulaci s nimi. Dále počítá s periodickými úlohami a obsahuje
speciální čekací metody. Jako příklad uvedu vytvoření jednoduché periodické RT úlohy.
RT_TASK p e r i o d i c _ t a s k ;
v o i d task_body ( v o i d ∗ a r g )
{
/∗
∗ Arguments : &t a s k (NULL= s e l f ) ,
∗
s t a r t t im e ,
∗
period ( here : 1 s )
∗/
r t _ t a s k _ s e t _ p e r i o d i c (NULL , TM_NOW, 1 0 0 0 0 0 0 0 0 0 ) ;
while ( true ) {
// Do c o d e . . . . .
r t _ t a s k _ w a i t _ p e r i o d (NULL) ;
}
return ;
}
i n t main ( i n t a r g c , c h a r ∗ a r g v [ ] )
{
/∗ A v o i d s memory s w a p p i n g f o r t h i s program ∗/
m l o c k a l l (MCL_CURRENT| MCL_FUTURE) ;
/∗
∗ Arguments : &t a s k ,
∗
name ,
∗
s t a c k s i z e (0= d e f a u l t ) ,
∗
priority ,
11
KAPITOLA 2. LINUX
∗
mode (FPU , s t a r t s u s p e n d e d , . . . )
∗/
r t _ t a s k _ c r e a t e (& p e r i o d i c _ t a s k , " t r i v i a l " , 0 , 9 9 , T_JOINABLE) ;
/∗
∗ Arguments : &t a s k ,
∗
task function ,
∗
f u n c t i o n a r g u me n t
∗/
r t _ t a s k _ s t a r t (& p e r i o d i c _ t a s k , &task_body , NULL) ;
/∗ Wait f o r t a s k f i n i s h ∗/
r t _ t a s k _ j o i n (& p e r i o d i c _ t a s k ) ;
r t _ t a s k _ d e l e t e (& p e r i o d i c _ t a s k ) ;
e x i t (0) ;
}
2.4
Comedi
Projekt Comedi vytváří a spravuje ovladače zařízení pro měřicí karty mnoha výrobců (National Instruments, Amplicon, Adlink, Advantech, Analog Devices, atd.). Jedná se o karty do PCI, ISA i USB a to
hlavně univerzální DAQ a DIO zařízení. Comedi umožňuje i snadnou úpravu ovladačů a tvorbu nových.
Stačí aby nový ovladač využíval předdefinované struktury Comedi a inicializační rutiny.
Většina kódu je udržována komunitou, nicméně někteří výrobci již vyvýjí sami ovladače pro Comedi
a nabízí je zákazníkům. Tento postup je pro ně výhodnější než vývoj čistě komerčních ovladačů a při tom
zachovává možnost uchovat některé části kódu uzavřené, použitím proprietárního firmware procesoru
na DAQ kartě. Pro zákazníka je výhoda otevřenosti ovladačů tímto dotčena jen minimálně. Při použití
Comedi je možné aby si zákazník sám ovladač upravil, měl možnost zkontrolovat jeho kvalitu a pochopit,
co karta s daty provádí.
Obrázek 2.1: Získávání dat z DAQ karty
Součásttí projektu je také Comedilib, což je sada knihoven, které lze využít pro kumunikaci s ovladačem. Nejjednodušší na použití, ale také nejméně konfigurovatelné jsou funkce comedi_data_read() a
comedi_dio_read() (případně „write” verze). Pomocí nich lze snadno vyčítat analogové a digitální kanály a s využitím comedi_to_phys() i převádět data na správné fyzikální jednotky. Dále existují funkce
pro konfiguraci kanálů atd.
O málo složitější, ale velmi flexibilní jsou instrukce. Pro jejich provedení stačí vyplnit strukturu comedi_insn_struct, která obsahuje instrukci (získání dat, zápis dat, konfiguraci kanálu), údaje o kanálu
12
KAPITOLA 2. LINUX
který se bude měřit a ukazatel na data. Takto lze nadefinovat celé pole (insnlist) připravených struktur,
které představuje po sobě jdoucí instrukce a celé jej vykonat pomocí comedi_do_insnlist().
Nejsložitější je použití příkazů. Je nutné naplnit strukturu comedi_cmd(), která obsahuje údaje nejen
o měřených kanálech a jejich konfiguraci, ale také o spouštění měření, odměru kanálu, A/D převodu, atd.,
jak je vidět z obrázku 2.1.
Je nutno říct, že ne všechny zásuvné měřicí karty podporují všechny typy spouštění. K tomu je totiž
potřeba skutečná hardwarová podpora příslušných spouštění. Např. karta NI 6221 podporuje hardwarové
spouštění externím vstupem pouze na analogových linkách. V případě dražších karet lze ale periodicky
vyčítat / nastavovat digitální i analogové vstupy při spouštění digitálním vstupem, určit spouštění všech
událostí z 2.1, atd.
Vývoj Comedilib je stále aktivní a do budoucna se testuje podpora analogové filtrace, analogového
spouštění, spouštění celým bajtem (shoda v bitech), NI RTSI Trigger Bus a mnohé další.
13
KAPITOLA 3
Časovače v PC
V počítači typu PC se můžeme setkat s mnoha programovatelnými časovači [5]. Některé na základních
deskách zůstávají již jen z historických důvodů, jiné se teprve začínají využívat. V této kapitole najdete
popis nějběžněji používaných časovačů.
3.1
PIT
Jedná se o nejstarší typ časovače, který přes své nevýhody stále přežívá i v moderních počítačích. Dříve
byl realizován samostatným obvodem Intel 8253 [11], dnes je často integrován do chipsetu (southbridge).
Obvod obsahuje tři nezávislé šestnáctibitové programovatelné časovače, z nichž časovač číslo dva bývá
použit pro refresh cyklus operační paměti a číslo tři ke generování tónů pro PC speaker.
Každý z časovačů lze naprogramovat do jiného módu. Čítání od zadaného čísla do nuly, modulo N,
generátor obdélníku, ... Pokud je tento časovač použit jako systémový, je spuštěn v periodickém čítání
od N do nuly.
Jeho maximální frekvence je 1000 Hz. Nebývá ale příliš přesný a přístup pro čtení/zápis je zdlouhavý
(dáno umístěním na southbridge).
3.2
RTC
Obvod Real Time Clock je dnes již také běžnou součástí chipsetu. Jedná se o obvod s nízkým časovým
rozlišením, který běží neustále (napájen z baterie a udržuje čas PC). Jeho programování je nejpomalejší
ze všech časovačů – používá se téměř výhradně v periodickém módu (OS GNU/Linux jen pro čtení data
a času). Je schopen dosáhnout frekvencí 2 Hz až 8192 Hz. Určitou výhodou tohoto časovače je nezávislost
na konkrétním CPU a chipsetu.
3.3
APIC
Obvod APIC (Advanced Programmable Interrupt Controller) se stará o řazení přerušení a jejich doručení
na příslušný procesor a zajišťuje meziprocesorová přerušení.
Z tohoto obvodu lze použít časovač APIC Timer [7]. Jedná se o třicetidvoubitový programovatelný
časovač, použitelný pro časování systémových úloh a přesné měření času v aplikacích. Jeho frekvence je
odvozena od systémové sběrnice a vydělena registrem Divide. Používá se opět mód čítání od N do nuly.
14
KAPITOLA 3. ČASOVAČE V PC
Pokud je aktivován periodický mód, čítač po vyvolání přerušení pokračuje opět od N. Hodnota čítače
může být resetována při hlubokém spánku CPU (Intel SpeedStep).
V OS GNU/Linux bývá výchozím časovačem (pokud není deaktivován APIC nebo není dostupný
vhodný TSC časovač).
3.4
TSC
TSC (Time Stamp Counter) je součástí CPU [7], a proto může být vyčítán ještě rychleji než APIC
Timer. Jeho frekvence je odvozena od frekvence CPU a obsahuje 64 bitová data. Teoretické rozlišení tedy
odpovídá frekvenci procesoru, což se může pohybovat pod 1 ns.
Tento časovač však přináší některé nevýhody. Je bezpodmínečně nutné nepoužívat Intel SpeedStep,
protože snížení frekvence CPU vede ke změně frekvence časovače (novější Intel P4 používají pro časovač
konstantní násobič a změna frekvence tak nevadí). Procesor nesmí přejít do stavu hlubokého spánku
(časovač se zastaví). Největší komplikací jsou výcejádrové procesory. Každé jádro používá vlastní TSC
časovač a při plánování jedné úlohy na různá jádra může dojít k nečekaným časovým posunům – OS
GNU/Linux však již obsahuje některé patche pro odstranění tohoto problému.
3.5
HPET
HPET (High Precision Event Timer) je nejmladší časovač ze všech zmiňovaným [8]. Microsoft jej implementoval až ve Windows Vista a GNU/Linux jej stále ještě příliš nevyužívá (podpora ale existuje déle).
Tento časovač by měl podle Intelu časem nahradit starý obvod PIT, čemuž svědčí i zájem firmy AMD a
implementace v jejích chipsetech.
Časovač dosahuje podobného rozlišení jako TSC, má také 64 bitové registry, ale nezpůsobuje problémy
s Intel SpeedStep a vícejádrovými procesory (i tak je ale Intel SpeedStep pro RT aplikace nevhodný),
protože je součástí chipsetu a využívá externí krystalový oscilátor. Ne všichni výrobci základních desek
však HPET ve firmwareu aktivují a na desku umisťují příslušný oscilátor.
Registry časovače jsou mapovány do I/O prostoru PC a snadněji dostupné pro CPU než PIT.
3.6
Problémy časování
Všechny časovače v PC mají nějaké nevýhody. Ty specifické pro určité typy jsou uvedeny výše. Dále
však přesnost časování ovlivňují další vlastnosti architektury dnešních procesorů. Paradoxně větší výkon
a efektivnost procesorů je pro RT úlohy na škodu a starší procesory by některé úlohy řešily s menším
rozptylem časování (jitter), v době jejich používání ovšem neexistovaly přesné časovače.
Dnešní CPU využívají např. tzv. Instruction Pool, který jim umožňuje měnit pořadí zpracování instrukcí. Při normálním nasazení toto nevadí – vede to k možnosti paralelně načítat instrukce a data
a zpracovávat instrukce, právě dle času doručení potřebných zdrojů. U RT aplikace však situace vede
ke zvětšení jitteru. Stejně tak odhadování skoků může mít vážné následky – někdy je odhadnut správný
průchod cyklem, někdy ne, což vede opět na větší jitter.
Přerušení, míříci na nožičky CPU prochází přes APIC, takže nelze zaručit jejich okamžité doručení
v původním pořadí.
15
KAPITOLA 4
Měřicí karty
Celkem byly použity dvě měřicí karty - DAQ NI PCI 6221 a DIO Amplicon PCI 236.
4.1
NI PCI 6221
Jedná se o DAQ zásuvnou měřicí kartu určenou
pro slot PCI, obsahující 16 bit analogové vstupy/výstupy, 10 DIO linek a 32 bit čítač. Použitý vzorek, který byl katedře věnován firmou National Instruments, nebyl zcela funkční, nicméně DIO linky
jsou použitelné. Na této kartě byla provedena prvotní měření. Z důvodu složitějšího obvodového řešení a proprietárního procesoru jsem se obával zbytečné latence v obvodech měřicí karty. Zpracování
přerušení zde také není triviální záležitostí.
4.2
Amplicon PCI 236
Tato karta byla použita jako druhá, hlavně pro měření latence přerušení. Její obvodové řešení je velmi
jednoduché a zaručuje nízkou latenci. Karta obsahuje pouze obvod PPI (82C55) a připojení na sběrnici PCI pomocí PLX9052. Brána C, pin 3 je navíc
připojen na pin přerušení PLX9052. Oba obvody
jsou mapovány do IO prostoru PCI sběrnice a jejich konfigurační registry je možno libovolně nastavovat.
PPI 82C55 je programovatelný obvod, obsahující tři 8 bit brány a konfigurační registr. Brány
A, B a 2x 1/2 C lze individuálně konfigurovat
na vstup/výstup. Verzi použitou na měřicí kartě
vyrábí firma OKI a od původního 8255 se liší vyšší
16
KAPITOLA 4. MĚŘICÍ KARTY
rychlostí a nižším proudovým odběrem.
PLX9052 je velmi univerzální obvod, umožňující připojení množství různorodých periferií na sběrnici
PCI, včetně obvodů určených původně pro ISA. Obvod vytváří sběrnici local bus, která může být 8 bit
až 32 bit, je vybaven FIFO frontou a nezávislými hodinami, pro připojení obvodů s jinou rychlostí než
PCI sběrnice. Výběr obvodu, který bude aktivní na local bus lze realizovat až čtyřmi signály chip select
a v případě multiplexování sběrnice je přítomen signál ALE. Pro uchování konfiguračních dat (včetně ID
výrobce a zařízení k identifikaci na PCI) lze připojit paměť EEPROM.
Karta je primárně určena k výzkumu a testování, čemuž odpovídá umístění pájivého kontaktního
pole v blízkosti DIO linek, které umožňuje přidání vlastních obvodů (dokumentace poskytuje i návod pro
zapojení optických oddělovačů a dalších obvodů) a v neposlední řadě také použití patice pro PPI (snadná
výměna při poškození).
Ke kartě je dodávána plná technické dokumentace, včetně schéma zapojení, příkladů programů (C/C++,
.NET, Delphi), ovladač pro Windows a NI LabView.
17
KAPITOLA 5
Generování přesného času
Po studiu možností GNU/Linux a jeho RT rozšíření jsem začal psát real-time aplikace v jazyce C.
Basic latency using RT_patch
Jakub Kocourek
March 14, 2009
START
main
5.1
Latence systému
První aplikace, kterou jsem vytvořil byl jednodu-
set_RT_priority
chý test latence. Zajímalo mě, jak rychle systém
připraví úlohu k běhu na procesoru, pokud se má
i=0
probudit z pasivního čekání nanosleep().
i=i+1
Na vývojovém diagramu 5.1 můžete vidět
funkci programu. Po startu aplikace se provede
i <= n
F
T
zvýšení priority a nastavení FIFO plánovače. Celá
get_time
latence je měřena jako rozdíl času čekání ve funkci
nanosleep() a skutečné sekundy (z HPET časo-
wait
(period)
vače). Důležitým výsledkem není sama průměrná
latence (systematická chyba), ale rozdíl minima a
maxima (náhodná chyba, kterou neovlivním).
get_time
Ze stovky změřených hodnot vychází rozdíl minima a maxima pouhé tři mikrosekundy, při prů-
calc_time_delta
měrné latenci šest mikrosekund. To je podstatný
údaj, který znamená, že úloha je na tomto systému
realizovatelná. Právě malý rozkmit latence (malá
neurčitost) charakterizuje RT systémy.
print_delta_times
Zdrojový kód je v příloze A.
Obrázek 5.1:
STOP
main
5.2
Sekundový generátor s HPET
časovačem a RT_PATCH
Page 1 of 1
Nyní již k samotné úloze generování sekundových tiků. RT_PATCH standardně nabízí dobrou
spolupráci s časovačem HPET, proto byl vybrán
18
KAPITOLA 5. GENEROVÁNÍ PŘESNÉHO ČASU
pro test této RT modifikace. Vývojový diagram je na obrázku 5.2. Jedná se o jednoduchou smyčku, ve
které nastavuji jeden pin na připojené DAQ kartě. Průměrná latence systému (systematická chyba) je
kompenzována staticky. Hodnota kompenzační konstanty
hlubší
opodstatnění,
protože není možné,
Basicnemá
DIO using
RT_patch
Jakub Kocourek
March 14, 2009
na rozdíl od jednoduchých uP bez OS, snadno určit dobu běhu jednotlivých příkazů.
Kalibrace tedy probíhá tak, že je program spuštěn s nulovou kompenzací, z naměřených chyb je
vypočtena střední hodnota (jako aritmetický prů-
START
main
měr) a ta je následně použita jako hledaná konstanta. Tímto způsobem je možno systematickou
set_RT_priority
chybu redukovat na několik desítek nanosekund.
V případě tohoto generátoru je změřená hodnota
průměrné latence (ze sta odměrů) 236,6758 us/tik.
true
S programem bylo dosaženo směrodatné od-
F
T
chylky pod jednu mikrosekundu, což je o řád lepší,
set_DIO
než vyžaduje zadání.
Zdrojový kód je v příloze B.
wait (Tup)
5.3
Sekundový generátor s TSC
clear_DIO
časovačem a Xenomai
Při použití rozšíření Xenomai je naopak výchozí
wait
(Tdown)
volbou časovač TSC (u HPET stále hrozí kolize
s dalšími úlohami, zvláště na 64 bit systémech).
Na vývojovém diagramu 5.3 a zvláště na zdrojovém kódu C je patrná odlišnost od předchozího
programu - jak jsem již uvedl, Xenomai se snaží
Obrázek 5.2:
STOP
main
o maximální kompatibilitu se skutečnými RT systémy (např. VxWorks), a proto zde existují úlohy
(task), kterým je možné nastavit periodicitu.
Page 1 of 1
Jádro programu je však stejné. Opět pomocí
klihoven Comedi nastavuji DAQ kartu. Xenomai však již počítá s periodickými úlohami, lze tedy provést
hlavní kód cyklu a následně nechat úlohu dospat zbylý čas do jedné sekundy. Ale ani s touto podporou
knihoven Xenomai není časovač přesný a je nutné staticky kompenzovat systematickou chybu stejným
způsobem jako v případě RT_PATCH. Zde je průměrná latence 75,9615 us/tik. Protože Xenomai se
o tuto kompenzaci částečně pokouší sám, při nulové kompenzaci v programu obvykle dojde k předčasnému
probouzení procesu, proto se v programu objevuje záporná kompenzace.
Dosahl jsem směrodatné odchylky pod polovinu mikrosekundy. To je také nejlepší dosažený výsledek.
5.4
Zpětnovazební sekundový generátor
Můj další pokus vedl k myšlence eliminovat latency „zpětnou vazbou”. Pokud po každém průchodu cyklem
generování tiku zjistím o kolik nanosekund jsem se odchýlil (regulační odchylka), mohu latency v dalším
kroku korigovat (akční zásah).
19
KAPITOLA 5. GENEROVÁNÍ PŘESNÉHO ČASU
Předpokladem takového řízení je však použití
velmi přesného referenčního signálu, což při prak-
START
gen_tik ()
tickém pokusu ani jeden z časovačů nedokázal. Latence byla stabilnější, ale kvůli systematické chybě
časovačů generátor nevyhověl žádaným paramet-
set_periodic(T)
rům.
true
V budoucnu bych rád zkusil použití přes-
F
ného krystalového oscilátoru, připojeného do čí-
T
tače na DAQ kartě. Časovače v PC by tak byly
použity pro krátkodobé časování a v pravidelných
set_DIO
intervalech by docházelo k jejich korekci proti ex-
START
main
ternímu časovači.
wait (Tup)
create task
(gen_tik)
5.5
clear_DIO
start task
(gen_tik)
Latence přerušení
Předchozí změřené hodnoty neodpovídají skutečné
latenci (systematická chyba), protože ta je z velké
wait_period
části kompenzována, ale jitteru, tedy měří se od-
join_task(gen_tik)
chylky od průměru. Měření skutečné latence přeru-
Obrázek 5.3:
STOP
gen_tik ()
šovacího systému je zajímavým parametrem, který
STOP
main
odhalí část latence výceméně nezávislou na toku
programu (nicméně nejedná se o latenci způsobenou pouze obvody karty). Rozšíření Xenomai
umožňuje v části user space ošetřovat přerušení,
což jsem také k tomuto měření využil.
20
KAPITOLA 6
Výsledky
Generátory, využívající oba typy RT rozšíření, byly testovány za standardních podmínek - teplota nebyla
regulována, PC skříň neobsahuje žádné vylepšené stínění ani speciální hardware. Při vysokém zatížení
PC a následném zahřátí jsem pozoroval značné odchylky periody generovaného signálu (zvýšení teploty
CPU o 15°C vyvolá nárůst systematické chyby téměř jedna mikrosekunda a směrodatnou odchylku zvýší
o 140%).
Teplotně závislá kompenzace je možná, má však nevýhodu - je nutno použít externí teplotní čidla,
protože integrovaná čidla užívají SMI přerušení. Při praktickém nasazení by bylo nutné PC provozovat
v klimatizovaném boxu a navíc vždy vyčkat zahřátí CPU a dalších součástí na provozní teplotu. Ani tak
nelze vyloučit problémy, protože CPU může podléhat velmi rychlým teplotním změnám. V tomto případě
skutečně platí pravidlo „RT systém není pouze výkonný systém” a lze jen doporučit úsporná mobilní CPU
s nízkým příkonem (samozřejmě s deaktivovaným Intel SpeedStep) a kvalitní chlazení.
6.1
Postup měření
Generátor byl na začátku měření vždy kalibrován (výše popsané nastavení statické kompenzace systematické chyby) a PC nebylo zatíženo žádnou jinou aktivní úlohou (pokud není uvedeno jinak) 1 .
V tabulce 6.1 jsou uvedeny naměřené hodnoty odchylky od generované periody.
Při použití HPET časovače a RT_PATCH jsem dosáhl směrodatné odchylky pod jednu mikrosekundu.
S TSC časovačem a Xenomai byl výsledek nejlepší - pouhých dvěstě osm nanosekund.
Jak jsem zmínil výše, úlohy s nízkou prioritou nesmí chování systému příliš ovlivnit. Proto jsem
provedl další měření, při kterém byl systém zatížen nejprve síťovým provozem (ping -i 0.02 localhost) a
následně vysokou činností pevného disku (dd if=/dev/zero >/test.file2 ). Test prokázal, že RT_PATCH
trpí zvyšováním offsetu generované periody se zvyšující se zátěží. Při extrémním vytížení (kopírování
souboru) již značně roste i směrodatná odchylka. Naopak Xenomai se vypořádal s vytíženým systémem
výborně, směrodatná odchylka ani offset významněji nenarostly.
Naměřené odchylky od generované periody jsou uvedeny v tabulce 6.2. V případě Xenomai narostl
offset na hodnotu cca 1 us se směrodatnou odchylkou 1,3 us v případě síťového provozu a 3,3 us při plné
zátěži.
1 Paralelně běžící úloha, běžící s normální prioritou by neměla ovlivnit RT úlohu, ale může zvyšovat zahřátí CPU a tím
značně ovlivnit měření.
2 Příkaz kopíruje data z virtuálního znakového zařízení, v tomto případě přečte samé nuly a dále je zapisuje na disk.
Dochází tedy k rozsáhlé komunikaci DMA kanálem a vysoké zátěži CPU.
21
KAPITOLA 6. VÝSLEDKY
Nezatížený NI
RT_PATCH
Nr. Latence (us)
1
0,138116
2
0,024143
3
0,358548
4
0,902119
5
-0,491965
6
-0,268267
7
0,135265
8
-0,924295
9
-0,031822
10
-0,250480
11
-0,370695
12
0,558922
13
-0,662443
14
0,814724
15
-0,302127
16
0,691097
17
0,450993
18
0,027143
19
-0,092019
Xenomai
Latence (us)
0,048136
0,076053
-0,028904
0,071764
-0,085392
0,087834
-0,033366
0,047333
0,075160
0,575725
-0,143896
-0,024338
0,194086
-0,173044
-0,308276
-0,096129
0,311469
0,213295
-0,297800
Nezatížený Amplicon
Xenomai
Nr.
Latence (us)
1
0,257284
2
0,797819
3
-0,257034
4
0,332734
5
0,230656
6
0,161903
7
0,678857
8
0,539242
9
0,316935
10
0,329921
11
-0,646465
12
0,430816
13
0,387691
14
0,341376
15
0,555455
16
0,702441
17
0,375247
18
0,487598
19
0,192109
Tabulka 6.1: Časovače HPET a TSC
Poslední částí měření byla latence přerušení. Měření bylo provedeno na DIO kartě Amplicon PCI 236.
Na přerušovací vstup (Port C, pin 3) byl z generátoru přiveden obdélníkový signál periody 1 Hz a výstup
karty snímán opět čítačem. Přerušení bylo zpracováno v Xenomai.
Z tabulky 6.3 je viditelná přímá souvislost latence přerušení s celkovou latencí, a to jak růst systematické složky, tak náhodné chyby.3
Počítač na kterém provádím testy je jednojádrový. Nebylo proto bohužel možné otestovat jak kvalitně
obě RT úpravy plánují úlohy na víceprocesorových systémech.
6.2
Výpočty
Pro všechna měření byla určena střední hodnota odchylky od generované periody a směrodatná odchylka.
Výsledky jsou uvedeny v tabulce 6.4. Pro srovnání je přidán i generátor sekundových pulzů na bázi
GPS modulu, který využívá nejen vnitřní krystalový oscilátor, ale také se synchronizuje s UTC stupnicí
vysílanou GPS satelity.
K měření byl použit přesný číslicový čítač SR620, s nastavením: režim měření periody, sample size 1,
mean; dle doporučení výrobce měření probíhalo po zahřátí přístroje (30 minut).
3 Při pohledu do tabulky nezapomeňte, že se jedná o latenci vstupní signál - zpracování - výstupní signál => pro porovnání
se sekundovým generátorem je potřeba uvažovat latenci přibližně poloviční.
22
KAPITOLA 6. VÝSLEDKY
Zatížený NI
Ping -i 0.02
RT_PATCH
Nr. Latence (us)
1
4,728035
2
2,982856
3
3,405238
4
2,761412
5
3,946772
6
3,378518
7
3,823351
8
4,816829
9
4,116759
10
1,496992
11
2,973858
12
1,248413
13
3,003415
14
1,782794
15
2,108667
16
2,655170
17
2,877431
18
2,868468
19
2,390652
Xenomai
Latence (us)
-1,314090
-0,596590
-0,770900
-0,563525
0,402851
-3,592917
-1,610528
-3,529700
-0,261070
1,300003
-0,655271
-2,992768
-1,626622
0,811518
-1,334961
-0,320977
-1,517497
-1,693427
-0,291718
Zatížený Amplicon
Ping -i 0.02
RT_PATCH
Nr. Latence (us)
1
0,393845
2
-0,537568
3
-0,641919
4
0,445326
5
0,066891
6
0,658027
7
2,225106
8
-0,209842
9
1,942543
10
-0,334353
11
0,845462
12
-0,898749
13
0,987546
14
1,157929
15
-0,092906
16
0,091371
17
1,636681
18
1,436255
19
0,333404
dd if=/dev/zero of=/test.file
RT_PATCH
Xenomai
Latence (us)
Latence (us)
26,522366
1,163433
26,558537
-8,147502
27,871766
-6,110571
17,547833
0,764081
31,689233
1,683832
28,858341
0,234817
27,701942
-2,267418
22,055732
-1,676823
31,142486
-0,169651
25,681642
0,181342
dd if=/dev/zero of=/test.file
Xenomai
Latence (us)
0,975420
9,358143
0,080029
4,147201
-0,061206
1,440782
-0,309625
-0,759937
4,776625
0,590603
Tabulka 6.2: Sekundový generátor na zatíženém systému
23
KAPITOLA 6. VÝSLEDKY
Nr.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Nezatížený
Latence (us)
29,899537
29,693331
29,095331
29,384040
29,941036
29,701259
29,286675
28,099839
27,628202
30,803522
29,555659
29,731634
28,530764
28,579183
27,615211
28,729956
29,402074
29,435946
29,532034
Ping -i 0.02
Latence (us)
33,313797
32,142307
33,954552
34,491528
33,915926
32,572507
33,927029
38,893306
34,618979
35,951790
34,904263
35,308719
34,350449
34,811306
36,179720
35,397583
35,107123
34,046890
33,793369
dd if=/dev/zero of=/test.file
Latence (us)
61,354816
56,893206
54,150734
51,083375
50,089003
58,754955
66,672019
62,628625
62,198771
57,182256
51,158032
51,070882
62,033232
57,449056
52,397251
50,978128
60,524792
62,688665
58,751817
Latence (us)
29,191854 ± 0,817111
34,614797 ± 1,461691
57,266296 ± 5,077210
Tabulka 6.3: Latence přerušení
Časovač
RT_PATCH (HPET)
Xenomai (TSC)
RT_PATCH ping
Xenomai ping
RT_PATCH dd
Xenomai dd
GPS
Latence (us) NI
+0,037208 ± 0,501110
+0,026827 ± 0,208202
+3,053914 ± 0,974659
-1,007909, ± 1,319111
Přes 10 us směrodat. odch.
-1,434446 ± 3,265764
+0,004365 ± 0,013650
Tabulka 6.4: Výsledky
24
Latence (us) Amplicon
Neměřeno
+0,327083 ± 0,331237
Neměřeno
+0,500266 ± 0,892113
Neměřeno
+2,023804 ± 3,011545
+0,004365 ± 0,013650
KAPITOLA 7
Zhodnocení
Je patrné, že generátor přesných pulzů, založený na bázi běžného PC nemůže konkurovat jednoúčelovým
zařízením. Primitivní integrované obvody jsou vždy schopné zajistit časování mnohem přesněji, než komplexní uP, podporující běh mnoha úloh, které nejsou navrženy pro specifické potřeby RT aplikací. I přes
to jsou dosažené výsledky lepší než očekávané, hlavně proto že v posledních několika letech se integrované
časovače velmi zlepšily (což dokazuje i zájem firmy AMD o HPET časovač, který původně vyvinul Intel)
a také GNU/Linux postupně vylepšuje práci s nimi a řeší nejrůznější problémy (např. TSC časovače na
SMP systému). Nové časovače se ale prosazují velmi pomalu a ještě dlouho potrvá jejich plná podpora
v operačních systémech, proto se stále setkáváme ve starými obvody PIT a APIC. Výrobci hardware i
vývojáři RT rozšíření mají do budoucna rozhodně co vylepšovat. Ostatně z kapitoly 5 je patrné, že dnes
neexistuje žádný ideální časovač a navíc každý OS preferuje jiný a jinak řeší různé problémy.
Obě testovaná RT rozšíření se osvědčila. Pro skutečnou garanci latence a přenos kódu na RT systémy
jako např. VxWorks bych doporučil spíše Xenomai se svým jednoduchým API, podobným právě VxWorks.
Navíc RT_PATCH mne o svých kvalitách nepřesvědčil, kvůli špatným výsledkům na zatíženém systému.
Mezi další výhody Xenomai patří snadný přístup k hardwaru - registrům, handleru přerušení, atd.
Tato práce měla mapovat možnosti RT rozšíření v GNU/Linux a základní problémy při práci s měřicími kartami při generování času. S kartou NI PCI 6221 i Amplicon PCI 236 jsem dosáhl obdobných
výsledků. Seměrodatná odchylka se pohybovala od 0,2 us u nezatíženého systému, až po 3,3 us na vytíženém systému (v obou případech měřeno s Xenomai).
V dalším studiu bych rád v práci pokračoval měřením dlouhodobé časové stability a prozkoumáním
možnosti synchronizace s externím přesným oscilátorem.
25
KAPITOLA 8
Použitá literatura
[1] Xenomai API reference, 26.2.2009.
Dostupné z: http://www.xenomai.org/documentation/
branches/v2.4.x/html/api/index.html.
[2] AG, S. PikeOS datasheet, 2009. Dostupné z: http://www.sysgo.com/fileadmin/user_upload/
datasheets/PikeOS.pdf.
[3] BOHMER, R. HOWTO: Build an RT-application, 29.1.2008. Dostupné z: http://rt.wiki.kernel.
org/index.php/HOWTO:_Build_an_RT-application.
[4] BOUWMAN, W. Xenomai quick build guide, 20.8.2008. Dostupné z: http://www.xenomai.org/
index.php/Xenomai_quick_build_quide.
[5] BOVET, D. P. – CESATI, M. Understanding the Linux Kernel, 3rd Edition. O’Reilly, 2005.
[6] FU, L. – SCHWEBEL, R. RT Preempt Howto, 28.4.2006. Dostupné z: http://rt.wiki.kernel.
org/index.php/RT_PREEMPT_HOWTO.
[7] INTEL. Intel(R) 64 and IA-32 Architectures Software Developer’s Manuals, Ver. 029. Dostupné z:
http://www.intel.com/products/processor/manuals/index.htm.
[8] INTEL. IA-PC HPET (High Precision Event Timers) Specification, Ver. 1.0a. Dostupné z: http:
//www.intel.com/hardwaredesign/hpetspec_1.pdf.
[9] LIU, J. W. S. Real-time systems. Prentice Hall, 2000.
[10] PELČÁK, V. Real-time modifikace Linuxu, 3.2.2006. Dostupné z: www.abclinuxu.cz.
[11] WIKIPEDIA. Intel 8253, 9.3.2009. Dostupné z: http://en.wikipedia.org/wiki/Intel_8253.
[12] YAGHMOUR, K. Adeos design document, 15.2.2001. Dostupné z: http://www.opersys.com/
adeos/dox/adeos/index.html.
26
PŘÍLOHA A
Zdrojový kód - test latence systému
#i n c l u d e < s t d i o . h>
#i n c l u d e <s y s / t i m e . h>
#i n c l u d e <t i m e . h>
#i n c l u d e <p t h r e a d . h>
#i n c l u d e <s c h e d . h>
#d e f i n e CNT 10
/∗ S u b s t r a c t i o n i n t e r v a l =now−b e g i n ∗/
int delta_t ( struct timespec ∗ i n t e r v a l , struct timespec ∗ begin , struct
t i m e s p e c ∗now )
{
i n t e r v a l −>t v _ n s e c = now−>t v _ n s e c − b e g i n −>t v _ n s e c ; /∗ S u b t r a c t ’ d e c i m a l
f r a c t i o n ’ f i r s t ∗/
i f ( i n t e r v a l −>t v _ n s e c < 0 ) {
i n t e r v a l −>t v _ n s e c += 1 0 0 0 0 0 0 0 0 0 ; /∗ Borrow 1 s e c from ’ t v _ s e c ’ i f
s u b t r a c t i o n −ve ∗/
i n t e r v a l −>t v _ s e c = now−>t v _ s e c − b e g i n −>t v _ s e c − 1 ; /∗ S u b t r a c t
w h o l e number o f s e c o n d s and r e t u r n 1 ∗/
return (1) ;
}
else {
i n t e r v a l −>t v _ s e c = now−>t v _ s e c − b e g i n −>t v _ s e c ; /∗ S u b t r a c t w h o l e
number o f s e c o n d s and r e t u r n 0 ∗/
return (0) ;
}
}
i n t main ( ) {
struct timespec ts , ts_after , ts_delta ;
struct timespec tts , tts_after , tts_delta ;
27
PŘÍLOHA A. ZDROJOVÝ KÓD - TEST LATENCE SYSTÉMU
const s t r u c t t i m e s p e c i n t e r v a l = { 1 , 0 } ;
s t r u c t sched_param s c h e d u l i n g _ p a r a m e t e r s ;
i n t count = 0;
unsigned long t d [CNT ] ;
/∗ Lock p a g e s i n r e a l memmory ∗/
m l o c k a l l (MCL_CURRENT|MCL_FUTURE) ;
/∗ S e t u p p r i o r i t y ∗/
scheduling_parameters . s c h e d _ p r i o r i t y = sched_get_priority_max (
SCHED_FIFO) − 4 ;
/∗ S e t u p FIFO s c h e d u l e r ∗/
i f ( 0 != p t h r e a d _ s e t s c h e d p a r a m ( p t h r e a d _ s e l f ( ) , SCHED_FIFO , &
scheduling_parameters ) )
{
p e r r o r ( " pthread_setschedparam ␣ e r r o r " ) ;
}
/∗ Get t i m e t o meas ure r u n n i n g t i m e ∗/
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t t s ) ;
f o r ( c o u n t = 0 ; c o u n t < CNT ; c o u n t++) {
// Time b e f o r e
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t s ) ;
n a n o s l e e p (& i n t e r v a l , NULL) ;
// Time a f t e r
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t s _ a f t e r ) ;
d e l t a _ t (& t s _ d e l t a , &t s , &t s _ a f t e r ) ;
// W r i t e l a t e n c y
td [ count ] = t s _ d e l t a . tv_nsec ;
}
/∗ Get t i m e t o meas ure r u n n i n g t i m e ∗/
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t t s _ a f t e r ) ;
d e l t a _ t (& t t s _ d e l t a , &t t s , &t t s _ a f t e r ) ;
p r i n t f ( " T o t a l ␣ w a i t : ␣%l u ␣ s e c ␣%l u ␣ n s e c \n" , t t s _ d e l t a . tv_sec , t t s _ d e l t a .
tv_nsec ) ;
f o r ( c o u n t = 0 ; c o u n t < CNT ; c o u n t++) {
p r i n t f ( " Waited : ␣%l u ␣ n s e c ␣ \n" , t d [ c o u n t ] ) ;
}
return 0;
}
28
PŘÍLOHA B
Zdrojový kód - sekundový generátor s HPET
časovačem
#i n c l u d e < s t d i o . h>
#i n c l u d e <s y s / t i m e . h>
#i n c l u d e <t i m e . h>
#i n c l u d e <p t h r e a d . h>
#i n c l u d e <s c h e d . h>
#i n c l u d e <c o m e d i l i b . h>
#d e f i n e CNT 100
#d e f i n e CORRECT_SYST 23168 // C o r r e c t i o n o f s y s t e m a t i c e r r o r
/∗ S u b s t r a c t i o n i n t e r v a l =now−b e g i n ∗/
int delta_t ( struct timespec ∗ i n t e r v a l , struct timespec ∗ begin , struct
t i m e s p e c ∗now )
{
i n t e r v a l −>t v _ n s e c = now−>t v _ n s e c − b e g i n −>t v _ n s e c ; /∗ S u b t r a c t ’ d e c i m a l
f r a c t i o n ’ f i r s t ∗/
i f ( i n t e r v a l −>t v _ n s e c < 0 ) {
i n t e r v a l −>t v _ n s e c += 1 0 0 0 0 0 0 0 0 0 ; /∗ Borrow 1 s e c from ’ t v _ s e c ’ i f
s u b t r a c t i o n −ve ∗/
i n t e r v a l −>t v _ s e c = now−>t v _ s e c − b e g i n −>t v _ s e c − 1 ; /∗ S u b t r a c t
w h o l e number o f s e c o n d s and r e t u r n 1 ∗/
return (1) ;
}
else {
i n t e r v a l −>t v _ s e c = now−>t v _ s e c − b e g i n −>t v _ s e c ; /∗ S u b t r a c t w h o l e
number o f s e c o n d s and r e t u r n 0 ∗/
return (0) ;
}
29
PŘÍLOHA B. ZDROJOVÝ KÓD - SEKUNDOVÝ GENERÁTOR S HPET ČASOVAČEM
}
i n t main ( ) {
struct timespec tts , tts_after , tts_delta ;
const s t r u c t t i m e s p e c i n t e r v a l _ u p = { 0 , 1 0 0 0 0 0 0 0 0 } ;
s t r u c t t i m e s p e c i n t e r v a l _ d o w n = {0 ,900000000 −CORRECT_SYST} ;
s t r u c t sched_param s c h e d u l i n g _ p a r a m e t e r s ;
i n t count = 0;
comedi_t ∗ i t ;
i n t subdev = 2 ;
i n t chan = 0 ;
/∗ Lock p a g e s i n r e a l memmory ∗/
m l o c k a l l (MCL_CURRENT|MCL_FUTURE) ;
/∗ S e t u p p r i o r i t y o f p r o c e s s ∗/
scheduling_parameters . s c h e d _ p r i o r i t y = sched_get_priority_max (
SCHED_FIFO) − 4 ;
/∗ S e t u p s c h e d u l i n g s t r a t e g y ∗/
i f ( 0 != p t h r e a d _ s e t s c h e d p a r a m ( p t h r e a d _ s e l f ( ) , SCHED_FIFO , &
scheduling_parameters ) )
{
p e r r o r ( " pthread_setschedparam ␣ e r r o r " ) ;
}
/∗ Open Comedi d e v i c e ∗/
i t = comedi_open ( " / dev / comedi0 " ) ;
/∗ C o n f i g u r e l i n e s t o DO ∗/
c o m e d i _ d i o _ c o n f i g ( i t , subdev , chan ,COMEDI_OUTPUT) ;
/∗ Get t i m e t o meas ure r u n n i n g t i m e ∗/
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t t s ) ;
f o r ( c o u n t = 0 ; c o u n t < CNT ; c o u n t++) {
c o m e d i _ d i o _ w r i t e ( i t , subdev , chan , 1 ) ;
n a n o s l e e p (& i n t e r v a l _ u p , NULL) ;
c o m e d i _ d i o _ w r i t e ( i t , subdev , chan , 0 ) ;
n a n o s l e e p (& i n t e r v a l _ d o w n , NULL) ;
}
/∗ Get t i m e t o meas ure r u n n i n g t i m e ∗/
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t t s _ a f t e r ) ;
d e l t a _ t (& t t s _ d e l t a , &t t s , &t t s _ a f t e r ) ;
p r i n t f ( " T o t a l ␣ w a i t : ␣%l u ␣ s e c ␣%l u ␣ n s e c \n" , t t s _ d e l t a . tv_sec , t t s _ d e l t a .
tv_nsec ) ;
comedi_close ( i t ) ;
return 0;
}
30
PŘÍLOHA C
Zdrojový kód - sekundový generátor s TSC
časovačem
#i n c l u d e < s t d i o . h>
#i n c l u d e < s i g n a l . h>
#i n c l u d e <u n i s t d . h>
#i n c l u d e <s y s /mman . h>
#i n c l u d e <n a t i v e / t a s k . h>
#i n c l u d e <n a t i v e / t i m e r . h>
#i n c l u d e <c o m e d i l i b . h>
#i n c l u d e <t i m e . h>
#i n c l u d e <s y s / t i m e . h>
#d e f i n e CNT 10
#d e f i n e CORRECT_SYST −2640 // C o r r e c t i o n o f s y s t e m a t i c e r r o r
RT_TASK comedi_task ;
RTIME s t a r t , end ;
/∗ S u b s t r a c t i o n i n t e r v a l =now−b e g i n ∗/
int delta_t ( struct timespec ∗ i n t e r v a l , struct timespec ∗ begin , struct
t i m e s p e c ∗now )
{
i n t e r v a l −>t v _ n s e c = now−>t v _ n s e c − b e g i n −>t v _ n s e c ;
i f ( i n t e r v a l −>t v _ n s e c < 0 ) {
i n t e r v a l −>t v _ n s e c += 1 0 0 0 0 0 0 0 0 0 ;
i n t e r v a l −>t v _ s e c = now−>t v _ s e c − b e g i n −>t v _ s e c − 1 ;
return (1) ;
}
else {
i n t e r v a l −>t v _ s e c = now−>t v _ s e c − b e g i n −>t v _ s e c ;
31
PŘÍLOHA C. ZDROJOVÝ KÓD - SEKUNDOVÝ GENERÁTOR S TSC ČASOVAČEM
return (0) ;
}
}
v o i d comedi ( v o i d ∗ a r g )
{
i n t count ;
comedi_t ∗ i t ;
i n t subdev = 2 ;
i n t chan = 0 ;
const s t r u c t t i m e s p e c i n t e r v a l _ u p = { 0 , 1 0 0 0 0 0 0 0 0 } ;
/∗ Open Comedi d e v i c e ∗/
i t = comedi_open ( " / dev / comedi0 " ) ;
/∗ C o n f i g u r e l i n e s t o DO ∗/
c o m e d i _ d i o _ c o n f i g ( i t , subdev , chan ,COMEDI_OUTPUT) ;
/∗
∗ Set t h i s task p e r i o d i c
∗ Arguments : &t a s k (NULL= s e l f ) ,
∗
s t a r t time ,
∗
period ( here : 1 s )
∗/
r t _ t a s k _ s e t _ p e r i o d i c (NULL , TM_NOW, 1000000000 −CORRECT_SYST) ;
f o r ( c o u n t =0; count <CNT ; c o u n t++) {
/∗ W r i t e 1−0 t o DAQ c a r d ∗/
c o m e d i _ d i o _ w r i t e ( i t , subdev , chan , 1 ) ;
n a n o s l e e p (& i n t e r v a l _ u p , NULL) ;
c o m e d i _ d i o _ w r i t e ( i t , subdev , chan , 0 ) ;
/∗ Wait f o r n e x t p e r i o d ∗/
r t _ t a s k _ w a i t _ p e r i o d (NULL) ;
}
comedi_close ( i t ) ;
return ;
}
void c a t c h _ s i g n a l ( i n t s i g )
{
/∗ S i g t e r m caught , s o e x i t ∗/
e x i t (0) ;
}
i n t main ( i n t a r g c , char ∗ a r g v [ ] )
{
struct timespec tts , tts_after , tts_delta ;
s i g n a l (SIGTERM , c a t c h _ s i g n a l ) ;
/∗ Lock p a g e s i n r e a l memmory ∗/
m l o c k a l l (MCL_CURRENT|MCL_FUTURE) ;
32
PŘÍLOHA C. ZDROJOVÝ KÓD - SEKUNDOVÝ GENERÁTOR S TSC ČASOVAČEM
/∗
∗ C r e a t e new t a s k ( t h r e a d )
∗ Arguments : &t a s k ,
∗
name ,
∗
s t a c k s i z e (0= d e f a u l t ) ,
∗
priority ,
∗
mode (FPU , s t a r t s u s p e n d e d , . . . )
∗/
r t _ t a s k _ c r e a t e (& comedi_task , " t r i v i a l " , 0 , 9 9 , T_JOINABLE) ;
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t t s ) ;
/∗
∗ Start task
∗ Arguments : &t a s k ,
∗
task function ,
∗
∗/
f u n c t i o n argument
r t _ t a s k _ s t a r t (& comedi_task , &comedi , NULL) ;
/∗ Wait f o r t a s k t e r m i n a t i o n ∗/
r t _ t a s k _ j o i n (& comedi_task ) ;
/∗ D e l e t e t a s k ∗/
r t _ t a s k _ d e l e t e (& comedi_task ) ;
c l o c k _ g e t t i m e (CLOCK_REALTIME,& t t s _ a f t e r ) ;
d e l t a _ t (& t t s _ d e l t a , &t t s , &t t s _ a f t e r ) ;
p r i n t f ( " T o t a l ␣ w a i t : ␣%l u ␣ s e c ␣%l u ␣ n s e c \n" , t t s _ d e l t a . tv_sec , t t s _ d e l t a .
tv_nsec ) ;
e x i t (0) ;
}
33
PŘÍLOHA D
Zdrojový kód - latence přerušení
#i n c l u d e < s t d i o . h>
#i n c l u d e <s y s / t i m e . h>
#i n c l u d e <t i m e . h>
#i n c l u d e <p t h r e a d . h>
#i n c l u d e <s c h e d . h>
#i n c l u d e <c o m e d i l i b . h>
#i n c l u d e <u n i s t d . h>
#i n c l u d e < f c n t l . h>
#i n c l u d e <s y s / s i g n a l . h>
#i n c l u d e <s y s / t y p e s . h>
#i n c l u d e <n a t i v e / t a s k . h>
#i n c l u d e <n a t i v e / t i m e r . h>
#i n c l u d e <n a t i v e / i n t r . h>
#i n c l u d e <s y s /mman . h>
#d e f i n e _POSIX_SOURCE 1
#d e f i n e FALSE 0
#d e f i n e TRUE 1
#d e f i n e IRQ 22
v o i d comedi_setup_IRQ ( ) ;
void pollTaskRun ( ) ;
void c a t c h _ s i g n a l ( i n t s i g ) ;
void initTaskRun ( ) ;
RT_TASK p o l l T a s k ;
RT_TASK i n i t T a s k ;
RT_INTR d i o I n t r ;
v o l a t i l e i n t STOP=FALSE ;
const s t r u c t t i m e s p e c i n t e r v a l _ u p = { 0 , 1 0 0 0 0 0 0 0 0 } ;
comedi_t ∗ i t ;
34
PŘÍLOHA D. ZDROJOVÝ KÓD - LATENCE PŘERUŠENÍ
i n t chan ;
i n t subdev ;
comedi_cmd cmd ;
i n t main ( )
{
int err ;
/∗ Lock p a g e s i n r e a l memmory ∗/
m l o c k a l l (MCL_CURRENT|MCL_FUTURE) ;
/∗ C r e a t e new IRQ h a n d l e r . A f t e r p r o c e s s i n g i n my h a n d l e r , p r o p a g a t e i t
t o L i n u x d r i v e r h a n d l e r i n K e r n e l s p a c e . ∗/
e r r = r t _ i n t r _ c r e a t e (& d i o I n t r , "Port_C_3" , IRQ , I_PROPAGATE) ;
p r i n t f ( " I n t r ␣ c r e a t e : ␣%i \n" , e r r ) ;
e r r = r t _ i n t r _ e n a b l e (& d i o I n t r ) ;
p r i n t f ( " I n t r ␣ e n a b l e : ␣%i \n" , e r r ) ;
/∗ C r e a t e new t a s k ( t h r e a d ) − IRQ h a n d l e r ∗/
r t _ t a s k _ c r e a t e (& p o l l T a s k , " p o l l T a s k " , 0 , 6 0 , T_JOINABLE) ;
/∗ C r e a t e new t a s k ( t h r e a d ) − i n i t t a s k ∗/
r t _ t a s k _ c r e a t e (& i n i t T a s k , " i n i t T a s k " , 0 , 4 0 , T_JOINABLE) ;
s i g n a l (SIGTERM , c a t c h _ s i g n a l ) ;
/∗ S t a r t t a s k − IRQ h a n d l e r ∗/
r t _ t a s k _ s t a r t (& p o l l T a s k , &p o l l T a s k R u n , NULL) ;
/∗ S t a r t t a s k − i n i t t a s k ∗/
r t _ t a s k _ s t a r t (& i n i t T a s k , &i n i t T a s k R u n , NULL) ;
/∗ Wait f o r t a s k t e r m i n a t i o n ∗/
r t _ t a s k _ j o i n (& i n i t T a s k ) ;
}
void c a t c h _ s i g n a l ( i n t s i g )
{
c o m e d i _ c a n c e l ( i t , s u b d e v +1) ;
comedi_close ( i t ) ;
e x i t (0) ;
}
void initTaskRun ( )
{
int ret = 0;
subdev = 0 ;
chan = 0 ;
/∗ Open Comedi d e v i c e ∗/
i t = comedi_open ( " / dev / comedi0 " ) ;
/∗ C o n f i g u r e l i n e s t o DO ∗/
c o m e d i _ d i o _ c o n f i g ( i t , subdev , chan ,COMEDI_OUTPUT) ;
/∗ C o n f i g u r e l i n e s t o DI ∗/
c o m e d i _ d i o _ c o n f i g ( i t , s u b d e v +1, chan , COMEDI_INPUT) ;
35
PŘÍLOHA D. ZDROJOVÝ KÓD - LATENCE PŘERUŠENÍ
/∗ E n a b l e IRQ ∗/
comedi_setup_IRQ(& i t ) ;
w h i l e (STOP==FALSE ) {
p r i n t f ( " . " ) ; f f l u s h (NULL) ; u s l e e p ( 1 0 0 0 0 0 ) ;
}
comedi_close ( i t ) ;
}
/∗ T h i s method e n a b l e s IRQ . I t i s v i r t u a l Comedi command , d e f i n e d i n d r i v e r
. ∗/
v o i d comedi_setup_IRQ ( )
{
unsigned i n t c h a n l i s t [ 0 ] ;
int err ;
cmd . s u b d e v
= 1;
cmd . f l a g s
cmd . s t a r t _ s r c
= 0;
= TRIG_NOW;
cmd . s t a r t _ a r g
= 0;
cmd . s c a n _ b e g i n _ s r c = TRIG_EXT ;
cmd . scan_begin_arg = 0 ;
cmd . c o n v e r t _ s r c
= TRIG_FOLLOW ;
cmd . c o n v e r t _ a r g
= 0;
cmd . scan_end_src
= TRIG_COUNT ;
cmd . scan_end_arg
= 1;
cmd . s t o p _ s r c
= TRIG_NONE ;
cmd . s t o p _ a r g
= 0;
cmd . c h a n l i s t = c h a n l i s t ;
cmd . c h a n l i s t _ l e n = 1 ;
c h a n l i s t [ 0 ] = CR_PACK( 0 , 0 , 0 ) ;
e r r = comedi_command ( i t , &cmd ) ;
p r i n t f ( "Command␣ s t a t u s : ␣%i \n" , e r r ) ;
}
/∗ IRQ h a n d l e r ∗/
void pollTaskRun ( i n t s t a t u s ) {
int err ;
w h i l e (TRUE) {
p r i n t f ( " W a i t i n g ␣ f o r ␣INT\n" ) ;
/∗ Wait f o r i n t e r r u p t ∗/
e r r = r t _ i n t r _ w a i t (& d i o I n t r , TM_INFINITE ) ;
i f ( e r r <0) break ;
printf (" I ") ;
c o m e d i _ d i o _ w r i t e ( i t , subdev , chan , 1 ) ;
n a n o s l e e p (& i n t e r v a l _ u p , NULL) ;
c o m e d i _ d i o _ w r i t e ( i t , subdev , chan , 0 ) ;
}
}
36
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement