Universal Serial Bus je standard organizace USB Implementers Forum. Definuje průmyslový standard a popisuje vlastnosti sběrnice, protokol, typy přenosů, hospodaření s prostředky a potřebnou programovou podporu. Existují tři verze specifikací: USB 1.0, USB 1.1 a USB 2.0. V této práci se budeme zabývat výhradně verzí USB 1.1, jejíž specifikace je k dispozici na http://smrz.chrudim.cz/usb/usbspec.zip. Mezi materiály, které vysvětlují USB 1.1 patří také dokument USB in a Nutshell, který více didakticky popisuje principy komunikace po USB.
Vlastnosti USB 1.1
- sériová poloduplexní sběrnice s přenosovou rychlostí 1,5 Mbaud (low speed) a 12Mbaud (full speed)
- elektricky je signál přenášen po diferenciálním vedení a je galvanicky spojen s napájecím (Vbus) a zemnícím (GND) vodičem
- zařízení mohou být napájena přímo přes vodiče Vbus a GND, ale musejí mít omezenou spotřebu (<500mA) a schopnost úsporného
režimu.
- linková vrstva používá NRZI kódování a bit stuffing, pakety jsou chráněny CRC16 nebo CRC5
- de facto se nejedná o sběrnici, ale o propojení PTP (point-to-point) mezi hostitelem (host) nebo hubem a zařízením (device)
- síťová topologie je stromového typu, v každém uzlu je umístěn přepínač (switch, ale nazýván USB hub)
- z hlediska transportní vrstvy (roura mezi odesílatelem a příjemcem) je celé přenosové médium transparentní
- zařízení mohou obsahovat až 7 rour (pipe) a více konfigurací, zařízení se sama enumerují a splňují specifikaci Plug and Play
- maximální počet USB zařízení připojených k jednomu kořenovému přepínači je 127
Elektrická vrstva
Diferenciální vedení o impedanci 90 ohm slouží pro obousměrnou komunikaci. Přístup k médiu je deterministický a nemůže na něm dojít ke kolizi, protože komunikace je vždy iniciována hostitelem. Oba diferenciální vodiče jsou na obou stranách vedení buzeny CMOS hradly (obr. 1) se sériově zapojenými rezistory (28 až 44 ohm), sloužícími jako impedanční přizpůsobení. Hostitel i zařízení mají možnost odpojení budicího hradla od linky pro případ, kdy přecházejí z vysílacího do přijímacího režimu.
 | obr. 1 - Budiče linky |
Úrovně jsou na diferenciálních vodičích pro klidový stav (hostitel i zařízení jsou odpojené) na straně hostitele určeny dvěma pull-down rezistory a na straně zařízení jedním pull-up rezistorem. Připojením pull-up rezistoru k D+ nebo D- vodiči se zařízení identifikuje buď jako full-speed nebo jako low-speed (tabulka 1). S tím se mění také počáteční podmínka pro výpočet NRZI (tabulka 2).
| pull up D+ | pull up D- |
low-speed | - | 1,5k |
full-speed | 1,5k | - |
Tabulka 1.
| D+ | D- | počáteční stav D+ |
low-speed | 0V | 3,3V | 0 |
full-speed | 3,3V | 0V | 1 |
Tabulka 2.
Přítomnost společného potenciálu na obou diferenciálních vodičích zároveň po dobu alespoň dvou bitů znamená konec paketu a je označována jako SE0.
Mechanická část
Diferenciální vedení se realizují kroucenou dvojlinkou se zemněným pláštěm (obr. 2).
 | obr. 2 - kabel |
Konektory se na straně hostitele a na straně zařízení liší, takže nelze spojit dva hostitele nebo dvě zařízení k sobě. Typ A (samice) je na straně hositele a tyb B (samice) je na straně zařízení. U miniaturních zařízení se nemusí používat propojovací A-B kabel, ale zařízení je přímo opatřeno konektorem typu A (samec).
Linková vrstva
Přenášené elementární datové struktury se nazývají pakety. MTU (maximal transfer unit) neboli maximální počet bytů v paketu (bez hlaviček a CRC) je pro 0. endpoint 8,16,32 nebo 64 bytů, pro ostatní endpointy je volitelný v pomocí Endpoint descriptoru. Čistá data je tedy třeba rozdělit více paketů, opatřit je hlavičkami a cyklickou redundantní kontrolou. Dále je nutné provést bit stuffing (za každou 6. jedničku musí být vložena nula), čímž může vzniknout neceločíselný počet bytů k přenesení. Poté se data zakódují kanálovým kódováním NRZI (Non-Return to Zero Invert), které nulu kóduje jako změnu úrovně a jedničku jako setrvání v úrovni. Pro bit v čase t platí line(t)=line(t-1) xor (!bit(t)). Realizace může proběhnout pomocí D klopného obvodu (DFF) na obr. 3.
 | obr. 3 - NRZI kodér |
Procesy linkové vrstvy (bit stuffing a NRZI) jsou zobrazené na obr. 4.
 | obr. 4 - příklad bit stuffingu a NRZI kódování |
Všechny operace jsou bitově orientované a lze je tedy přepsat do kaskády proudových filtrů (obr. 5), které mají za vstup bitový tok dat (LSb i LSB se posílají nejdříve). Skutečnost, že data se zpracovávají bit po bitu nahrává realizaci USB řadiče na hradlovém poli nebo pomocí bitového stavového automatu. Rozhodně však nelze data zpracovávat instrukcemi procesoru po celých bytech. Budeme-li například vstupní proud zpracovávat procesorem, bude se jednat o operace nad každým jedním bitem a teprve až po provedení všech dekódování a CRC výpočtů bude možné data deserializovat a skladovat po bytech.
 | obr. 5 - proudové filtry výstupní části řadiče |
V případě, že odesílaná data jsou dopředu známa, je možné vyloučit z kaskády filtrů výpočet CRC16 a jeho předpočítanou hodnotu zařadit za data paketu. Pro náš úkol je zajímavá i možnost předpočítat si data úplně celá (pro oba diferenciální vodiče) a pak je pouze po dvojicích serializovat a zapisovat na linku. Takový přístup sice není elegantní, přesto se s ním v naší práci později setkáme.
Cyklická redundantní kontrola
Obsah paketů je doplněn 16-bitovým číslem, takzvanou cyklickou redundantní kontrolou (CRC). Odesílatel počítá CRC pro odeslanou zprávu a hodnotu CRC přikládá na konec paketu. Příjemce má dvě možnosti, jak zkontrolovat správnost paketu:
- příjemce přečte paket, který se skládá z dat (payload) a z dvoubytového CRC. Příjemce pro data spočítá CRC a porovná ji s přijatou CRC. Pokud se obě čísla shodují, paket dorazil správně. Příjemce buď potřebuje vědět dopředu, jak dlouhá jsou data (to je na USB sběrnici nemožné), a nebo bude počítat CRC s dvoubytovým zpožděním a při konci paketu bude jisté, že výpočet proběhl pouze nad daty a ne nad přijímaným CRC.
- příjemce přečte paket a CRC spočítá z celého jeho obsahu. Cyklická redundantní kontrola obecně má tu vlastnost, že přijímáme-li stejnou posloupnost bitů, jaká byla obsahem CRC registru, pak dostaneme vždy stejný výsledek (tzv. zbytek, residual). CRC spočítané z celého paketu tedy musí vyjít pro každý správně přijatý paket vždy stejně. Pro náš případ je zbytek 0x800D, zrcadlově 0x4FFE.
USB specifikace definuje pořadí odesílaných bitů jako LSb first, tedy nejméně významný bit je odesílán nejdříve. Pokud máme data uložena v registru, musíme provést pravou rotaci (rotate through carry right). Tím se vždy nejméně významný bit odebere a zpracuje. Po provedení sedmi rotací zbyde v registru nejvíce významný bit - a ke zpracování se tedy dostane jako poslední. Je výhodné, aby reprezentace všech čísel, která se chystáme po USB sběrnici odeslat, měla shodné pořadí bitů. Zvykem je, že binární čísla (a obecně čísla) se zapisují zleva doprava od MSb po LSb.
Výpočet CRC je možno provést dvěma způsoby. První je uveden v USB specifikaci a používá generující polynom x16+x15+x2+1 s binárním vyjádřením 0x8005 a zbytkem 0x800D. Při generování se ale nepochopitelně používá levá rotace (přestože všechna data jsou rotována doprava) a tím vzniká potřeba před odesláním CRC zrcadlově převrátit. Druhý způsob výpočtu používá stejný generující polynom, ale protože používá správně pravou rotaci, musí být jeho binární vyjádření jiné, tedy 0xA001 a zbytek 0x4FFE. Rozdíl mezi metodami je kromě pořadí bitů také v počátečních hodnotách registrů.
1. metoda:
- počáteční hodnota CRC je 0xFFFF
- CRC=(CRC shl 1), přičemž vysunutý bit (MSb) je předmětem dalšího výpočtu
- pokud je MSb různý od vstupního bitu (xor je 1), pak CRC=CRC xor 0x8005
- pro všechny vstupní bity opakovat od bodu 2, jinak skončit
- budeme-li CRC odesílat, musíme jej zrcadlově převrátit (15. bit na 0., 14. bit na 1., atd.) a znegovat (xor 0xFFFF)
- byl-li CRC počítán pro kontrolu příchozího paketu, musí se pro bezchybný paket rovnat zbytku 0x800D
Tento postup je ale nepříjemný kvůli bodu 5), kdy je potřeba zrcadlově převracet a negovat CRC.
2. metoda (rozepsaná ve funkci crcpipe()) je ekvivalentní, zrcadlově převrácená od začátku a méně náročná na výpočet.
2. metoda:
- počáteční hodnota CRC je 0x0000
- CRC=(CRC shr 1) or 0x8000, přičemž vysunutý bit (LSb) je předmětem dalšího výpočtu
- pokud je LSb stejný jako vstupní bit (xor je 0), pak CRC=CRC xor 0xA001
- pro všechny vstupní bity opakovat od bodu 2, jinak skončit
- budeme-li CRC odesílat, můžeme jej použít bez jakékoliv úpravy
- byl-li CRC počítán pro kontrolu příchozího paketu, musí se pro bezchybný paket rovnat zbytku 0x4FFE
Výpočet CRC je možné hardwarově realizovat shift registrem, jehož některé prvky mají možnost znegovat svůj obsah (funkce XOR - negace na masku). U takového CRC shift registru nemá smysl hovořit o levé nebo pravé rotaci, protože pořadí dat vstupu a výstupu je zaručeně stejné a nevzniká zde zmatek ohledně pořadí bitů. Shift regist pro počáteční hodnotu 0x0000 je uveden na obrázku 5.
 | Obr. 5 - výpočet CRC16 |
Programově lze výpočet CRC nahradit voláním procedury crcpipe() pro každý přijatý bit:
var crc:word; {pocatecni hodnota je 0}
procedure crcpipe(bit:byte);
var lsb:byte;
begin
lsb:=crc and 1;
crc:=(crc shr 1) or $8000;
if lsb=bit then begin
crc:=crc xor $A001;
end;
end;
Pro zkušební výpočty CRC slouží program data2usb.pas (Pascal). Ve zdrojovém kódu programu je vidět praktický výpočet CRC. Vývojové prostředí Turbo Pascalu se skládá ze třech souborů, které je potřeba stáhnout do jednoho adresáře a spustit turbo.exe. Soubory jsou k dispozici ke stažení jako turbo.exe, turbo.tpl, turbo.tph.
Dalším programem je usbprep.exe (příprava USB dat, zdroj usbprep.pas), který přijímá jako parametr zprávu, kterou chceme zakódovat. Zpráva může být libovolně dlouhá. Program zprávu rozdělí na 8 bytové pakety, vypočítá CRC16 a opatří je bit stuffingem. Zpráva se předává programu jako parametr na příkazové řádce, mezerou se oddělují jednotlivé byty nebo wordy. Wordy jsou programem automaticky rozdělelny na byty a položeny LSB napřed. Pokud je počet bitů v paketu neceločíselným násobkem 8 (po doplnění bit stuffingem je to běžné) a přesto potřebujeme odesílací rutině předat data po bytech, musíme bitový tok zarovnat doprava a v prvním bytu doplnit chybějící pozice jedničkami. Při odesílání se úvodní jedničky nijak neprojeví (vyplývá to z vlastností NRZI kanálového kódování), pouze se prodlouží doba před synchronizační sekvencí 0x80. Pokud by se chybějící pozice doplňovaly nulami, prodloužila by se synchronizační sekvence, což není v souladu se specifikací pro low-speed USB (ve specifikaci je délka synchronizačního bytu stanovena na 0x80). U odesílacích rutin, které přijímají data včetně bit stuffingu (9MHz a 10,5 MHz) je vhodnější úvodní jedničky ignorovat, aby se zbytečně neprodlužovala doba mezi posledním přijatým a následujícím odeslaným paketem. Většínou ale rutiny (12MHz, 15MHz, 18MHz a 19,5MHz) očekávají data bez bit stuffingu a celá operace přípravy dat se jednodušuje pouze na dělení na pakety a výpočet CRC.
Ukázka použití programu usbprep.exe (zprávou je zde Device Descriptor):
c:\>usbprep 12 01 0100 00 00 00 08 065d 1031 0004 00 00 00 01
USB prep - rozdeli zpravu na pakety a opatri je CRC16 a bit stuffingem
Prevod zpravy: 12 01 0100 00 00 00 08 065d 1031 0004 00 00 00 01
Info: bitove toky jsou LSb (least significant bit) first, hex cisla LSB (least s
ignificant byte) first.
------
data1: 0100100010000000000000001000000000000000000000000000000000010000
crc: 0xE713
00000001110100100100100010000000000000001000000000000000000000000000000000010000
1100100011100111
delka paketu: 96 b
paket: 80 4B 12 01 00 01 00 00 00 08 13 E7
delka paketu s bit stuffingem: 96 b
delka paketu s bit stuffingem mod 8: 0
paket s bit stuffingem: 80 4B 12 01 00 01 00 00 00 08 13 E7
prvni byte, doplneno jednickami zpredu (LSB): 00000001
------
data0: 1011101001100000100011000000100000100000000000000000000000000000
crc: 0x83D9
00000001110000111011101001100000100011000000100000100000000000000000000000000000
1001101111000001
delka paketu: 96 b
paket: 80 C3 5D 06 31 10 04 00 00 00 D9 83
delka paketu s bit stuffingem: 96 b
delka paketu s bit stuffingem mod 8: 0
paket s bit stuffingem: 80 C3 5D 06 31 10 04 00 00 00 D9 83
prvni byte, doplneno jednickami zpredu (LSB): 00000001
------
data1: 0000000010000000
crc: 0x8F3F
000000011101001000000000100000001111110011110001
delka paketu: 48 b
paket: 80 4B 00 01 3F 8F
delka paketu s bit stuffingem: 49 b
delka paketu s bit stuffingem mod 8: 1
paket s bit stuffingem: 7F C0 25 80 80 1F 8F
prvni byte, doplneno jednickami zpredu (LSB): 11111110
Transportní vrstva
Na této úrovni řešíme vyměňování paketů mezi hostitelem a zařízením za účelem:
- přenést užitečná data (payload) - rourový přístup
- provést enumeraci (počáteční vyjednávání) speciální rourou (endpoint 0)
- řídit stavy zařízení
- korigovat chyby při přenosu, zařizovat handshaking
Existuje 16 typů paketů rozdělěných do čtyřech skupin:
- potvrzovací (handshake) - ACK, NAK, STALL, NYET (No Response Yet)
- pověřovací (token) - IN, OUT, SETUP, SOF
- datové (data) - DATA0, DATA1, DATA2, MDATA
- speciální (special) - PREamble, ERR, Split, Ping
Identifikátor paketu (PID) je prvním bytem v paketu (po synchronizační sekvenci SYNC 0x80) a skládá se z čísla paketu (v dolní polovině bytu) a invertovaného čísla paketu (v horní polovině bytu):
|